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

[Impeller] construct text frames on UI thread. #45418

Merged
merged 14 commits into from
Sep 6, 2023

Conversation

jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Sep 3, 2023

Conversion of SkTextBlobs to impeller::TextFrame objects is one of the most expensive operations in display list dispatching. While the rest of the engine and framework makes a reasonable attempt to cache the SkTextBlobs generated during paragraph construction, the design of the dl dispatcher means that these the Impeller backend will always reconstruct all text frames on each frame - even if the display list/picture that contained those text frames was unchanged.

Removing this overhead is one of the goals of #45386 , however this patch is also fairly risky and will be difficult to land. As a more incremental solution, we can instead construct the impeller::TextFrame objects when performing paragraph painting and record them in the display list. This both moves the text frame construction to the UI thread and allows the framework/engine to cache unchanged text frames.

This also does not conflict with the dl_aiks_canvas patch directly, and is fine to land before or after it does. (though I'd argue we should land this first).

To compare the current performance levels, I ran the complex_layout_scroll perf test, since this is fairly text filled. On a Pixel 6 pro. Across several runs this is a fairly consistent ~1ms raster time improvement.

Skia

  "average_frame_build_time_millis": 1.497333333333333,
  "90th_percentile_frame_build_time_millis": 2.038,
  "99th_percentile_frame_build_time_millis": 17.686,
  "worst_frame_build_time_millis": 23.095,
  "missed_frame_build_budget_count": 3,
  "average_frame_rasterizer_time_millis": 5.5078589743589745,
  "stddev_frame_rasterizer_time_millis": 2.226343414420338,
  "90th_percentile_frame_rasterizer_time_millis": 7.481,
  "99th_percentile_frame_rasterizer_time_millis": 19.11,
  "worst_frame_rasterizer_time_millis": 79.799,
  "missed_frame_rasterizer_budget_count": 7,
  "frame_count": 234,
  "frame_rasterizer_count": 234,
  "new_gen_gc_count": 10,
  "old_gen_gc_count": 2,

Impeller (ToT)

  "average_frame_build_time_millis": 1.431575000000001,
 "90th_percentile_frame_build_time_millis": 2.196,
 "99th_percentile_frame_build_time_millis": 14.486,
 "worst_frame_build_time_millis": 23.728,
 "missed_frame_build_budget_count": 2,
 "average_frame_rasterizer_time_millis": 6.536087499999999,
 "stddev_frame_rasterizer_time_millis": 1.9902712500000004,
 "90th_percentile_frame_rasterizer_time_millis": 9.705,
 "99th_percentile_frame_rasterizer_time_millis": 14.727,
 "worst_frame_rasterizer_time_millis": 17.838,
 "missed_frame_rasterizer_budget_count": 1,
 "frame_count": 240,
 "frame_rasterizer_count": 240,
 "new_gen_gc_count": 10,
 "old_gen_gc_count": 2,

Impeller (Patched)

  "average_frame_build_time_millis": 1.4500167364016743,
"90th_percentile_frame_build_time_millis": 2.478,
"99th_percentile_frame_build_time_millis": 14.883,
"worst_frame_build_time_millis": 18.782,
"missed_frame_build_budget_count": 1,
"average_frame_rasterizer_time_millis": 5.023033333333336,
"stddev_frame_rasterizer_time_millis": 1.6445388888888894,
"90th_percentile_frame_rasterizer_time_millis": 7.814,
"99th_percentile_frame_rasterizer_time_millis": 13.497,
"worst_frame_rasterizer_time_millis": 15.008,
"missed_frame_rasterizer_budget_count": 0,
"frame_count": 239,
"frame_rasterizer_count": 240,
"new_gen_gc_count": 8,
"old_gen_gc_count": 0,

@jonahwilliams
Copy link
Member Author

A second component to this change is that the public API for getting bitmap/emoji font requires a Skia paragraph object, and so moving the text frame construction allows us to (later) fix that too

@jonahwilliams jonahwilliams marked this pull request as ready for review September 5, 2023 16:14
Copy link
Contributor

@matanlurey matanlurey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a few comments.

@@ -1302,6 +1302,48 @@ void DisplayListBuilder::DrawTextBlob(const sk_sp<SkTextBlob>& blob,
SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags);
drawTextBlob(blob, x, y);
}

void DisplayListBuilder::drawTextFrame(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that drawTextBlob will go unused in the Impeller backend?

If so, would you want to (ifdef'd?) make sure it's not used, i.e. UNIMPLEMENTED, similar to the dispatcher?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we use DisplayListBuilder on Skia ever. FYI @flar

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do. That was its original purpose and is iteratively becoming its only purpose.

return;
}
impeller::Rect bounds = text_frame->GetBounds();
SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised we don't have a conversion for this? If we don't, feel free to resolve.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do, its just all over the place.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we would want to just do a separate class at some point versus having all these switches?

(No changes requested in this PR, more of an open question/possible TODO)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that ultimately depends on how many (if any) additional checks we need to add versus how quickly we move through preview for the remaining platforms. I wouldn't think so at this time, but if we have to do much more than splitting into two might make sense. however is still 80% the same code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At one point DL was supposed to be an agnostic storage format that could be used for any backend, allowing common operations at the "picture" level. There was potential for more value added operations that we never pursued. Meanwhile performance considerations related to conversions have been driving it into custom backend-specific cases.

@@ -180,6 +202,14 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter {
return path;
}

bool ShouldRenderAsPath(const DlPaint& paint) const {
// Text with non-trivial color sources or stroke paint mode should be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding context.

Is this for performance or correctness or both? Could you note that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added correctness information.

@@ -79,6 +82,20 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter {
}
size_t paint_id = std::get<PaintID>(paint);
FML_DCHECK(paint_id < dl_paints_.size());

if (ShouldRenderAsPath(dl_paints_[paint_id])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider putting this inside the impeller_enabled_ block below, and FML_DCHECK'ing instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you also want to guard this with IMPELLER_SUPPORTS_RENDERING?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@chinmaygarde chinmaygarde changed the title [Impeller] construct text frames on UI thread. [Impeller] Construct text frames on UI thread. Sep 5, 2023
@chinmaygarde chinmaygarde changed the title [Impeller] Construct text frames on UI thread. [Impeller] construct text frames on UI thread. Sep 5, 2023
@flar
Copy link
Contributor

flar commented Sep 5, 2023

How expensive are the TextBlobs themselves? If we kept both around then we'd retain the ability to render a DL under both Skia and Impeller, but at the cost of storing both. That isn't our long-term goal, but it keeps some ability to remain backend-agnostic while we get there.

@jonahwilliams
Copy link
Member Author

The text blobs themselves won't be expensive to store, once we have them we have them. But DLs isn't backend agnostic, as both images/textures and shaders are backend specific now.

@jonahwilliams jonahwilliams added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 6, 2023
@auto-submit auto-submit bot merged commit 8bacc3b into flutter:main Sep 6, 2023
28 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 6, 2023
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Sep 6, 2023
…134089)

flutter/engine@5903490...8bacc3b

2023-09-06 jonahwilliams@google.com [Impeller] construct text frames on UI thread. (flutter/engine#45418)
2023-09-06 jiahaog@users.noreply.github.com Add import for `<unordered_map>` to fix the g3 build (flutter/engine#45471)
2023-09-06 skia-flutter-autoroll@skia.org Roll Skia from af473004622f to 0a253625a76a (2 revisions) (flutter/engine#45470)
2023-09-05 skia-flutter-autoroll@skia.org Roll Skia from 1019c10a2d38 to af473004622f (2 revisions) (flutter/engine#45469)
2023-09-05 zanderso@users.noreply.github.com Adds a comment on clang_arm64_apilevel26 toolchain usage (flutter/engine#45467)

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 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://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@jonahwilliams jonahwilliams deleted the text_frame_skia_2 branch September 6, 2023 18:33
@jonahwilliams jonahwilliams restored the text_frame_skia_2 branch September 16, 2023 00:47
jonahwilliams added a commit that referenced this pull request Sep 16, 2023
auto-submit bot pushed a commit that referenced this pull request Sep 16, 2023
Reverts #45418

Some google3 tests are hitting the CHECK I added in the DlSkCanvasDispatcher::drawTextFrame, which indicates that the SkParagraph code likely thinks impeller is enabled, whereas other code might be running with Skia.

Perhaps this could happen if its software rendering? It should be a fatal error on startup so we can track this down.
XilaiZhang pushed a commit to XilaiZhang/engine that referenced this pull request Sep 16, 2023
Reverts flutter#45418

Some google3 tests are hitting the CHECK I added in the DlSkCanvasDispatcher::drawTextFrame, which indicates that the SkParagraph code likely thinks impeller is enabled, whereas other code might be running with Skia.

Perhaps this could happen if its software rendering? It should be a fatal error on startup so we can track this down.
XilaiZhang pushed a commit to XilaiZhang/engine that referenced this pull request Sep 18, 2023
Reverts flutter#45418

Some google3 tests are hitting the CHECK I added in the DlSkCanvasDispatcher::drawTextFrame, which indicates that the SkParagraph code likely thinks impeller is enabled, whereas other code might be running with Skia.

Perhaps this could happen if its software rendering? It should be a fatal error on startup so we can track this down.
XilaiZhang added a commit that referenced this pull request Sep 18, 2023
…ad." (#45910) (#45958)

Reverts #45418

Some google3 tests are hitting the CHECK I added in the
DlSkCanvasDispatcher::drawTextFrame, which indicates that the
SkParagraph code likely thinks impeller is enabled, whereas other code
might be running with Skia.

Perhaps this could happen if its software rendering? It should be a
fatal error on startup so we can track this down.

cherry pick for flutter roll

Co-authored-by: Jonah Williams <jonahwilliams@google.com>
harryterkelsen pushed a commit that referenced this pull request Oct 23, 2023
Reverts #45418

Some google3 tests are hitting the CHECK I added in the DlSkCanvasDispatcher::drawTextFrame, which indicates that the SkParagraph code likely thinks impeller is enabled, whereas other code might be running with Skia.

Perhaps this could happen if its software rendering? It should be a fatal error on startup so we can track this down.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App e: impeller
Projects
No open projects
Archived in project
4 participants