Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@ColdPaleLight
Copy link
Member

@ColdPaleLight ColdPaleLight commented Aug 31, 2021

Update

  1. Add new method virtual bool AllowsDrawingWhenGpuDisabled() const to flutter:Surface to distinguish whether Rasterizer::DrawToSurface needs to use sync switch
  2. Rasterizer::DrawToSurface uses sync switch on iOS's OpenGLES and Metal implementation. If Rasterizer::DrawToSurface is called in the background, this frame will be discarded.
  3. Remove the sync switch from the external view embedders by reverting started providing the GPU sync switch to external view embedders #22302
  4. Add some rasterizer unit tests;

Origin

  1. Add new method virtual bool IsAllowDrawingToSurfaceWhenGpuDisabled() const to flutter:Surface to distinguish whether Rasterizer.Draw needs to use gpu_disable_sync_switch
  2. Rasterizer.Draw uses gpu_disable_sync_switch on iOS's OpenGLES and Metal implementation. If Rasterizer.Draw is called in the background, this frame will be discarded.
  3. Make SyncSwitch recursive

Fixes issue:
flutter/flutter#89171

maybe can fix: flutter/flutter#89204 (edit gaaclarke: we have evidence that it's a different issue).

about test:
If we confirm to use this patch, I can add tests

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides].
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the [CLA].
  • All existing and new tests are passing.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@ColdPaleLight ColdPaleLight marked this pull request as ready for review August 31, 2021 15:33
Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

I think you are right that this is the best approach to fix these bugs for now. You make a compelling argument that this could fix the linked issue but we don't 100% know. We do know it does fix some issues.

In the future we should look into implementing a solution that doesn't require a mutex lock every frame.


private:
mutable std::mutex mutex_;
mutable std::recursive_mutex mutex_;
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason this is recursive? I'd rather not make it recursive. I don't think anyone under DoDraw should be executing the sync switch, no?

Copy link
Member Author

@ColdPaleLight ColdPaleLight Aug 31, 2021

Choose a reason for hiding this comment

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

Replaced here with std::recursive_mutex is to let Scenario App Integration Tests pass,This is the test result before the replacement:https://logs.chromium.org/logs/flutter/buildbucket/cr-buildbucket/8837385340239046353/+/u/Scenario_App_Integration_Tests/stdout?format=raw.
The reason is that external view embedders also use SyncSwitch. #22302
So there are two options.

  1. Use std::recursive_mutex instead of std::mutex. This solution has a small workload but may have poor performance
  2. Remove the SyncSwitch used by external view embedders. This solution may have some workload

Please let me know if you prefer the second option, my original plan was to land this PR first, and then see if we can remove the SyncSwitch in the external view embedders.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should do the second thing, remove the sync switch from the external view embedders, this lock supersedes that one.

Rasterizer::Draw
  -> Rasterizer::DoDraw
    -> Rasterizer::DrawToSurface
      -> FlutterPlatformViewsController::SubmitFrame
        -> SyncSwitch::Execute

However look at Rasterizer::DrawLastLayerTree, potentially that is using the sync switch since that calls Rasterizer::DrawToSurface outside of Rasterizer::Draw

Rasterizer::DrawLastLayerTree
  -> Rasterizer::DrawToSurface
    -> FlutterPlatformViewsController::SubmitFrame
      -> SyncSwitch::Execute

That means we should use the sync switch in Rasterizer::DrawToSurface instead of Rasterizer::Draw. Instead of removing or adding a sync switch, we can think of this as just moving the existing one in FlutterPlatformViewsController up one more layer. From the stack traces we've seen this should be a good place to have it still.

Copy link
Member Author

@ColdPaleLight ColdPaleLight Aug 31, 2021

Choose a reason for hiding this comment

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

Currently, if we are in the background, the RasterStatus we return is RasterStatus::kDiscarded.I can be sure that there is no problem, because the following code also returns RasterStatus::kDiscarded in Draw.

 if (discardCallback(*layer_tree.get())) {
   raster_status = RasterStatus::kDiscarded;
 } 

But if we move sync switch to Rasterizer::DrawToSurface, and in the background in iOS we retuns RasterStatus::kDiscarded from Rasterizer::DrawToSurface, how should Rasterizer::DoDraw handle it?
https://github.com/flutter/engine/blob/master/shell/common/rasterizer.cc#L380-L388

Copy link
Member Author

Choose a reason for hiding this comment

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

RasterStatus::kDiscarded is Added by #21179.

Copy link
Member

Choose a reason for hiding this comment

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

I'd return kDiscarded from DrawToSurface and in DoDraw expand the branch that checks return codes to early out like so:

  RasterStatus raster_status =
      DrawToSurface(*frame_timings_recorder, *layer_tree);
  if (raster_status == RasterStatus::kSuccess) {
    last_layer_tree_ = std::move(layer_tree);
  } else if (raster_status == RasterStatus::kResubmit ||
             raster_status == RasterStatus::kSkipAndRetry) {
    resubmitted_layer_tree_ = std::move(layer_tree);
    return raster_status;
  } else if (raster_status = RasterStatus::kDiscarded) {
    return raster_status;
  }

I think that should be good.

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 got it. Another problem is that we need to wrap a method outside DrawToSurface to handle sync switch (such as DrawToSurfaceWrapper), DoDraw and DrawLastLayerTree should call DrawToSurfaceWrapper instead of DrawToSurface . Here I need a good method name because I am not a native speaker.

Copy link
Member

@gaaclarke gaaclarke Aug 31, 2021

Choose a reason for hiding this comment

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

"Wrapper" isn't that good since it doesn't tell you anything interesting about the code's semantics, just how it is structured.

How about his:

RasterStatus DrawToSurface() {
  RasterStatus result;
  ...
  IsGpuDisabledSyncSwitch().execute(Handlers()
    .SetIfTrue([]{result = kDiscarded;})
    .SetIfFalse([]{result = DrawToSurfaceUnSafe()});
  ...
}

We use the "UnSafe" postfix in other code in the engine to denote the context with which it should be used is enforced higher in the call stack.

Is that was you were asking about?

Copy link
Member Author

@ColdPaleLight ColdPaleLight Aug 31, 2021

Choose a reason for hiding this comment

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

Is that was you were asking about?

Yes, I got it

} else {
raster_status =
DoDraw(std::move(frame_timings_recorder), std::move(layer_tree));
if (!layer_tree || !surface_) {
Copy link
Member

Choose a reason for hiding this comment

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

Leave this to DoDraw, this creates an early return that didn't exist previously.

Copy link
Member Author

Choose a reason for hiding this comment

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

The early return here is used to ensure that surface_ is not null pointer, because we need to use it right away

if (surface_->AllowsDrawingWhenGpuDisabled()) {
  raster_status = DoDraw(std::move(frame_timings_recorder),
                         std::move(layer_tree));

Copy link
Member

Choose a reason for hiding this comment

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

Good call, lets change it to:

if (!layer_tree || !surface_) {
  raster_status = RasterStatus::kFailed;
} else if (surface_->AllowsDrawingWhenGpuDisabled()) {
  raster_status = DoDraw(...);
} else {
  ...
}

This removes the new early out. You can remove the check inside of DoDraw too now that it is here (replace it with asserts is probably better).

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. I’m not sure if assertions can be used here, so I’ll just keep the original logic unchanged.

Copy link
Member

Choose a reason for hiding this comment

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

(although this becomes moot if we make the change I just suggested in the other comment, moving the sync switch to DrawToSurface)

@gaaclarke
Copy link
Member

@ColdPaleLight let me know if you need any help with this PR. There are some internal customers that are interested in the fix and I'm able to take over if you don't have the time to work on this. You've been really helpful in the past, I just wanted to offer to help if you need it.

@gaaclarke
Copy link
Member

Can you also make sure that "Allow edits by maintainers" is enabled. That way I can tweak the PR quickly instead of waiting for your time zone, as long as you are ok with that.

@ColdPaleLight
Copy link
Member Author

Hi, @gaaclarke
I am going to make changes according to your request now.

@ColdPaleLight
Copy link
Member Author

@ColdPaleLight let me know if you need any help with this PR. There are some internal customers that are interested in the fix and I'm able to take over if you don't have the time to work on this. You've been really helpful in the past, I just wanted to offer to help if you need it.

I have plenty of time, and I will try my best to land the PR as soon as possible

@ColdPaleLight
Copy link
Member Author

Can you also make sure that "Allow edits by maintainers" is enabled. That way I can tweak the PR quickly instead of waiting for your time zone, as long as you are ok with that.

"Allow edits by maintainers" is enable. You can directly modify the code if you need.

@ColdPaleLight
Copy link
Member Author

I added a few unit tests to RasterizerTest, so the "needs tests" label can be removed

@ColdPaleLight
Copy link
Member Author

Hi, @gaaclarke
I have completed the work we planned:

  1. move sync switch toRasterizer::DrawToSurface
  2. Remove the sync switch from the external view embedders (Revert https://github.com/flutter/engine/pull/22302)
  3. Revert std::recursive_mutex to std::mutex in SyncSwitch
  4. Add 4 unit tests for the PR

You can review this PR again. By the way, my time zone is GMT + 8,You can directly edit the code yourself if you can’t contact me.

@ColdPaleLight ColdPaleLight changed the title Started providing the GPU sync switch to Rasterizer.Draw() Started providing the GPU sync switch to Rasterizer.DrawToSurface() Sep 1, 2021
Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

Code looks good. I updated the tests and added some comments. I'll approve this once my changes pass CI. Thanks again for all your help! =)

Comment on lines 50 to 52
bool AllowsDrawingWhenGpuDisabled() const override {
return allows_drawing_when_gpu_disabled_;
}
Copy link
Member

Choose a reason for hiding this comment

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

This should be a mock method too.

Copy link
Member

Choose a reason for hiding this comment

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

Done

Comment on lines +374 to +375
EXPECT_TRUE(result);
auto no_discard = [](LayerTree&) { return false; };
Copy link
Member

Choose a reason for hiding this comment

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

All of these tests have the same asserts. They aren't asserting any different behavior for the different variables.

Copy link
Member

Choose a reason for hiding this comment

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

Done

return raster_status;
}

RasterStatus Rasterizer::DrawToSurfaceUnsafe(
Copy link
Member

Choose a reason for hiding this comment

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

Add a comment about why it is unsafe.

Copy link
Member

Choose a reason for hiding this comment

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

Done

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

LGTM!

@gaaclarke gaaclarke merged commit be8467e into flutter:master Sep 1, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 1, 2021
gaaclarke pushed a commit to gaaclarke/engine that referenced this pull request Sep 8, 2021
christopherfujino added a commit that referenced this pull request Sep 9, 2021
* Roll Skia cherrypicks

* Revert "Reland enable DisplayList by default (#27892)" (#28308)

* Started providing the GPU sync switch to Rasterizer.DrawToSurface() (#28383)

Co-authored-by: Aaron Clarke <gaaclarke>

* Update licenses_skia

* update .ci.yaml

Co-authored-by: Jim Graham <flar@google.com>
Co-authored-by: ColdPaleLight <31977171+ColdPaleLight@users.noreply.github.com>
christopherfujino added a commit that referenced this pull request Sep 9, 2021
* Started providing the GPU sync switch to Rasterizer.DrawToSurface() (#28383)
Co-authored-by: Aaron Clarke <gaaclarke>
* 'add branch flutter-2.6-candidate.3 to enabled_branches in .ci.yaml'
* Roll skia cherrypicks
* update licenses_skia

Co-authored-by: ColdPaleLight <31977171+ColdPaleLight@users.noreply.github.com>
@d6638219
Copy link

d6638219 commented Sep 15, 2021

@gaaclarke @christopherfujino flutter 2.0.6 requests to merge the code. flutter-1.26-candidate.17

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash in GrMtlAttachment::onRelease()

3 participants