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 Dec 30, 2021

fix issue: flutter/flutter#95957

There will be a situation where AndroidSurfaceGL::on_screen_surface_ is set, but Rasterizer::surface_ has not been set. So when'AndroidSurfaceGL::CreatePbufferSurface' is called, AndroidSurfaceGL::on_screen_surface_ may already exist. At this time, we should not generate a new one, but should directly use the existing one.

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.


std::unique_ptr<Surface> AndroidSurfaceGL::CreatePbufferSurface() {
onscreen_surface_ = GLContextPtr()->CreatePbufferSurface();
if (!onscreen_surface_ || !onscreen_surface_->IsValid()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe just an early return instead? This needs a test too.

Copy link
Member Author

@ColdPaleLight ColdPaleLight Dec 30, 2021

Choose a reason for hiding this comment

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

If we early return, then Rasterizer::DoMakeRasterSnapshot will execute the following logic, which will change from hardware rasterization to software rasterization. The performance will be a little bit worse, but the logic should be correct. If you think an early return is more reasonable, I'm happy to tweak the code.

if (!snapshot_surface) {
// Raster surface is fine if there is no on screen surface. This might
// happen in case of software rendering.
sk_sp<SkSurface> sk_surface = SkSurface::MakeRaster(image_info);
result = DrawSnapshot(sk_surface, draw_callback);
} else {

And about test, I'm not sure how to test this, would you mind giving me some suggestions ? Thank you :) .

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 thought about it again, the early return will introduce a new problem. If Rasterizer::DoMakeRasterSnapshot is executed multiple times when the app is in the background. The first time there is no problem, and AndroidSurfaceGL::on_screen_surface_ which size is 1x1 is created, but the second time, because AndroidSurfaceGL::on_screen_surface_ already exists, it is returned early. As a result, Rasterizer::DoMakeRasterSnapshot has changed from hardware rasterization to software rasterization.

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 the part I'm not clear on is how we'd get here.

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 the part I'm not clear on is how we'd get here.

Although I can't provide a reproducible sample, it appeared many times in my application, and I finally found the reason for the problem by printing the Log, and the problem was triggered by this line of code.
https://github.com/flutter/flutter/blob/019f03374c5b106c3fde05c8eee930cd26b8ffff/packages/flutter/lib/src/painting/shader_warm_up.dart#L95

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 this is a combination of a few things:

  • The platform thread creates the onscreen surface
  • It then calls Shell::NotifyCreated
  • NotifyCreated fire and forgets the UI task runner, which might schedule an image decode (this is a recent change, from November or Decemberish)
  • The rasterizer setup is called, but now there's a race where the UI thread might have asked the rasterizer to make a snapshot before it has been setup
  • If the UI thread beats rasterizer setup, the rasterizer will say it doesn't have a surface to work with and ask for a snapshotting surface. The AndroidContextGL creates a pbuffer surface for this and sets it as the onscreen_surface_, which destroys the one we actually want.

Copy link
Contributor

Choose a reason for hiding this comment

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

So what I think should happen here is the following:

  • Rename AndroidSurfaceGL::CreatePbufferSurface to something like AndroidSurfaceGL::GetOrCreateSnapshotSurface.
  • Have that renamed method only create the Pbuffer surface if onscreen_surface_ is null/invalid. Otherwise, return onscreen_surface_.
  • Write a test that exercises this, e.g. latch the raster task runner before NotifyCreated is called, run some Dart code that calls and completes toImage, unlatch the raster task runner, make sure the rasterizer surface is the right one.

Copy link

Choose a reason for hiding this comment

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

Have that renamed method only create the Pbuffer surface if onscreen_surface_ is null/invalid. Otherwise, return onscreen_surface_.

What happens if the buffer isn't a Pbuffer at this time? Does it make sense to destroy the existing surface and then create the actual Pbuffer?

Copy link
Contributor

Choose a reason for hiding this comment

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

No. We just want to make sure that we have a GPU backed surface and don't end up in the software backend code.

Copy link
Member Author

Choose a reason for hiding this comment

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

So what I think should happen here is the following:

  • Rename AndroidSurfaceGL::CreatePbufferSurface to something like AndroidSurfaceGL::GetOrCreateSnapshotSurface.
  • Have that renamed method only create the Pbuffer surface if onscreen_surface_ is null/invalid. Otherwise, return onscreen_surface_.
  • Write a test that exercises this, e.g. latch the raster task runner before NotifyCreated is called, run some Dart code that calls and completes toImage, unlatch the raster task runner, make sure the rasterizer surface is the right one.

@dnfield
We can't return onscreen_surface_ because the type of the return value is std::unique_ptr<Surface>, but the type of onscreen_surface_ is std::unique_ptr<AndroidEGLSurface>. So in either case we have to create a new std::unique_ptr<Surface>. so I renamed it to AndroidSurfaceGL::CreateSnapshotSurface. I also added test cases as you said in discord.

@ColdPaleLight ColdPaleLight requested a review from dnfield January 13, 2022 14:09
@ColdPaleLight ColdPaleLight changed the title Only creates 'onscreen_surface_' when it's not available in 'AndroidSurfaceGL::CreatePbufferSurface' Only creates 'onscreen_surface_' when it's not available in 'AndroidSurfaceGL::CreateSnapshotSurface' Jan 13, 2022
// |GPUSurfaceGLDelegate|
sk_sp<const GrGLInterface> GetGLInterface() const override;

AndroidEGLSurface* GetOnscreenSurface() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a doc comment, probably explaining that this is only intended for tests.

e.g.

  // Obtain a raw pointer to the AndroidEGLSurface. This method is intended for use in tests. Callers must not delete this pointer.

Copy link
Contributor

Choose a reason for hiding this comment

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

(I went ahead and made this change so we can just get this landed, rather than wait for another time zone cycle :)

Copy link
Contributor

@dnfield dnfield 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 doc nit

@dnfield dnfield added the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Jan 13, 2022
@dnfield dnfield merged commit fab1982 into flutter:main Jan 13, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jan 13, 2022
JsouLiang pushed a commit to JsouLiang/engine that referenced this pull request Jan 14, 2022
…urfaceGL::CreateSnapshotSurface' (flutter#30590)

* Only creates 'onscreen_surface_' when it's not available in 'AndroidSurfaceGL::CreatePbufferSurface'

* Rename the function and add some unit tests

* Update android_surface_gl.h

Co-authored-by: Dan Field <[email protected]>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

customer: alibaba needs tests platform-android waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants