-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] hash even less stuff per frame. #55092
Conversation
@@ -16,6 +16,8 @@ namespace impeller { | |||
/// This object is typically the entrypoint in the Impeller type | |||
/// rendering subsystem. | |||
/// | |||
/// A text frame should not be reused in multiple places within a single frame, |
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.
If this behavior is a problem, we could instead use a different object as a cache, but I think that would be substantially more refactoring for little to no gain, as this condition is always true for us.
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.
Sounds good and thanks for the clarification.
std::vector<FontGlyphPair>& new_glyphs, | ||
std::vector<Rect>& glyph_sizes) { | ||
for (const auto& frame : text_frames) { | ||
// TODO(jonahwilliams): unless we destroy the atlas (which we know about), |
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.
Leaving this as the next optimizations. With a little plumbing when we recreate the atlas we can actually avoid any hashing on frames with no new text.
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.
sgtm
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. |
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 from me; but you asked for Chinmay
impeller/typographer/text_frame.h
Outdated
std::vector<TextRun> runs_; | ||
Rect bounds_; | ||
bool has_color_; | ||
|
||
// per frame data. |
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.
super nit for clarity: "per frame data" -> "per text frame data"
FYI @chinmaygarde this is still waiting on a review from you. |
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 is still waiting on a review from you
Sorry about the delay. All nits.
impeller/typographer/text_frame.h
Outdated
std::vector<FrameBounds> bound_values_; | ||
Scalar scale_; | ||
Point offset_; | ||
std::optional<GlyphProperties> properties_ = std::nullopt; |
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.
= std::nullopt
is redundant.
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.
Done
impeller/typographer/text_frame.h
Outdated
|
||
// per frame data. | ||
std::vector<FrameBounds> bound_values_; | ||
Scalar scale_; |
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.
Zero out the scalar.
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.
Done
@@ -16,6 +16,8 @@ namespace impeller { | |||
/// This object is typically the entrypoint in the Impeller type | |||
/// rendering subsystem. | |||
/// | |||
/// A text frame should not be reused in multiple places within a single frame, |
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.
Sounds good and thanks for the clarification.
impeller/typographer/text_frame.cc
Outdated
} | ||
|
||
FrameBounds TextFrame::GetFrameBounds(size_t index) { | ||
return bound_values_[index]; |
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.
If you use .at(index)
you get bounds checking in debug modes for free.
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.
GetFrameBounds can be const
.
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.
Done
impeller/typographer/glyph_atlas.h
Outdated
Rect glyph_bounds; | ||
/// Whether [atlas_bounds] are still a placeholder and have | ||
/// not yet been computed. | ||
bool placeholder; |
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.
Zero out the scalar.
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.
Name this is_placeholder
instead? While reading this later, I thought placeholder returned a placeholder itself.
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.
Done
impeller/typographer/glyph_atlas.h
Outdated
SubpixelGlyph::Hash, | ||
SubpixelGlyph::Equal> | ||
positions_; | ||
|
||
FontGlyphAtlas(const FontGlyphAtlas&) = delete; | ||
|
||
FontGlyphAtlas& operator=(const FontGlyphAtlas&) = delete; | ||
// FontGlyphAtlas& operator=(const FontGlyphAtlas&) = delete; |
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.
Did you mean to comment this out? Couldn't find where you don't want to copy but do want to make it copy assigned. At least the rule of 5 thing doesn't track.
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 forgot about this one. I wasn't sure if we should try to design around this. Basically with this I can't do:
font_atlas_map_[scaled_font] = FontGlyphAtlas();
return &font_atlas_map_[scaled_font];
and I'm not sure what the alternative is.
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 think the alternative is insert
. You provide a pair and it returns an iterator.
} | ||
} | ||
} | ||
CollectNewGlyphs(last_atlas, text_frames, new_glyphs, new_sizes); |
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.
DCHECK that the sizes of the two out vectors are the same?
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'm slightly partial to returning a pair of vectors instead of out variables because it makes it clear that existing values in the vectors will be not be disregarded. I didn't see a place where you didn't have to clear the vectors before calling CollectNewGlyphs. Just one less thing you don't have to remember to do. Then you can use a structured binding to do something like:
auto [new_glyphs, new_sizes] = CollectNewGlyphs(last_atlas, text_frames);
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.
Done. I think I one point we were incrementally adding to these but that is long since gone
std::vector<FontGlyphPair>& new_glyphs, | ||
std::vector<Rect>& glyph_sizes) { | ||
for (const auto& frame : text_frames) { | ||
// TODO(jonahwilliams): unless we destroy the atlas (which we know about), |
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.
sgtm
Thank you! :) |
reason for revert: framework golden failures. |
This reverts commit d283bc8.
Reverts: #55092 Initiated by: jonahwilliams Reason for reverting: framework golden failures. Original PR Author: jonahwilliams Reviewed By: {chinmaygarde, jtmcdole} This change reverts the following previous change: Follow up to #55060 Currently we have multiple stages of hashing while font rendering, which is relatively expensive for the actualy required workload. First, we hash the contents of all text frames to compute the unique set of glyphs per frame. Then we diff this hash set against the hashmap of glyphs within the atlas. Finally we hash and lookup the final rendered bounds for each glyph. We can simplify this to 2. hash lookups for glyphs not yet in the atlas and 1. hash lookup for glyphs that are in the atlas. This is done by combing the step where we uniquely compute glyphs per frame with the diff against the current atlas. When this lookup is performed, we also store the glyph position (if found) in the text_frame itself - which allows text contents to skip the last hash, as long as the glyph has already been rendered. ### Before  ### After  Using this handy dandy test app: ```dart import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { Widget build(context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Platform View'), ), body: SafeArea(child: Stack(children: [ SizedBox( width: 380, height: 380, child: LinearProgressIndicator(), ), Stack( children: List<Widget>.generate(1000, (index) { // The problem already happens with a small amount of widgets. // Using an excessive amount of widgets is just to make the problem more evident. return Text("Lots of Texts represent a Widget with complex components."); }), ), Align( alignment: Alignment.bottomCenter, child: TextButton( child: Text("Button"), onPressed: () { print("Tap ${DateTime.now()}"); }, ), ), ], ), ), ), ); } } ```
…155843) flutter/engine@7c603de...f21f2b2 2024-09-27 [email protected] [docs] Fix broken links in docs/ (flutter/engine#55350) 2024-09-27 [email protected] Roll Skia from e77818421e91 to 7efc11f2ea9e (6 revisions) (flutter/engine#55489) 2024-09-27 [email protected] Listen for uncaught exceptions during loading of a web test suite in Chrome (flutter/engine#55166) 2024-09-27 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Impeller] hash even less stuff per frame. (#55092)" (flutter/engine#55491) 2024-09-27 [email protected] [web] Update builder json generator to reflect recent changes (flutter/engine#55307) 2024-09-27 [email protected] [Impeller] hash even less stuff per frame. (flutter/engine#55092) 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 [email protected],[email protected] 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
…lutter#155843) flutter/engine@7c603de...f21f2b2 2024-09-27 [email protected] [docs] Fix broken links in docs/ (flutter/engine#55350) 2024-09-27 [email protected] Roll Skia from e77818421e91 to 7efc11f2ea9e (6 revisions) (flutter/engine#55489) 2024-09-27 [email protected] Listen for uncaught exceptions during loading of a web test suite in Chrome (flutter/engine#55166) 2024-09-27 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Impeller] hash even less stuff per frame. (flutter#55092)" (flutter/engine#55491) 2024-09-27 [email protected] [web] Update builder json generator to reflect recent changes (flutter/engine#55307) 2024-09-27 [email protected] [Impeller] hash even less stuff per frame. (flutter/engine#55092) 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 [email protected],[email protected] 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
Reverted due to missing glyph when rolling into the framework. Problem was that we should be always using the unrounded rounded scale to compute subpixel position but we sometimes used the rounded scale. --- Currently we have multiple stages of hashing while font rendering, which is relatively expensive for the actualy required workload. First, we hash the contents of all text frames to compute the unique set of glyphs per frame. Then we diff this hash set against the hashmap of glyphs within the atlas. Finally we hash and lookup the final rendered bounds for each glyph. We can simplify this to 2. hash lookups for glyphs not yet in the atlas and 1. hash lookup for glyphs that are in the atlas. This is done by combing the step where we uniquely compute glyphs per frame with the diff against the current atlas. When this lookup is performed, we also store the glyph position (if found) in the text_frame itself - which allows text contents to skip the last hash, as long as the glyph has already been rendered. See #55092
Reverted due to missing glyph when rolling into the framework. Problem was that we should be always using the unrounded rounded scale to compute subpixel position but we sometimes used the rounded scale. --- Currently we have multiple stages of hashing while font rendering, which is relatively expensive for the actualy required workload. First, we hash the contents of all text frames to compute the unique set of glyphs per frame. Then we diff this hash set against the hashmap of glyphs within the atlas. Finally we hash and lookup the final rendered bounds for each glyph. We can simplify this to 2. hash lookups for glyphs not yet in the atlas and 1. hash lookup for glyphs that are in the atlas. This is done by combing the step where we uniquely compute glyphs per frame with the diff against the current atlas. When this lookup is performed, we also store the glyph position (if found) in the text_frame itself - which allows text contents to skip the last hash, as long as the glyph has already been rendered. See flutter/engine#55092
Follow up to #55060
Currently we have multiple stages of hashing while font rendering, which is relatively expensive for the actualy required workload. First, we hash the contents of all text frames to compute the unique set of glyphs per frame. Then we diff this hash set against the hashmap of glyphs within the atlas. Finally we hash and lookup the final rendered bounds for each glyph.
We can simplify this to 2. hash lookups for glyphs not yet in the atlas and 1. hash lookup for glyphs that are in the atlas. This is done by combing the step where we uniquely compute glyphs per frame with the diff against the current atlas. When this lookup is performed, we also store the glyph position (if found) in the text_frame itself - which allows text contents to skip the last hash, as long as the glyph has already been rendered.
Before
After
Using this handy dandy test app: