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

Prevent app from accessing the GPU in the background in EncodeImage #28369

Merged
merged 10 commits into from
Sep 9, 2021

Conversation

ColdPaleLight
Copy link
Member

@ColdPaleLight ColdPaleLight commented Aug 30, 2021

fix issue: flutter/flutter#89182

about test:
(edit ColdPaleLight: I added the new test as #28369 (comment))
(edit ColdPaleLight: I added the test #28369 (comment))
I tried to add a test like before, but this is a bit difficult, maybe I need some help

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.

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'm not sure about testing. One not great idea would be to pull ConvertToRasterUsingResourceContext into a header and making templated so you can pass in a mock SyncSwitch. You could go as high as ConvertImageToRaster. Since it's just a file local function it shouldn't change much. You might want to try @chinmaygarde for better ideas.

template<typename Switch>
sk_sp<SkImage> ConvertToRasterUsingResourceContext(
    sk_sp<SkImage> image,
    GrDirectContext* resource_context
    const std::shared_ptr<Switch>& sync_switch) {
  ...
}

@@ -132,19 +143,20 @@ void ConvertImageToRaster(sk_sp<SkImage> image,
// to prevent concurrent usage of the image on both the IO and raster threads.
raster_task_runner->PostTask([image, encode_task = std::move(encode_task),
resource_context, snapshot_delegate,
io_task_runner]() {
io_task_runner, &gpu_disable_sync_switch]() {
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 capture a copy of the shared_ptr, not a reference to the shared_ptr.

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

sk_sp<SkImage> raster_image =
snapshot_delegate->ConvertToRasterImage(image);

io_task_runner->PostTask([image, encode_task = std::move(encode_task),
raster_image = std::move(raster_image),
resource_context]() mutable {
resource_context,
&gpu_disable_sync_switch]() mutable {
Copy link
Member

Choose a reason for hiding this comment

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

Same here, capture a copy.

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

@ColdPaleLight
Copy link
Member Author

ColdPaleLight commented Aug 31, 2021

I'm not sure about testing. One not great idea would be to pull ConvertToRasterUsingResourceContext into a header and making templated so you can pass in a mock SyncSwitch. You could go as high as ConvertImageToRaster. Since it's just a file local function it shouldn't change much. You might want to try @chinmaygarde for better ideas.

template<typename Switch>
sk_sp<SkImage> ConvertToRasterUsingResourceContext(
    sk_sp<SkImage> image,
    GrDirectContext* resource_context
    const std::shared_ptr<Switch>& sync_switch) {
  ...
}

I briefly tried your suggestion, and I think the change is still a bit big.Since existing tests can ensure that EncodeImage works as expected. In addition, this patch should not be easy to be accidentally revert back, so is it possible not to add additional tests?

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.

The code looks good now minus a naming nit. The existing tests don't assert that the new code is actually doing anything. A low level test that tests that the sync switch at least is being exercised is better than nothing. I wouldn't worry about how easy something is to revert.

I personally think the imperfect test is better than no test. If you strongly disagree we can defer the decision about no tests to someone else on the team.

(edit: To be clear, my request for changes has to do with the name change. The testing question I can defer to someone else on the team if you feel strongly against the tests)

@@ -65,15 +65,24 @@ void InvokeDataCallback(std::unique_ptr<DartPersistentValue> callback,

sk_sp<SkImage> ConvertToRasterUsingResourceContext(
sk_sp<SkImage> image,
GrDirectContext* resource_context) {
fml::WeakPtr<GrDirectContext> resource_context,
const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch) {
Copy link
Member

Choose a reason for hiding this comment

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

s/gpu_disable_sync_switch/is_gpu_disabled_sync_switch

That way the "SetIfTrue" makes sense.

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

@zanderso
Copy link
Member

I agree with @gaaclarke that this PR needs at least a simple unit test. Among other reasons, without a test, it can't be considered for a cherry-pick to the beta or stable branches if one is needed. The test @gaaclarke describes sounds adequate to me.

@ColdPaleLight
Copy link
Member Author

ColdPaleLight commented Sep 1, 2021

Hi, @gaaclarke @zanderso
I think what you said is very reasonable, I will add a test to this PR.

(editing ColdPaleLight : I added the test: #28369 (comment) )
In addition, I have an idea, I don’t know if it is feasible.In fact, in this use case, we just want to test whether SyncSwitch::Execute has been executed. Can we change some code of SyncSwitch to make it better for testing?

For example, if SyncSwitch has a Listener, if this Listener is registered, then the Listener can be notified every time when SyncSwitch::SetSwitch or SyncSwitch::Execute is called. If SyncSwitch adds this feature, the problem of testing whether SyncSwitch::Execute is called will be able to easily resolved, I believe there will be such scenarios in the future.

Or there is a State in the SyncSwitch, and there is an execute_count_ in the State to record the number of operations. Each time SyncSwitch::Execute will increase the execute_count_. We can check whether SyncSwitch::Execute is executed by execute_count

In addition, I want to know whether Engine has predefined macros in the test environment. If so, can we add additional code to some classes based on the predefined macros to make it easier to test?

Anyway, I will add a test to this PR.

Thanks

@ColdPaleLight
Copy link
Member Author

ColdPaleLight commented Sep 2, 2021

Hi, @gaaclarke @zanderso
I added the unit tests for this PR. I used the predefined macro FLUTTER_RELEASE to distinguish the following two cases:

  1. If FLUTTER_RELEASE is not defined, it means that it is currently in debug or profile mode. I did the following
    a. Added WasExecuted and ClearWasExecutedFlag methods to SyncSwitch to help test whether the SyncSwitch::Execute method was called, and I also added a test for new method.
    b. Add a flutter::testing::ConvertToRasterUsingResourceContextForTest to image_encoding.cc, specifically for testing.
    c. Add test case TEST_F(ShellTest, EncodeImageAccessesSyncSwitch)
  2. If FLUTTER_RELEASE is defined, it means that it is currently in release mode.
    Everything remains the same as before.

This Idea comes from : #25868

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 don't particularly like this test. I'm out-of-office but I'd like to spend some time trying to write a test for you when the holiday weekend is over. I probably won't approve something like this until you've decided this is the best you could come up with and one of us tries to come up with an alternative. If @zanderso, feels different that's fine with me.

@ColdPaleLight
Copy link
Member Author

ColdPaleLight commented Sep 3, 2021

Hi, @gaaclarke
Thanks for your reply. In fact, I am not sure whether the current test is good or not, so your opinion is very important to me. May I ask why you don’t like this test?

You are more experienced in this field than me, so I will follow the advice you provided before to tweak the test.

Let me talk about my initial thoughts. I thought the advantages of this test are these two:

  1. There is no change if FLUTTER_RELEASE is defined
  2. Minimize the changes of EncodeImage, where the image_encoding.h file has no changes, and the image_encoding.cc file only adds a test method.

The disadvantage of this test is that these two: ( I guess this may be the reason you don’t like this test )

  1. FLUTTER_RELEASE macro appears in multiple places in the code
  2. SyncSwitch has been changed: Two methods that have nothing to do with its core functions have been added.

Then I saw that FLUTTER_RELEASE is also used in the project, so I decided to try to write this test first.

Comment on lines 8 to 11
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
Copy link
Member Author

@ColdPaleLight ColdPaleLight Sep 4, 2021

Choose a reason for hiding this comment

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

Nit: This is where I am a little hesitant to use this approach. We have included more header files than before

Copy link
Member

Choose a reason for hiding this comment

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

Just pull ConvertToRasterUsingResourceContext into its own header file and include it in image_encoding.cc and image_encoding_unittests.cc. Name it something like image_encoding_impl.h. It doesn't need to be publicly visible from image_encoding.h

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

@ColdPaleLight
Copy link
Member Author

ColdPaleLight commented Sep 4, 2021

Hi, @gaaclarke
I am very grateful that you are willing to spend some time writing tests for me. But I still want to try again by myself, because I think I can make it better.

I revert the previous test and wrote a new test based on your suggestion:

  1. Pull ConvertToRasterUsingResourceContext into a header and making templated.
  2. Add a MockSyncSwitch for test.
  3. Add test case TEST_F(ShellTest, EncodeImageAccessesSyncSwitch)

Would you mind reviewing it again?

Thanks

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.

Nice! I like this approach much better, thanks. Sorry I didn't explain myself better last time. Your last approach relied on conditional compilation which is difficult to maintain. That's why this approach is preferred.

Comment on lines 133 to 134
const std::shared_ptr<const MockSyncSwitch>& is_gpu_disabled_sync_switch =
std::make_shared<MockSyncSwitch>();
Copy link
Member

Choose a reason for hiding this comment

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

This variable shouldn't be a reference. You can just use auto here auto is_gpu_disabled_sync_switch = std::make_shared<MockSyncSwitch>(); since the type is in the right hand expression.

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

Comment on lines 8 to 11
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
Copy link
Member

Choose a reason for hiding this comment

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

Just pull ConvertToRasterUsingResourceContext into its own header file and include it in image_encoding.cc and image_encoding_unittests.cc. Name it something like image_encoding_impl.h. It doesn't need to be publicly visible from image_encoding.h

CreateNewThread() // io
);

auto nativeEncodeImage = [&](Dart_NativeArguments args) {
Copy link
Member

Choose a reason for hiding this comment

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

s/nativeEncodeImage/native_encode_image

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

if (!raster_image) {
// The rasterizer was unable to render the cross-context image
// (presumably because it does not have a GrContext). In that case,
// convert the image on the IO thread using the resource context.
raster_image =
ConvertToRasterUsingResourceContext(image, resource_context);
raster_image = ConvertToRasterUsingResourceContext<fml::SyncSwitch>(
Copy link
Member

Choose a reason for hiding this comment

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

Remove <fml::SyncSwitch>, the type system will do that for 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.

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.

Nice work @ColdPaleLight! Nice find, nice fix, and nice followup on your PR. Thanks =)

This PR was a bit weird because it was hard to test, I think this is a good approach though considering the circumstances.

.WillOnce([](const MockSyncSwitch::Handlers& handlers) {
handlers.true_handler();
});
ConvertToRasterUsingResourceContext<MockSyncSwitch>(
Copy link
Member

Choose a reason for hiding this comment

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

nit: remove <MockSyncSwitch>

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

@ColdPaleLight
Copy link
Member Author

Nice work @ColdPaleLight! Nice find, nice fix, and nice followup on your PR. Thanks =)

This PR was a bit weird because it was hard to test, I think this is a good approach though considering the circumstances.

Thank you, @gaaclarke , I have learned a lot from you in this process. : )

@gaaclarke
Copy link
Member

(rebasing onto master to try to address the CI flakes)

@gaaclarke gaaclarke 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 Sep 9, 2021
@fluttergithubbot fluttergithubbot merged commit 00f0d00 into flutter:master Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 9, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 10, 2021
@dodatw
Copy link

dodatw commented Jan 11, 2022

Is this fixing in flutter 2.8 ? It still have MTLGetWarningMode crash in flutter 2.8

@ColdPaleLight
Copy link
Member Author

Is this fixing in flutter 2.8 ? It still have MTLGetWarningMode crash in flutter 2.8

This patch is in 2.8. I think the problem you encountered should be related to flutter/flutter#89171, you can pay attention to the 2.9 or higher version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes customer: alibaba 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
5 participants