From bf9d2633fab5d48e7f6bae68ce30a2dbfdfb52ed Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 4 Nov 2019 08:52:40 -0800 Subject: [PATCH 001/591] Roll src/third_party/dart bbe2ac28c9..ab5cf0f854 (73 commits) (#13614) dart-lang/sdk@ab5cf0f854 [vm] Ensure create_sdk GN build has working dart2native iff AOT is supported dart-lang/sdk@d2bd6df607 [infra] Update make_version.py hack for Flutter for 2.7.0 dart-lang/sdk@6c0387da67 Fix typos in CHANGELOG.md dart-lang/sdk@740cd89ad8 Fix link markup in ffi README dart-lang/sdk@f91ef3afd7 [kernel] Deprecate Library.isExternal dart-lang/sdk@c75e5e4cf4 [CFE] Fix assert in parser dart-lang/sdk@2438fe10cb Rename BottomTypeImpl to NeverTypeImpl. dart-lang/sdk@523e9ffa88 [cfe] Add EqualsExpression, BinaryExpression, UnaryExpression and ParenthesizedExpression dart-lang/sdk@a996783de5 [cfe+analyzer] Move StackListener to _fe_analyzer_shared dart-lang/sdk@28cc459458 Implement List.empty() in the DDC patch file. dart-lang/sdk@e83e66111d Copy propagate on private symbol vars dart-lang/sdk@0aaa1c5fc8 Port of dart:core to NNBD. dart-lang/sdk@b76ab4012d linter 0.1.103 dart-lang/sdk@9bffff6dfc Fix exception in migration of ?? operator when the right hand side is a generic type dart-lang/sdk@ed1910006c [vm/bytecode] Cleanup support for bytecode format before v20 dart-lang/sdk@d96cd87896 [vm, reload] Initialize new fields in existing instances lazily. dart-lang/sdk@8b67fcbbd1 [vm] Cleanup script tags dart-lang/sdk@ebc6340e36 [gardening] Skip bytecode_with_ast_in_aot_test on crossword-ast bot dart-lang/sdk@79c7d3d8bc [vm] Always use bytecode when it is present in a kernel file. Let FLAG_use_bytecode_compiler only control whether the kernel isolate generates bytecode. dart-lang/sdk@7db3b15a48 Update changelog for analysis_server_client 1.1.2 dart-lang/sdk@fb5c75c506 Upgrade dartdoc to 0.29.0. dart-lang/sdk@eab70c2673 Visit annotations on classes in EdgeBuilder dart-lang/sdk@9fd4d468b2 NNBD i13n: Temporary fix in order to migrate path package dart-lang/sdk@e47a531e61 dartfix: Hide --server option; don't include hidden options in CHANGELOG dart-lang/sdk@5965789b92 [CFE] Fix bug in lint test dart-lang/sdk@b3ed442f70 Fix some unnecessary imports of front_end in analyzer dart-lang/sdk@2ad5af1e13 Move resolve_relative_uri.dart into _fe_analyzer_shared dart-lang/sdk@51023cbb54 [dart2js] Add test for sequence of promotions dart-lang/sdk@c4b6df5009 Move ID testing infrastructure into _fe_analyzer_shared. dart-lang/sdk@3e5b3b15a0 [cfe] Add the initial functionality of reachability analysis dart-lang/sdk@6ea11a8b64 Decouple colors.dart from CompilerContext dart-lang/sdk@00c2962db8 [cfe+analyzer] Move messages, scanner and parser to package:_fe_analyzer_shared dart-lang/sdk@df678dbec0 [VM/nnbd] Introduce type Never in VM and bytecode. dart-lang/sdk@917ba19c1a [ VM / Service ] Remove flaky portion of get_flag_list_rpc_test dart-lang/sdk@bc6cadfac4 [ VM / Service ] Fix get_flag_list_rpc_test for real this time dart-lang/sdk@b81eb3bfc8 Bump dartfix to 0.1.5; add CHANGELOG notes dart-lang/sdk@3224a6633b [ VM / dart:io ] Replace usage of SecTrustEvaluate with SecTrustEvaluateWithError on MacOS >= 10.14 dart-lang/sdk@0dd494bc54 [ VM / Service ] Fix get_flag_list_rpc_test failing on release builds. dart-lang/sdk@8a09d7ab5a [vm/bytecode] Remove alignment of sections in bytecode dart-lang/sdk@d9b069b27d [vm] Use `fuchsia.deprecatedtimezone` dart-lang/sdk@550985189c Added errors for variance positions in fields. dart-lang/sdk@924ec34e01 Remove analysis logger, replace with calls to InstrumentationService dart-lang/sdk@09bc0cc5ab Issue 38734. Don't report IMPLICIT_THIS_REFERENCE_IN_INITIALIZER for late fields. dart-lang/sdk@60c34ac420 [ VM / Service ] Allow for the profiler to be enabled/disabled via SetFlags RPC dart-lang/sdk@cc98941bdd [dart2js] new-rti: defer infrequent types in lazy initializers dart-lang/sdk@84a8a2db33 Issue 39192. Fix codeLength / codeOffset, store it into the cached data. dart-lang/sdk@0f7b67477f Invalidate DDC library cache on trackLibraries dart-lang/sdk@94e312f7a6 Added errors for variance positions in method members. dart-lang/sdk@489591068b Revert "Exclude flow_analysis/data directories from analysis." dart-lang/sdk@bb45179cad Remove tests already moved to ImplicitThisReferenceInInitializerTest. dart-lang/sdk@a00376349a Exclude flow_analysis/data directories from analysis. dart-lang/sdk@d7def97773 typo dart-lang/sdk@f7722a2ecd [frontend_server] write JavaScript source maps dart-lang/sdk@f9b57bdfa8 Update the language version in DDC patch files for migrated libraries. dart-lang/sdk@9b15195448 Inline CompialtionUnitElementImpl.getTypeFromTypes dart-lang/sdk@7c6d212e66 Exclude shared integration test suites from analysis. dart-lang/sdk@4551080062 Add retry logic for incremental compiler initialization. dart-lang/sdk@f78e5463c9 Clean up server code to remove a lint dart-lang/sdk@e56612020c Prepare to publish analyzer 0.39.1. dart-lang/sdk@bb8ddfcdd6 Format definite_assignment test cases. dart-lang/sdk@a0ee662b7b Move shared analyzer/FE integration test data into shared package. dart-lang/sdk@cdcc5282ea [vm] Add NativePort static extension to `dart:ffi` dart-lang/sdk@6b61050d9c [cfe+analyzer] Split generated message files into shared and cfe-only dart-lang/sdk@f8ab10b322 Remove CompilationUnitElementImpl.replaceTopLevelVariable dart-lang/sdk@5b4612bc68 Move front_end/lib/src/base/errors.dart into _fe_analyzer_shared package. dart-lang/sdk@29ab43e0e2 Move flow_analysis tests into package:_fe_analyzer_shared. dart-lang/sdk@499815842c Create a package to hold code shared between front_end and analyzer. dart-lang/sdk@d76ffbc149 [cfe] Handle assigned variables in for and for-in dart-lang/sdk@bb71c3eecc [dart2js] Ensure type check metadata is generated for types that otherwise only appear in substitutions. dart-lang/sdk@6f5aee9f7f Update dart2native.dart dart-lang/sdk@2897bebdef [cfe] Resolve serialization mismatch due to variance computation dart-lang/sdk@decca61f8c Deprecate TypeDefiningElement.type except ExecutableElement. dart-lang/sdk@fde7cbf293 support type params in stateful widget conversion --- DEPS | 6 +++--- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 4519e6a5ff476..2692bf474f5ef 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'bbe2ac28c9ab5bfec4e13d5df9360e7aa691cfe6', + 'dart_revision': 'ab5cf0f854f4cae1c0adad5f7a8aa158b1d646ca', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -63,7 +63,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.15.7', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.102', + 'dart_linter_tag': '0.1.103', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.3', @@ -231,7 +231,7 @@ deps = { Var('dart_git') + '/dart2js_info.git' + '@' + Var('dart_dart2js_info_tag'), 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@v0.28.8', + Var('dart_git') + '/dartdoc.git@v0.29.0', 'src/third_party/dart/third_party/pkg/ffi': Var('dart_git') + '/ffi.git' + '@' + Var('dart_ffi_tag'), diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index a3a74a5cb0e3a..2b5938972d195 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 10793d772d6c660b5fab347dc5ac6cf2 +Signature: 8114cc2f074215c96eafc221089b981b UNUSED LICENSES: From 6e0e2274f165c15c29ccd84bc6438202ccfb6644 Mon Sep 17 00:00:00 2001 From: Yegor Date: Mon, 4 Nov 2019 10:16:38 -0800 Subject: [PATCH 002/591] fix getBoxesForRange for zero-length ranges (#13483) --- lib/web_ui/lib/src/engine/text/paragraph.dart | 2 +- lib/web_ui/test/paragraph_test.dart | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 3214fb897d0df..6574cbddbc1d8 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -201,7 +201,7 @@ class EngineParagraph implements ui.Paragraph { }) { assert(boxHeightStyle != null); assert(boxWidthStyle != null); - if (_plainText == null) { + if (_plainText == null || start == end) { return []; } diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart index 99700e11ed599..4d5c0a92c86b2 100644 --- a/lib/web_ui/test/paragraph_test.dart +++ b/lib/web_ui/test/paragraph_test.dart @@ -96,4 +96,40 @@ void main() async { paragraph2.layout(ParagraphConstraints(width: fontSize * 5.0)); expect(paragraph2.height, closeTo(fontSize, 0.001)); // because it wraps }); + + test('getBoxesForRange returns a box', () { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 10, + textDirection: TextDirection.rtl, + )); + builder.addText('abcd'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 1000)); + expect( + paragraph.getBoxesForRange(1, 2).single, + const TextBox.fromLTRBD( + 10, + 0, + 20, + 10, + TextDirection.rtl, + ), + ); + }); + + test('getBoxesForRange return empty list for zero-length range', () { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 10, + )); + builder.addText('abcd'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 1000)); + expect(paragraph.getBoxesForRange(0, 0), isEmpty); + }); } From 4161d5d3fe5bf05303629cacbdd51ffad28daa31 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 4 Nov 2019 14:25:41 -0500 Subject: [PATCH 003/591] Roll fuchsia/sdk/core/mac-amd64 from ggsQ5... to XcaoM... (#13535) Roll fuchsia/sdk/core/mac-amd64 from ggsQ5... to XcaoM... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 2692bf474f5ef..acd5c6573ac83 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'ggsQ5vCgXEhoHFZSNBKKZWxStVwTPSlaOD2fRdJyL_gC' + 'version': 'XcaoMSXfWFsCkt_giGaUOk3isEOMGjIv6LWtd8FPgjgC' } ], 'condition': 'host_os == "mac"', From e73c9c8f6bd5876f1d95df98ec47a36a00776148 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 4 Nov 2019 14:28:23 -0500 Subject: [PATCH 004/591] Roll src/third_party/skia 8e083eee8ece..4cc2dc64ff13 (1 commits) (#13543) https://skia.googlesource.com/skia.git/+log/8e083eee8ece..4cc2dc64ff13 git log 8e083eee8ece..4cc2dc64ff13 --date=short --no-merges --format='%ad %ae %s' 2019-11-03 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update SKP version Created with: gclient setdep -r src/third_party/skia@4cc2dc64ff13 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index acd5c6573ac83..e41af26f2cbe6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8e083eee8ece4e1acaeb01ae4192050969c4b0c6', + 'skia_revision': '4cc2dc64ff134fca1f26651b9b2c8882ec7f0837', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 3683ad8f6e01f..8de484254768c 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 77f23f792f731e0334254b8005795881 +Signature: 6ca0730fa38db7898dad5c1db34bee73 UNUSED LICENSES: From 02a479007420b05df8e075978ecdd15442ea520f Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Mon, 4 Nov 2019 12:33:41 -0800 Subject: [PATCH 005/591] Added new lifecycle enum (#11913) --- lib/ui/window.dart | 16 ++--- lib/web_ui/lib/src/ui/window.dart | 13 ++-- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- .../FlutterActivityAndFragmentDelegate.java | 2 + .../systemchannels/LifecycleChannel.java | 4 ++ ...lutterActivityAndFragmentDelegateTest.java | 11 ++++ .../ios/framework/Source/FlutterEngine.mm | 3 + .../ScenariosTests/AppLifecycleTests.m | 66 +++++++++++++++++++ 9 files changed, 99 insertions(+), 20 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 3bd28a248bbba..954640ab79167 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -171,18 +171,16 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - /// - /// Android apps in this state should assume that they may enter the - /// [suspending] state at any time. paused, - /// The application will be suspended momentarily. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// The application is still hosted on a flutter engine but is detached from + /// any host views. /// - /// On iOS, this state is currently unused. - suspending, + /// When the application is in this state, the engine is running without + /// a view. It can either be in the progress of attaching a view when engine + /// was first initializes, or after the view being destroyed due to a Navigator + /// pop. + detached, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 58c82410de7c6..731874e6dff97 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -73,18 +73,13 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - /// - /// Android apps in this state should assume that they may enter the - /// [suspending] state at any time. paused, - /// The application will be suspended momentarily. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// The application is detached from view. /// - /// On iOS, this state is currently unused. - suspending, + /// When the application is in this state, the engine is running without + /// a platform UI. + detached, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index c0ee45d762e81..5ade4672cbece 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -120,7 +120,7 @@ class RuntimeController final : public WindowClient { std::string variant_code; std::vector locale_data; std::string user_settings_data = "{}"; - std::string lifecycle_state; + std::string lifecycle_state = "AppLifecycleState.detached"; bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 38b034d52d0d0..77b6577271511 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -311,7 +311,7 @@ bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { const auto& data = message->data(); std::string state(reinterpret_cast(data.data()), data.size()); if (state == "AppLifecycleState.paused" || - state == "AppLifecycleState.suspending") { + state == "AppLifecycleState.detached") { activity_running_ = false; StopAnimator(); } else if (state == "AppLifecycleState.resumed" || diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index eddacf2b25531..6cebfcbef6232 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -461,6 +461,8 @@ void onDetach() { platformPlugin = null; } + flutterEngine.getLifecycleChannel().appIsDetached(); + // Destroy our FlutterEngine if we're not set to retain it. if (host.shouldDestroyEngineWithHost()) { flutterEngine.destroy(); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java index abc6323907d0b..cd244ff8251e1 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java @@ -39,4 +39,8 @@ public void appIsPaused() { channel.send("AppLifecycleState.paused"); } + public void appIsDetached() { + Log.v(TAG, "Sending AppLifecycleState.detached message."); + channel.send("AppLifecycleState.detached"); + } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 154c59f173191..d335fa5064dbc 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -85,18 +85,21 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter. delegate.onResume(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is paused, an inactive message should have been sent to Flutter. delegate.onPause(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is stopped, a paused message should have been sent to Flutter. // Notice that Flutter uses the term "paused" in a different way, and at a different time @@ -105,6 +108,14 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); + + // When activity detaches, a detached message should have been sent to Flutter. + delegate.onDetach(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsDetached(); } @Test diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 83551d2aed042..27c8b9ece0a77 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -180,6 +180,8 @@ - (void)setViewController:(FlutterViewController*)viewController { [self maybeSetupPlatformViewChannels]; __block FlutterEngine* blockSelf = self; + [_flutterViewControllerWillDeallocObserver release]; + self.flutterViewControllerWillDeallocObserver = [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc object:viewController @@ -201,6 +203,7 @@ - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { } - (void)notifyViewControllerDeallocated { + [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"]; if (!_allowHeadlessExecution) { [self destroyContext]; } else { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m index 3646e3fd5ee40..7f8f6902bde2a 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m @@ -225,4 +225,70 @@ - (void)testVisibleFlutterViewControllerRespondsToApplicationLifecycle { [engine setViewController:nil]; } +- (void)testFlutterViewControllerDetachingSendsApplicationLifecycle { + XCTestExpectation* engineStartedExpectation = [self expectationWithDescription:@"Engine started"]; + + // Let the engine finish booting (at the end of which the channels are properly set-up) before + // moving onto the next step of showing the next view controller. + ScreenBeforeFlutter* rootVC = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:^void() { + [engineStartedExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:nil]; + + UIApplication* application = UIApplication.sharedApplication; + application.delegate.window.rootViewController = rootVC; + FlutterEngine* engine = rootVC.engine; + + NSMutableArray* lifecycleExpectations = [NSMutableArray arrayWithCapacity:10]; + + // Expected sequence from showing the FlutterViewController is inactive and resumed. + [lifecycleExpectations addObjectsFromArray:@[ + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" + forStep:@"showing a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed" + forStep:@"showing a FlutterViewController"] + ]]; + // At the end of Flutter VC, we want to make sure it deallocs and sends detached signal. + // Using autoreleasepool will guarantee that. + FlutterViewController* flutterVC; + @autoreleasepool { + flutterVC = [rootVC showFlutter]; + [engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) { + if (lifecycleExpectations.count == 0) { + XCTFail(@"Unexpected lifecycle transition: %@", message); + return; + } + XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0]; + if (![[nextExpectation expectedLifecycle] isEqualToString:message]) { + XCTFail(@"Expected lifecycle %@ but instead received %@", + [nextExpectation expectedLifecycle], message); + return; + } + + [nextExpectation fulfill]; + [lifecycleExpectations removeObjectAtIndex:0]; + }]; + + [self waitForExpectations:lifecycleExpectations timeout:5]; + + // Starts dealloc flutter VC. + [lifecycleExpectations addObjectsFromArray:@[ + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" + forStep:@"detaching a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.paused" + forStep:@"detaching a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] + initForLifecycle:@"AppLifecycleState.detached" + forStep:@"detaching a FlutterViewController"] + ]]; + [flutterVC dismissViewControllerAnimated:NO completion:nil]; + flutterVC = nil; + } + [self waitForExpectations:lifecycleExpectations timeout:5]; + + [engine.lifecycleChannel setMessageHandler:nil]; + [engine setViewController:nil]; +} + @end From fe0838e94860d30ea68a2f3dda52be17ca20411a Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Mon, 4 Nov 2019 13:40:20 -0800 Subject: [PATCH 006/591] Revert "Added new lifecycle enum (#11913)" (#13632) This reverts commit 02a479007420b05df8e075978ecdd15442ea520f. --- lib/ui/window.dart | 16 +++-- lib/web_ui/lib/src/ui/window.dart | 13 ++-- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- .../FlutterActivityAndFragmentDelegate.java | 2 - .../systemchannels/LifecycleChannel.java | 4 -- ...lutterActivityAndFragmentDelegateTest.java | 11 ---- .../ios/framework/Source/FlutterEngine.mm | 3 - .../ScenariosTests/AppLifecycleTests.m | 66 ------------------- 9 files changed, 20 insertions(+), 99 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 954640ab79167..3bd28a248bbba 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -171,16 +171,18 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// Android apps in this state should assume that they may enter the + /// [suspending] state at any time. paused, - /// The application is still hosted on a flutter engine but is detached from - /// any host views. + /// The application will be suspended momentarily. + /// + /// When the application is in this state, the engine will not call the + /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. /// - /// When the application is in this state, the engine is running without - /// a view. It can either be in the progress of attaching a view when engine - /// was first initializes, or after the view being destroyed due to a Navigator - /// pop. - detached, + /// On iOS, this state is currently unused. + suspending, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 731874e6dff97..58c82410de7c6 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -73,13 +73,18 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// Android apps in this state should assume that they may enter the + /// [suspending] state at any time. paused, - /// The application is detached from view. + /// The application will be suspended momentarily. + /// + /// When the application is in this state, the engine will not call the + /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. /// - /// When the application is in this state, the engine is running without - /// a platform UI. - detached, + /// On iOS, this state is currently unused. + suspending, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 5ade4672cbece..c0ee45d762e81 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -120,7 +120,7 @@ class RuntimeController final : public WindowClient { std::string variant_code; std::vector locale_data; std::string user_settings_data = "{}"; - std::string lifecycle_state = "AppLifecycleState.detached"; + std::string lifecycle_state; bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 77b6577271511..38b034d52d0d0 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -311,7 +311,7 @@ bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { const auto& data = message->data(); std::string state(reinterpret_cast(data.data()), data.size()); if (state == "AppLifecycleState.paused" || - state == "AppLifecycleState.detached") { + state == "AppLifecycleState.suspending") { activity_running_ = false; StopAnimator(); } else if (state == "AppLifecycleState.resumed" || diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 6cebfcbef6232..eddacf2b25531 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -461,8 +461,6 @@ void onDetach() { platformPlugin = null; } - flutterEngine.getLifecycleChannel().appIsDetached(); - // Destroy our FlutterEngine if we're not set to retain it. if (host.shouldDestroyEngineWithHost()) { flutterEngine.destroy(); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java index cd244ff8251e1..abc6323907d0b 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java @@ -39,8 +39,4 @@ public void appIsPaused() { channel.send("AppLifecycleState.paused"); } - public void appIsDetached() { - Log.v(TAG, "Sending AppLifecycleState.detached message."); - channel.send("AppLifecycleState.detached"); - } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index d335fa5064dbc..154c59f173191 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -85,21 +85,18 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter. delegate.onResume(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is paused, an inactive message should have been sent to Flutter. delegate.onPause(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is stopped, a paused message should have been sent to Flutter. // Notice that Flutter uses the term "paused" in a different way, and at a different time @@ -108,14 +105,6 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); - - // When activity detaches, a detached message should have been sent to Flutter. - delegate.onDetach(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsDetached(); } @Test diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 27c8b9ece0a77..83551d2aed042 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -180,8 +180,6 @@ - (void)setViewController:(FlutterViewController*)viewController { [self maybeSetupPlatformViewChannels]; __block FlutterEngine* blockSelf = self; - [_flutterViewControllerWillDeallocObserver release]; - self.flutterViewControllerWillDeallocObserver = [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc object:viewController @@ -203,7 +201,6 @@ - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { } - (void)notifyViewControllerDeallocated { - [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"]; if (!_allowHeadlessExecution) { [self destroyContext]; } else { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m index 7f8f6902bde2a..3646e3fd5ee40 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m @@ -225,70 +225,4 @@ - (void)testVisibleFlutterViewControllerRespondsToApplicationLifecycle { [engine setViewController:nil]; } -- (void)testFlutterViewControllerDetachingSendsApplicationLifecycle { - XCTestExpectation* engineStartedExpectation = [self expectationWithDescription:@"Engine started"]; - - // Let the engine finish booting (at the end of which the channels are properly set-up) before - // moving onto the next step of showing the next view controller. - ScreenBeforeFlutter* rootVC = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:^void() { - [engineStartedExpectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:5 handler:nil]; - - UIApplication* application = UIApplication.sharedApplication; - application.delegate.window.rootViewController = rootVC; - FlutterEngine* engine = rootVC.engine; - - NSMutableArray* lifecycleExpectations = [NSMutableArray arrayWithCapacity:10]; - - // Expected sequence from showing the FlutterViewController is inactive and resumed. - [lifecycleExpectations addObjectsFromArray:@[ - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" - forStep:@"showing a FlutterViewController"], - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed" - forStep:@"showing a FlutterViewController"] - ]]; - // At the end of Flutter VC, we want to make sure it deallocs and sends detached signal. - // Using autoreleasepool will guarantee that. - FlutterViewController* flutterVC; - @autoreleasepool { - flutterVC = [rootVC showFlutter]; - [engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) { - if (lifecycleExpectations.count == 0) { - XCTFail(@"Unexpected lifecycle transition: %@", message); - return; - } - XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0]; - if (![[nextExpectation expectedLifecycle] isEqualToString:message]) { - XCTFail(@"Expected lifecycle %@ but instead received %@", - [nextExpectation expectedLifecycle], message); - return; - } - - [nextExpectation fulfill]; - [lifecycleExpectations removeObjectAtIndex:0]; - }]; - - [self waitForExpectations:lifecycleExpectations timeout:5]; - - // Starts dealloc flutter VC. - [lifecycleExpectations addObjectsFromArray:@[ - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" - forStep:@"detaching a FlutterViewController"], - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.paused" - forStep:@"detaching a FlutterViewController"], - [[XCAppLifecycleTestExpectation alloc] - initForLifecycle:@"AppLifecycleState.detached" - forStep:@"detaching a FlutterViewController"] - ]]; - [flutterVC dismissViewControllerAnimated:NO completion:nil]; - flutterVC = nil; - } - [self waitForExpectations:lifecycleExpectations timeout:5]; - - [engine.lifecycleChannel setMessageHandler:nil]; - [engine setViewController:nil]; -} - @end From 7b968ff95ff17ac798dedb46272ebb258738fbc6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 4 Nov 2019 17:28:51 -0800 Subject: [PATCH 007/591] Ensure that the CAMetalLayer FBO attachments can be read from. (#13643) By default, the CAMetalLayer backing store is a framebuffer attachment that is only optimized for display. However, effects such as backdrop filters require renderbuffer readback. Making this calls will result in exceptions. Disable the write only optimization on such backing store. Fixes https://github.com/flutter/flutter/issues/43555 --- shell/gpu/gpu_surface_metal.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index a5e2757ffa2a3..81abf740d48f6 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -23,6 +23,9 @@ } layer.get().pixelFormat = MTLPixelFormatBGRA8Unorm; + // Flutter needs to read from the color attachment in cases where there are effects such as + // backdrop filters. + layer.get().framebufferOnly = NO; auto metal_device = fml::scoped_nsprotocol>([layer_.get().device retain]); auto metal_queue = fml::scoped_nsprotocol>([metal_device newCommandQueue]); From 42299931bf604a5ed8f5790cfb039d35cf47448a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 4 Nov 2019 21:23:20 -0500 Subject: [PATCH 008/591] Roll src/third_party/skia 4cc2dc64ff13..4a4f34b76f3a (19 commits) (#13637) https://skia.googlesource.com/skia.git/+log/4cc2dc64ff13..4a4f34b76f3a git log 4cc2dc64ff13..4a4f34b76f3a --date=short --no-merges --format='%ad %ae %s' 2019-11-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-04 brianosman@google.com Fully embrace skcms types in SkColorSpace API 2019-11-04 michaelludwig@google.com Make SkBlendMode_AsCoeff() public 2019-11-04 bsalomon@google.com Some improvements to backend texture creation. 2019-11-04 jvanverth@google.com Fix memory leak in iOS viewer. 2019-11-04 robertphillips@google.com Use QuadHelper and PatternHelper in more places 2019-11-04 herb@google.com Fix fuzz bug in isValid for SkDescriptor 2019-11-04 fmalita@chromium.org [skottie] Add support for gradient opacity stops 2019-11-04 michaelludwig@google.com Return coverage from inset() call instead of using a separate function. 2019-11-04 mtklein@google.com updateUniforms() just once 2019-11-04 fmalita@chromium.org [skottie] Add a couple more 3D tests 2019-11-04 brianosman@google.com Update comment on isNumericalTransferFn to clarify that fn is always set 2019-11-04 michaelludwig@google.com Skip unnecessary moveTo in degenerate quad calculations 2019-11-04 borenet@google.com Reland "[recipes] Assert that Git is obtained from CIPD" 2019-11-04 penghuang@chromium.org Make GrVkResource::Trace thread safe. 2019-11-04 borenet@google.com [infra] Make SkottieWASM and LottieWeb use git from CIPD 2019-11-04 brianosman@google.com SkColorMatrix cleanup 2019-11-04 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 e96039218e9d..6c7208f93d6e (8 commits) 2019-11-04 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader fda994c63075..aaa64b76c0b4 (8 commits) Created with: gclient setdep -r src/third_party/skia@4a4f34b76f3a If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e41af26f2cbe6..71cc3acb6c53c 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4cc2dc64ff134fca1f26651b9b2c8882ec7f0837', + 'skia_revision': '4a4f34b76f3a3e085375c9c8649fbdb10a3be1e2', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 8de484254768c..f9ce9bb1da757 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6ca0730fa38db7898dad5c1db34bee73 +Signature: ac81e67751ecb0152b7e17a3969a4de4 UNUSED LICENSES: From 0928c1af2f45361a9473565d7946978dbc42493e Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Tue, 5 Nov 2019 12:27:34 -0800 Subject: [PATCH 009/591] Add 'Cough' test font and support multiple test fonts. (#13649) --- lib/ui/text/font_collection.cc | 19 +- runtime/test_font_data.cc | 178 ++++++++++++++++++- runtime/test_font_data.h | 4 +- third_party/txt/src/txt/test_font_manager.cc | 15 +- third_party/txt/src/txt/test_font_manager.h | 5 +- 5 files changed, 205 insertions(+), 16 deletions(-) diff --git a/lib/ui/text/font_collection.cc b/lib/ui/text/font_collection.cc index 4ef95709cad59..170f30c864f11 100644 --- a/lib/ui/text/font_collection.cc +++ b/lib/ui/text/font_collection.cc @@ -129,17 +129,24 @@ void FontCollection::RegisterFonts( } void FontCollection::RegisterTestFonts() { - sk_sp test_typeface = - SkTypeface::MakeFromStream(GetTestFontData()); + std::vector> test_typefaces; + std::vector> font_data = GetTestFontData(); + for (auto& font : font_data) { + test_typefaces.push_back(SkTypeface::MakeFromStream(std::move(font))); + } std::unique_ptr font_provider = std::make_unique(); - font_provider->RegisterTypeface(std::move(test_typeface), - GetTestFontFamilyName()); + size_t index = 0; + std::vector names = GetTestFontFamilyNames(); + for (sk_sp typeface : test_typefaces) { + font_provider->RegisterTypeface(std::move(typeface), names[index]); + index++; + } - collection_->SetTestFontManager(sk_make_sp( - std::move(font_provider), GetTestFontFamilyName())); + collection_->SetTestFontManager( + sk_make_sp(std::move(font_provider), names)); collection_->DisableFontFallback(); } diff --git a/runtime/test_font_data.cc b/runtime/test_font_data.cc index dd6e070159e65..302c8d05c2455 100644 --- a/runtime/test_font_data.cc +++ b/runtime/test_font_data.cc @@ -1205,28 +1205,198 @@ static const unsigned char kAhemFont[] = { 0x69, 0x69, 0x33, 0x30, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x02, 0x00, 0x10, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03}; +// "Cough" font is Flutter's custom test font and may expand to cover +// font-dependent testing specific features that "Ahem" cannot cover. +// +// Features and description of Cough: +// +// * EM square size of 1000. This is atypical of a power of 2 EM size, but +// is included to make it easier to tell the final value a metric is meant +// to be. For example, with fontSize 100, 10 units = 1 pixel. +// * The EM square has an ascent of 800 and a descent of 200. +// * The HHead, typo and win metrics are identical, making this font platform +// agnostic. +// * The ASCII glyphs of "A", "a", and "g" are included. This will likely expand +// in the future to include representative CJK glyphs as well as hanging +// glyphs. These glyphs are meant for testing purposes only, and only vaguely +// look like the character in order to minimize the size of the font. +// * Cap height is 800. +// * X height is 500. +// * Underline height is -100. +static const unsigned char kCoughFont[] = { + 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x80, 0x00, 0x03, 0x00, 0x50, + 0x46, 0x46, 0x54, 0x4d, 0x89, 0xba, 0x1a, 0x0b, 0x00, 0x00, 0x06, 0x10, + 0x00, 0x00, 0x00, 0x1c, 0x47, 0x44, 0x45, 0x46, 0x00, 0x27, 0x00, 0x2d, + 0x00, 0x00, 0x05, 0xe8, 0x00, 0x00, 0x00, 0x26, 0x4f, 0x53, 0x2f, 0x32, + 0x7d, 0x51, 0x4a, 0x11, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x00, 0x60, + 0x63, 0x6d, 0x61, 0x70, 0x01, 0x14, 0x07, 0x66, 0x00, 0x00, 0x01, 0xd0, + 0x00, 0x00, 0x01, 0x6a, 0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, + 0x00, 0x00, 0x05, 0xe0, 0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, + 0x53, 0x7d, 0x78, 0xff, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x01, 0x08, + 0x68, 0x65, 0x61, 0x64, 0x15, 0x1b, 0x17, 0x3d, 0x00, 0x00, 0x00, 0xdc, + 0x00, 0x00, 0x00, 0x36, 0x68, 0x68, 0x65, 0x61, 0x05, 0x84, 0x01, 0x3b, + 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, + 0x05, 0x51, 0xff, 0xfd, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x18, + 0x6c, 0x6f, 0x63, 0x61, 0x00, 0x8c, 0x00, 0xea, 0x00, 0x00, 0x03, 0x3c, + 0x00, 0x00, 0x00, 0x10, 0x6d, 0x61, 0x78, 0x70, 0x00, 0x0b, 0x00, 0x1d, + 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x3f, 0x88, 0x53, 0x13, 0x00, 0x00, 0x04, 0x54, 0x00, 0x00, 0x01, 0x59, + 0x70, 0x6f, 0x73, 0x74, 0xff, 0xed, 0x00, 0x71, 0x00, 0x00, 0x05, 0xb0, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x39, 0x28, 0xcc, 0xe3, 0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x03, 0xe8, + 0x00, 0x00, 0x00, 0x00, 0xd9, 0xe6, 0x56, 0x09, 0x00, 0x00, 0x00, 0x00, + 0xd9, 0xe6, 0x7e, 0x49, 0xff, 0xfe, 0xff, 0x37, 0x02, 0x31, 0x02, 0xcf, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x03, 0x52, 0xff, 0x06, 0x00, 0x00, 0x02, 0x30, + 0xff, 0xfe, 0xff, 0xff, 0x02, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x1c, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xe7, + 0x01, 0x90, 0x00, 0x05, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, + 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, + 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x69, 0x72, 0x64, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x67, 0x03, 0x52, 0xff, 0x06, 0x00, 0x64, 0x03, 0x52, + 0x00, 0xfa, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, + 0x03, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x02, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, + 0x02, 0x30, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x61, + 0x00, 0x67, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x20, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x67, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf5, + 0xff, 0xe3, 0xff, 0xc3, 0xff, 0xa4, 0xff, 0x9f, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x2e, + 0x00, 0x54, 0x00, 0x84, 0x00, 0x02, 0x00, 0x00, 0x00, 0x32, 0x01, 0x90, + 0x02, 0x9e, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x00, 0x37, 0x32, 0x33, 0x34, + 0x11, 0x22, 0x23, 0x14, 0x27, 0x32, 0x21, 0x14, 0x11, 0x22, 0x21, 0x34, + 0x32, 0x64, 0xc8, 0x64, 0xc8, 0x32, 0x85, 0x01, 0x0b, 0x85, 0xfe, 0xf5, + 0x64, 0xad, 0x01, 0x5b, 0xad, 0xdf, 0xcf, 0xfe, 0x63, 0xcf, 0x00, 0x00, + 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0x02, 0x30, 0x02, 0xcf, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x22, 0x25, 0x36, 0x13, 0x16, 0x02, 0x30, 0x8d, 0xfe, + 0x5b, 0x48, 0xd9, 0x44, 0x01, 0x01, 0xb4, 0x02, 0x1b, 0xb4, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x02, 0x30, 0x00, 0x0b, + 0x00, 0x15, 0x00, 0x00, 0x31, 0x30, 0x31, 0x32, 0x37, 0x36, 0x37, 0x16, + 0x13, 0x06, 0x27, 0x26, 0x01, 0x06, 0x07, 0x36, 0x37, 0x36, 0x23, 0x22, + 0x27, 0x26, 0x02, 0x6b, 0x6e, 0x48, 0x44, 0xca, 0x8c, 0xd2, 0xd3, 0x01, + 0x17, 0x18, 0x49, 0x34, 0x4e, 0x4e, 0x01, 0x02, 0x28, 0x29, 0xd0, 0xd4, + 0x8c, 0x8c, 0xfe, 0x5e, 0x01, 0x01, 0x01, 0x01, 0x1b, 0x2e, 0x88, 0x01, + 0x02, 0x02, 0x42, 0x43, 0x00, 0x03, 0xff, 0xff, 0xff, 0x37, 0x02, 0x30, + 0x02, 0x30, 0x00, 0x07, 0x00, 0x11, 0x00, 0x1b, 0x00, 0x00, 0x03, 0x32, + 0x21, 0x06, 0x11, 0x22, 0x05, 0x34, 0x13, 0x06, 0x03, 0x36, 0x25, 0x34, + 0x27, 0x34, 0x31, 0x06, 0x01, 0x14, 0x15, 0x16, 0x17, 0x30, 0x17, 0x34, + 0x35, 0x26, 0x01, 0x8c, 0x01, 0xa5, 0x01, 0x8c, 0xfe, 0x5d, 0x55, 0x01, + 0x03, 0x67, 0x01, 0x33, 0x01, 0x65, 0xfe, 0xcb, 0x85, 0x85, 0xa0, 0xe6, + 0x02, 0x30, 0xbe, 0xfd, 0xc6, 0x01, 0xbe, 0x01, 0xe4, 0x65, 0xfe, 0xd2, + 0x01, 0x01, 0x65, 0x98, 0x97, 0x01, 0xfd, 0xfb, 0x31, 0x31, 0x02, 0x01, + 0x01, 0x19, 0x4a, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xae, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x15, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x2e, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x48, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x63, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x84, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0xa2, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x01, 0x00, 0x10, 0x00, 0x03, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x1e, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x03, 0x00, 0x10, 0x00, 0x36, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x04, 0x00, 0x10, 0x00, 0x51, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x05, 0x00, 0x16, 0x00, 0x6c, 0x00, 0x03, + 0x00, 0x01, 0x04, 0x09, 0x00, 0x06, 0x00, 0x10, 0x00, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x54, 0x00, 0x79, 0x00, 0x70, 0x00, 0x65, 0x00, 0x66, 0x00, + 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x66, + 0x61, 0x63, 0x65, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, + 0x00, 0x6c, 0x00, 0x61, 0x00, 0x72, 0x00, 0x00, 0x52, 0x65, 0x67, 0x75, + 0x6c, 0x61, 0x72, 0x00, 0x00, 0x54, 0x00, 0x79, 0x00, 0x70, 0x00, 0x65, + 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x00, 0x54, 0x79, + 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x00, 0x00, 0x54, 0x00, 0x79, 0x00, + 0x70, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, + 0x00, 0x54, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x00, 0x00, 0x56, + 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, + 0x00, 0x20, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x54, + 0x00, 0x79, 0x00, 0x70, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, + 0x00, 0x65, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x9c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x24, + 0x00, 0x44, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x1e, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xd5, 0xed, 0x45, 0xb8, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xe6, 0x56, 0x09, + 0x00, 0x00, 0x00, 0x00, 0xd9, 0xe6, 0x7e, 0x49}; + static const unsigned int kAhemFontLength = 13884; +static const unsigned int kCoughFontLength = 1576; #else // EMBED_TEST_FONT_DATA static const unsigned char kAhemFont[] = {}; +static const unsigned char kCoughFont[] = {}; static const unsigned int kAhemFontLength = 0; +static const unsigned int kCoughFontLength = 0; #endif // EMBED_TEST_FONT_DATA namespace flutter { -std::unique_ptr GetTestFontData() { +std::vector> GetTestFontData() { + std::vector> data; if (kAhemFontLength == 0) { - return nullptr; + data.push_back(nullptr); + } else { + data.push_back(std::make_unique(kAhemFont, kAhemFontLength, + false /* copy data */)); + } + + if (kCoughFontLength == 0) { + data.push_back(nullptr); + } else { + data.push_back(std::make_unique( + kCoughFont, kCoughFontLength, false /* copy data */)); } - return std::make_unique(kAhemFont, kAhemFontLength, - false /* copy data */); + return data; } std::string GetTestFontFamilyName() { return "Ahem"; } +std::vector GetTestFontFamilyNames() { + std::vector names = {"Ahem", "Cough"}; + return names; +} + } // namespace flutter diff --git a/runtime/test_font_data.h b/runtime/test_font_data.h index 957422e0bd470..83ef523aa1dba 100644 --- a/runtime/test_font_data.h +++ b/runtime/test_font_data.h @@ -7,13 +7,15 @@ #include #include +#include #include "third_party/skia/include/core/SkStream.h" namespace flutter { -std::unique_ptr GetTestFontData(); +std::vector> GetTestFontData(); std::string GetTestFontFamilyName(); +std::vector GetTestFontFamilyNames(); } // namespace flutter diff --git a/third_party/txt/src/txt/test_font_manager.cc b/third_party/txt/src/txt/test_font_manager.cc index d5b1146fd4474..a23a8aa58807b 100644 --- a/third_party/txt/src/txt/test_font_manager.cc +++ b/third_party/txt/src/txt/test_font_manager.cc @@ -21,14 +21,23 @@ namespace txt { TestFontManager::TestFontManager( std::unique_ptr font_provider, - std::string test_font_family_name) + std::vector test_font_family_names) : AssetFontManager(std::move(font_provider)), - test_font_family_name_(test_font_family_name) {} + test_font_family_names_(test_font_family_names) {} TestFontManager::~TestFontManager() = default; SkFontStyleSet* TestFontManager::onMatchFamily(const char family_name[]) const { - return AssetFontManager::onMatchFamily(test_font_family_name_.c_str()); + // Find the requested name in the list, if not found, default to the first + // font family in the test font family list. + std::string requested_name(family_name); + std::string sanitized_name = test_font_family_names_[0]; + for (const std::string& test_family : test_font_family_names_) { + if (requested_name == test_family) { + sanitized_name = test_family; + } + } + return AssetFontManager::onMatchFamily(sanitized_name.c_str()); } } // namespace txt diff --git a/third_party/txt/src/txt/test_font_manager.h b/third_party/txt/src/txt/test_font_manager.h index 1c2fc8966edc0..b1a7d58b999cf 100644 --- a/third_party/txt/src/txt/test_font_manager.h +++ b/third_party/txt/src/txt/test_font_manager.h @@ -19,6 +19,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "third_party/skia/include/core/SkFontMgr.h" @@ -32,12 +33,12 @@ namespace txt { class TestFontManager : public AssetFontManager { public: TestFontManager(std::unique_ptr font_provider, - std::string test_font_family_name); + std::vector test_font_family_names); ~TestFontManager() override; private: - std::string test_font_family_name_; + std::vector test_font_family_names_; SkFontStyleSet* onMatchFamily(const char family_name[]) const override; From fcd8a2189387e1ff4e06f2cde8f276dc18b3e853 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 5 Nov 2019 09:44:10 -0800 Subject: [PATCH 010/591] Roll src/third_party/dart ab5cf0f854..8bdca37e98 (23 commits) dart-lang/sdk@8bdca37e98 [vm] Bump minimum kernel version to 29. dart-lang/sdk@75dc8483ab [kernel] Include promoted bound in TypeParameterType equality dart-lang/sdk@4ebc1e3fe7 Migrate dart:async to NNBD. dart-lang/sdk@d0e51573fc [infra] Update checked-in SDKs to 2.6.0 dart-lang/sdk@48ebcdac5f [ddk] Don't use Library.isExternal dart-lang/sdk@208f3709b9 [vm/benchmark] Add IsolateSpawnMemory.Dart2JSDeltaRss benchmarks. dart-lang/sdk@91c38d00c0 [dart2js] New RTI: Improve lowering of simple is-tests. dart-lang/sdk@54b324ca04 [dart2js] new-rti: Cache non-generic types on constructor dart-lang/sdk@6d28f59c54 Added promiseToFuture function to js_util.dart dart-lang/sdk@0804a6f260 Don't use deprecated analyzer elements in dartfuzz. dart-lang/sdk@79dc0cbc47 [vm/benchmarks] Add benchmark that decodes json on a separate isolate. dart-lang/sdk@7ba5584c2e [analysis_server] Fix outdated desin Service->Server, use Service->Logger. dart-lang/sdk@5279b6438c [vm/bytecode] Fix raw casts in DynamicCall in the interpreter dart-lang/sdk@0ea69b7342 [vm/benchmarks] Use service api to get heap usage for IsolateSpawn benchmark. dart-lang/sdk@8119253f64 Include space after comma in FunctionType.toString(). dart-lang/sdk@c38dafec6b Issue 39051. Implement isAbstract for ConstructorElement. dart-lang/sdk@abb1a17c89 Include FeatureSet into unlinked API signature. dart-lang/sdk@5bae0f23cf Increase completion model token lookback to 100 dart-lang/sdk@f1fd342f71 Initial sketch of a preview site dart-lang/sdk@48d44af154 Report error if inheriting from FutureOr. Fixes #33745 dart-lang/sdk@2a5ece5d89 Fix analyzer_plugin tutorial link dart-lang/sdk@9d5649175a Fix using deprecated analyzer elements in DDC. dart-lang/sdk@5a3c4002b8 Replace 'bottom' with 'Never' in subtyping tests. --- DEPS | 6 +++--- ci/licenses_golden/licenses_third_party | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 71cc3acb6c53c..07cff423c5497 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'ab5cf0f854f4cae1c0adad5f7a8aa158b1d646ca', + 'dart_revision': '8bdca37e98a59bdb7f52daea9da838b1a60c1cdf', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -192,7 +192,7 @@ deps = { # WARNING: Unused Dart dependencies in the list below till "WARNING:" marker are removed automatically - see create_updated_flutter_deps.py. 'src/third_party/dart/pkg/analysis_server/language_model': - {'packages': [{'version': '9fJQZ0TrnAGQKrEtuL3-AXbUfPzYxqpN_OBHr9P4hE4C', 'package': 'dart/language_model'}], 'dep_type': 'cipd'}, + {'packages': [{'version': 'lIRt14qoA1Cocb8j3yw_Fx5cfYou2ddam6ArBm4AI6QC', 'package': 'dart/language_model'}], 'dep_type': 'cipd'}, 'src/third_party/dart/third_party/observatory_pub_packages': Var('dart_git') + '/observatory_pub_packages.git' + '@' + Var('dart_observatory_pub_packages_rev'), @@ -381,7 +381,7 @@ deps = { Var('dart_git') + '/package_resolver.git' + '@' + Var('dart_package_resolver_tag'), 'src/third_party/dart/tools/sdks': - {'packages': [{'version': 'version:2.6.0-dev.4.0', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, + {'packages': [{'version': 'version:2.6.0', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, # WARNING: end of dart dependencies list that is cleaned up automatically - see create_updated_flutter_deps.py. diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 2b5938972d195..79966cccb864c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 8114cc2f074215c96eafc221089b981b +Signature: 62624acd0f795337602c13cd2e2e2fdf UNUSED LICENSES: @@ -7457,7 +7457,9 @@ FILE: ../../../third_party/dart/.mailmap FILE: ../../../third_party/dart/.style.yapf FILE: ../../../third_party/dart/.vpython FILE: ../../../third_party/dart/PATENT_GRANT +FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/sample.json FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/helloworld.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/helloworld.dart FILE: ../../../third_party/dart/client/idea/.idea/.name FILE: ../../../third_party/dart/client/idea/.idea/inspectionProfiles/Project_Default.xml FILE: ../../../third_party/dart/client/idea/.idea/vcs.xml @@ -7542,7 +7544,9 @@ FILE: ../../../third_party/dart/benchmarks/FfiCall/dart/native/native_functions. FILE: ../../../third_party/dart/benchmarks/FfiMemory/dart/FfiMemory.dart FILE: ../../../third_party/dart/benchmarks/FfiStruct/dart/FfiStruct.dart FILE: ../../../third_party/dart/benchmarks/Isolate/dart/Isolate.dart +FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/IsolateJson.dart FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart FILE: ../../../third_party/dart/runtime/bin/abi_version.h FILE: ../../../third_party/dart/runtime/bin/abi_version_in.cc FILE: ../../../third_party/dart/runtime/bin/elf_loader.cc From 5b87cd0773271527119bf29fe7aa4385de25afc6 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Tue, 5 Nov 2019 13:31:40 -0800 Subject: [PATCH 011/591] Fix bug where Enter doesn't add new line in multi-line fields (#13630) --- .../src/engine/text_editing/input_type.dart | 6 ++ .../src/engine/text_editing/text_editing.dart | 3 +- lib/web_ui/test/text_editing_test.dart | 56 ++++++++++++++++++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text_editing/input_type.dart b/lib/web_ui/lib/src/engine/text_editing/input_type.dart index 7df6b2cbb4151..47c0ff7a09927 100644 --- a/lib/web_ui/lib/src/engine/text_editing/input_type.dart +++ b/lib/web_ui/lib/src/engine/text_editing/input_type.dart @@ -59,6 +59,9 @@ abstract class EngineInputType { /// . String get inputmodeAttribute; + /// Whether this input type allows the "Enter" key to submit the input action. + bool get submitActionOnEnter => true; + /// Create the appropriate DOM element for this input type. html.HtmlElement createDomElement() => html.InputElement(); @@ -124,6 +127,9 @@ class MultilineInputType extends EngineInputType { @override final String inputmodeAttribute = null; + @override + bool get submitActionOnEnter => false; + @override html.HtmlElement createDomElement() => html.TextAreaElement(); } diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 17fb8c3eda4d3..a3c7ded6fb074 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -437,7 +437,8 @@ class TextEditingElement { } void _maybeSendAction(html.KeyboardEvent event) { - if (event.keyCode == _kReturnKeyCode) { + if (_inputConfiguration.inputType.submitActionOnEnter && + event.keyCode == _kReturnKeyCode) { event.preventDefault(); _onAction(_inputConfiguration.inputAction); } diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index cfa7ac0eba142..902d4dd09116d 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -13,6 +13,9 @@ import 'package:test/test.dart'; import 'matchers.dart'; +/// The `keyCode` of the "Enter" key. +const int _kReturnKeyCode = 13; + const MethodCodec codec = JSONMethodCodec(); TextEditingElement editingElement; @@ -250,10 +253,41 @@ void main() { // No input action so far. expect(lastInputAction, isNull); - dispatchKeyboardEvent(editingElement.domElement, 'keydown', keyCode: 13); + dispatchKeyboardEvent( + editingElement.domElement, + 'keydown', + keyCode: _kReturnKeyCode, + ); expect(lastInputAction, 'TextInputAction.done'); }); + test('Does not trigger input action in multi-line mode', () { + final InputConfiguration config = InputConfiguration( + inputType: EngineInputType.multiline, + obscureText: false, + inputAction: 'TextInputAction.done', + ); + editingElement.enable( + config, + onChange: trackEditingState, + onAction: trackInputAction, + ); + + // No input action so far. + expect(lastInputAction, isNull); + + final KeyboardEvent event = dispatchKeyboardEvent( + editingElement.domElement, + 'keydown', + keyCode: _kReturnKeyCode, + ); + + // Still no input action. + expect(lastInputAction, isNull); + // And default behavior of keyboard event shouldn't have been prevented. + expect(event.defaultPrevented, isFalse); + }); + group('[persistent mode]', () { test('Does not accept dom elements of a wrong type', () { // A regular shouldn't be accepted. @@ -883,7 +917,7 @@ void main() { dispatchKeyboardEvent( textEditing.editingElement.domElement, 'keydown', - keyCode: 13, + keyCode: _kReturnKeyCode, ); expect(spy.messages, hasLength(1)); @@ -894,6 +928,24 @@ void main() { [clientId, 'TextInputAction.next'], ); }); + + test('does not send input action in multi-line mode', () { + showKeyboard( + inputType: 'multiline', + inputAction: 'TextInputAction.next', + ); + + final KeyboardEvent event = dispatchKeyboardEvent( + textEditing.editingElement.domElement, + 'keydown', + keyCode: _kReturnKeyCode, + ); + + // No input action and no platform message have been sent. + expect(spy.messages, isEmpty); + // And default behavior of keyboard event shouldn't have been prevented. + expect(event.defaultPrevented, isFalse); + }); }); group('EditingState', () { From f6e24a00ccfa4da86d29fe3580e3607ebadc50e5 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Tue, 5 Nov 2019 13:31:51 -0800 Subject: [PATCH 012/591] [web] Ignore changes in *.ttf files in felt build watch mode (#13634) --- lib/web_ui/dev/build.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/web_ui/dev/build.dart b/lib/web_ui/dev/build.dart index 541710d74d191..86c462e77ec7f 100644 --- a/lib/web_ui/dev/build.dart +++ b/lib/web_ui/dev/build.dart @@ -60,6 +60,8 @@ class BuildCommand extends Command { PipelineWatcher( dir: libPath.absolute, pipeline: buildPipeline, + // Ignore font files that are copied whenever tests run. + ignore: (event) => event.path.endsWith('.ttf'), ).start(); // Return a never-ending future. return Completer().future; @@ -143,10 +145,13 @@ class Pipeline { } } +typedef WatchEventPredicate = bool Function(WatchEvent event); + class PipelineWatcher { PipelineWatcher({ @required this.dir, @required this.pipeline, + this.ignore, }) : watcher = DirectoryWatcher(dir); /// The path of the directory to watch for changes. @@ -158,6 +163,10 @@ class PipelineWatcher { /// Used to watch a directory for any file system changes. final DirectoryWatcher watcher; + /// A callback that determines whether to rerun the pipeline or not for a + /// given [WatchEvent] instance. + final WatchEventPredicate ignore; + void start() { watcher.events.listen(_onEvent); } @@ -166,6 +175,10 @@ class PipelineWatcher { Timer _scheduledPipeline; void _onEvent(WatchEvent event) { + if (ignore != null && ignore(event)) { + return; + } + final String relativePath = path.relative(event.path, from: dir); print('- [${event.type}] ${relativePath}'); From faa0c164dbd9ae4ce9b6023e24b9330f170c135b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 5 Nov 2019 16:33:35 -0500 Subject: [PATCH 013/591] Roll fuchsia/sdk/core/mac-amd64 from XcaoM... to bCFtP... (#13665) Roll fuchsia/sdk/core/mac-amd64 from XcaoM... to bCFtP... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 07cff423c5497..d4ea907de76d8 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'XcaoMSXfWFsCkt_giGaUOk3isEOMGjIv6LWtd8FPgjgC' + 'version': 'bCFtPAMdFsXQFQeOaZ7EdGSMTVN-tZ7RVs5bT3BueMUC' } ], 'condition': 'host_os == "mac"', From 89e0142165f46e412088986ab978c3bb4cb29e55 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 5 Nov 2019 16:35:17 -0500 Subject: [PATCH 014/591] Roll fuchsia/clang/mac-amd64 from 6tttO... to OzTZO... (#13690) Roll fuchsia/clang/mac-amd64 from 6tttO... to OzTZO... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-toolchain-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d4ea907de76d8..229e2b77c15f5 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/clang/mac-amd64', - 'version': '6tttOTlynvfnjkMSTokwfI4sj7gfuyeF02Y6F7zwBKkC' + 'version': 'OzTZOKkICT0yD82Dbx0jvVn5hN5eOSi6ByVTDseE7i0C' } ], 'condition': 'host_os == "mac"', From f22a4073f4f736855fdd2a9cd534437dbfda1269 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 5 Nov 2019 16:37:35 -0500 Subject: [PATCH 015/591] Roll fuchsia/clang/linux-amd64 from WxGHg... to OT6p3... (#13694) Roll fuchsia/clang/linux-amd64 from WxGHg... to OT6p3... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-toolchain-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 229e2b77c15f5..a739f426d2122 100644 --- a/DEPS +++ b/DEPS @@ -572,7 +572,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/clang/linux-amd64', - 'version': 'WxGHgTOyvYvcynHb6cBxdHIwSdivQJecIrb5NRHvxfwC' + 'version': 'OT6p30bQQhyCzRSy7xPsSbZ88J3PWOnneenkMZ0j7kIC' } ], 'condition': 'host_os == "linux"', From af3a69c955bc98544cc0037b0630ceae976ec54a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 5 Nov 2019 16:41:50 -0500 Subject: [PATCH 016/591] Roll src/third_party/skia 4a4f34b76f3a..f3d4109a793b (1 commits) (#13659) https://skia.googlesource.com/skia.git/+log/4a4f34b76f3a..f3d4109a793b git log 4a4f34b76f3a..f3d4109a793b --date=short --no-merges --format='%ad %ae %s' 2019-11-05 mtklein@google.com add (x,y) params to shader program() Created with: gclient setdep -r src/third_party/skia@f3d4109a793b If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a739f426d2122..b09383ae22a74 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4a4f34b76f3a3e085375c9c8649fbdb10a3be1e2', + 'skia_revision': 'f3d4109a793b2adafa1ae330ba092d8a7b38fbf6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f9ce9bb1da757..5e4c3b15c71e0 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ac81e67751ecb0152b7e17a3969a4de4 +Signature: 25dad30fe021ac972ef0a9e2e3b7ab6b UNUSED LICENSES: From 76312eefdd8d5e34ea2a7fa42919e1b5347bcabb Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 5 Nov 2019 13:45:57 -0800 Subject: [PATCH 017/591] Fix Class.forName unchecked call warning (#13695) --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index f91214974add8..d92b395079335 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -248,7 +248,7 @@ private boolean isAttachedToJni() { */ private void registerPlugins() { try { - Class generatedPluginRegistrant = Class.forName("io.plugins.GeneratedPluginRegistrant"); + Class generatedPluginRegistrant = Class.forName("io.plugins.GeneratedPluginRegistrant"); Method registrationMethod = generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class); registrationMethod.invoke(null, this); } catch (Exception e) { From 1bfb928e071674a21779cee94908fbcae1c2e657 Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Tue, 5 Nov 2019 14:52:16 -0800 Subject: [PATCH 018/591] Issues/39832 reland (#13642) * Reland "Added new lifecycle enum (#11913)" --- lib/ui/window.dart | 16 ++--- lib/web_ui/lib/src/ui/window.dart | 13 ++-- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- .../FlutterActivityAndFragmentDelegate.java | 2 + .../systemchannels/LifecycleChannel.java | 4 ++ ...lutterActivityAndFragmentDelegateTest.java | 11 ++++ .../ios/framework/Source/FlutterEngine.mm | 24 ++++--- .../ScenariosTests/AppLifecycleTests.m | 66 +++++++++++++++++++ 9 files changed, 111 insertions(+), 29 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 3bd28a248bbba..954640ab79167 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -171,18 +171,16 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - /// - /// Android apps in this state should assume that they may enter the - /// [suspending] state at any time. paused, - /// The application will be suspended momentarily. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// The application is still hosted on a flutter engine but is detached from + /// any host views. /// - /// On iOS, this state is currently unused. - suspending, + /// When the application is in this state, the engine is running without + /// a view. It can either be in the progress of attaching a view when engine + /// was first initializes, or after the view being destroyed due to a Navigator + /// pop. + detached, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 58c82410de7c6..731874e6dff97 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -73,18 +73,13 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - /// - /// Android apps in this state should assume that they may enter the - /// [suspending] state at any time. paused, - /// The application will be suspended momentarily. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// The application is detached from view. /// - /// On iOS, this state is currently unused. - suspending, + /// When the application is in this state, the engine is running without + /// a platform UI. + detached, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index c0ee45d762e81..5ade4672cbece 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -120,7 +120,7 @@ class RuntimeController final : public WindowClient { std::string variant_code; std::vector locale_data; std::string user_settings_data = "{}"; - std::string lifecycle_state; + std::string lifecycle_state = "AppLifecycleState.detached"; bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 38b034d52d0d0..77b6577271511 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -311,7 +311,7 @@ bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { const auto& data = message->data(); std::string state(reinterpret_cast(data.data()), data.size()); if (state == "AppLifecycleState.paused" || - state == "AppLifecycleState.suspending") { + state == "AppLifecycleState.detached") { activity_running_ = false; StopAnimator(); } else if (state == "AppLifecycleState.resumed" || diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index eddacf2b25531..6cebfcbef6232 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -461,6 +461,8 @@ void onDetach() { platformPlugin = null; } + flutterEngine.getLifecycleChannel().appIsDetached(); + // Destroy our FlutterEngine if we're not set to retain it. if (host.shouldDestroyEngineWithHost()) { flutterEngine.destroy(); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java index abc6323907d0b..cd244ff8251e1 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java @@ -39,4 +39,8 @@ public void appIsPaused() { channel.send("AppLifecycleState.paused"); } + public void appIsDetached() { + Log.v(TAG, "Sending AppLifecycleState.detached message."); + channel.send("AppLifecycleState.detached"); + } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 154c59f173191..d335fa5064dbc 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -85,18 +85,21 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter. delegate.onResume(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is paused, an inactive message should have been sent to Flutter. delegate.onPause(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is stopped, a paused message should have been sent to Flutter. // Notice that Flutter uses the term "paused" in a different way, and at a different time @@ -105,6 +108,14 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); + + // When activity detaches, a detached message should have been sent to Flutter. + delegate.onDetach(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsDetached(); } @Test diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 83551d2aed042..a9f20708f15b5 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -175,18 +175,23 @@ - (void)ensureSemanticsEnabled { - (void)setViewController:(FlutterViewController*)viewController { FML_DCHECK(self.iosPlatformView); - _viewController = [viewController getWeakPtr]; + _viewController = + viewController ? [viewController getWeakPtr] : fml::WeakPtr(); self.iosPlatformView->SetOwnerViewController(_viewController); [self maybeSetupPlatformViewChannels]; - __block FlutterEngine* blockSelf = self; - self.flutterViewControllerWillDeallocObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc - object:viewController - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification* note) { - [blockSelf notifyViewControllerDeallocated]; - }]; + if (viewController) { + __block FlutterEngine* blockSelf = self; + self.flutterViewControllerWillDeallocObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc + object:viewController + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* note) { + [blockSelf notifyViewControllerDeallocated]; + }]; + } else { + self.flutterViewControllerWillDeallocObserver = nil; + } } - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { @@ -201,6 +206,7 @@ - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { } - (void)notifyViewControllerDeallocated { + [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"]; if (!_allowHeadlessExecution) { [self destroyContext]; } else { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m index 3646e3fd5ee40..7f8f6902bde2a 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m @@ -225,4 +225,70 @@ - (void)testVisibleFlutterViewControllerRespondsToApplicationLifecycle { [engine setViewController:nil]; } +- (void)testFlutterViewControllerDetachingSendsApplicationLifecycle { + XCTestExpectation* engineStartedExpectation = [self expectationWithDescription:@"Engine started"]; + + // Let the engine finish booting (at the end of which the channels are properly set-up) before + // moving onto the next step of showing the next view controller. + ScreenBeforeFlutter* rootVC = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:^void() { + [engineStartedExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:nil]; + + UIApplication* application = UIApplication.sharedApplication; + application.delegate.window.rootViewController = rootVC; + FlutterEngine* engine = rootVC.engine; + + NSMutableArray* lifecycleExpectations = [NSMutableArray arrayWithCapacity:10]; + + // Expected sequence from showing the FlutterViewController is inactive and resumed. + [lifecycleExpectations addObjectsFromArray:@[ + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" + forStep:@"showing a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed" + forStep:@"showing a FlutterViewController"] + ]]; + // At the end of Flutter VC, we want to make sure it deallocs and sends detached signal. + // Using autoreleasepool will guarantee that. + FlutterViewController* flutterVC; + @autoreleasepool { + flutterVC = [rootVC showFlutter]; + [engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) { + if (lifecycleExpectations.count == 0) { + XCTFail(@"Unexpected lifecycle transition: %@", message); + return; + } + XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0]; + if (![[nextExpectation expectedLifecycle] isEqualToString:message]) { + XCTFail(@"Expected lifecycle %@ but instead received %@", + [nextExpectation expectedLifecycle], message); + return; + } + + [nextExpectation fulfill]; + [lifecycleExpectations removeObjectAtIndex:0]; + }]; + + [self waitForExpectations:lifecycleExpectations timeout:5]; + + // Starts dealloc flutter VC. + [lifecycleExpectations addObjectsFromArray:@[ + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" + forStep:@"detaching a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.paused" + forStep:@"detaching a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] + initForLifecycle:@"AppLifecycleState.detached" + forStep:@"detaching a FlutterViewController"] + ]]; + [flutterVC dismissViewControllerAnimated:NO completion:nil]; + flutterVC = nil; + } + [self waitForExpectations:lifecycleExpectations timeout:5]; + + [engine.lifecycleChannel setMessageHandler:nil]; + [engine setViewController:nil]; +} + @end From 05ab04dbe8cf8ad0e27821abb785ee2655d9af0b Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Tue, 5 Nov 2019 15:25:18 -0800 Subject: [PATCH 019/591] Fixed the scroll direction for iOS horizontal accessibility scroll events. (#13651) * Fixed the scroll direction for horizonatal accessibilty scroll events. * Updated the comment describing the scroll direction mapping. --- .../ios/framework/Source/accessibility_bridge.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 36f320d93856e..557e517729066 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -20,18 +20,20 @@ flutter::SemanticsAction GetSemanticsActionForScrollDirection( UIAccessibilityScrollDirection direction) { - // To describe scroll direction, UIAccessibilityScrollDirection uses the direction the scroll bar - // moves in and SemanticsAction uses the direction the finger moves in. Both move in opposite - // directions, which is why the following maps left to right and vice versa. + // To describe the vertical scroll direction, UIAccessibilityScrollDirection uses the + // direction the scroll bar moves in and SemanticsAction uses the direction the finger + // moves in. However, the horizontal scroll direction matches the SemanticsAction direction. + // That is way the following maps vertical opposite of the SemanticsAction, but the horizontal + // maps directly. switch (direction) { case UIAccessibilityScrollDirectionRight: case UIAccessibilityScrollDirectionPrevious: // TODO(abarth): Support RTL using // _node.textDirection. - return flutter::SemanticsAction::kScrollLeft; + return flutter::SemanticsAction::kScrollRight; case UIAccessibilityScrollDirectionLeft: case UIAccessibilityScrollDirectionNext: // TODO(abarth): Support RTL using // _node.textDirection. - return flutter::SemanticsAction::kScrollRight; + return flutter::SemanticsAction::kScrollLeft; case UIAccessibilityScrollDirectionUp: return flutter::SemanticsAction::kScrollDown; case UIAccessibilityScrollDirectionDown: From 3ea400584414db5a26dbfd0bdaefe56afdb51a4f Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Tue, 5 Nov 2019 15:27:25 -0800 Subject: [PATCH 020/591] Added Semantic header support on Android. (#13262) Added Semantic header support on Android. --- .../android/io/flutter/view/AccessibilityBridge.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 5093bedfa1f2b..95f2a1d88565b 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -4,6 +4,7 @@ package io.flutter.view; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ContentResolver; import android.database.ContentObserver; @@ -489,6 +490,8 @@ private boolean shouldSetCollectionInfo(final SemanticsNode semanticsNode) { */ @Override @SuppressWarnings("deprecation") + // Supressing Lint warning for new API, as we are version guarding all calls to newer APIs + @SuppressLint("NewApi") public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { if (virtualViewId >= MIN_ENGINE_GENERATED_NODE_ID) { // The node is in the engine generated range, and is provided by the accessibility view embedder. @@ -753,6 +756,11 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { result.setSelected(semanticsNode.hasFlag(Flag.IS_SELECTED)); + // Heading support + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + result.setHeading(semanticsNode.hasFlag(Flag.IS_HEADER)); + } + // Accessibility Focus if (accessibilityFocusedSemanticsNode != null && accessibilityFocusedSemanticsNode.id == virtualViewId) { result.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); From 58a6d6554f16cfe54d1ecc97acb9befedc520185 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Tue, 5 Nov 2019 23:36:35 +0000 Subject: [PATCH 021/591] Fix plugin registrant reflection path. (#44161) (#13698) --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index d92b395079335..f8580519d5603 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -248,7 +248,7 @@ private boolean isAttachedToJni() { */ private void registerPlugins() { try { - Class generatedPluginRegistrant = Class.forName("io.plugins.GeneratedPluginRegistrant"); + Class generatedPluginRegistrant = Class.forName("io.flutter.plugins.GeneratedPluginRegistrant"); Method registrationMethod = generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class); registrationMethod.invoke(null, this); } catch (Exception e) { From 6c763bb551cbc06da59b6a55b4c5ee0eccb6575f Mon Sep 17 00:00:00 2001 From: Filip Filmar Date: Tue, 5 Nov 2019 18:51:37 -0800 Subject: [PATCH 022/591] [fuchsia] Temporarily disable intl provider (#13696) The intention of the property provider is to try to connect, not bail out if connection is not possible. This code tried to connect unconditionally, which is not what we wanted. Removing this initialization code to enable a roll, and will fix in a followup. --- shell/platform/fuchsia/flutter/engine.cc | 50 ------------------- .../flutter/meta/flutter_aot_runner.cmx | 1 - .../meta/flutter_jit_product_runner.cmx | 1 - .../flutter/meta/flutter_jit_runner.cmx | 1 - .../flutter/meta/flutter_runner_tests.cmx | 1 - 5 files changed, 54 deletions(-) diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 340468d66c30c..6984a6e035659 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -41,13 +41,6 @@ static void UpdateNativeThreadLabelNames(const std::string& label, set_thread_name(runners.GetIOTaskRunner(), label, ".io"); } -static fml::RefPtr MakeLocalizationPlatformMessage( - const fuchsia::intl::Profile& intl_profile) { - return fml::MakeRefCounted( - "flutter/localization", MakeLocalizationPlatformMessageData(intl_profile), - nullptr); -} - Engine::Engine(Delegate& delegate, std::string thread_label, std::shared_ptr svc, @@ -263,49 +256,6 @@ Engine::Engine(Delegate& delegate, // notification. Fire one eagerly. shell_->GetPlatformView()->NotifyCreated(); - // Connect to the intl property provider. - { - intl_property_provider_.set_error_handler([](zx_status_t status) { - FML_LOG(ERROR) << "Failed to connect to " - << fuchsia::intl::PropertyProvider::Name_ << ": " - << zx_status_get_string(status); - }); - - // Note that we're using the runner's services, not the component's. - // Flutter locales should be updated regardless of whether the component has - // direct access to the fuchsia.intl.PropertyProvider service. - ZX_ASSERT(runner_services->Connect(intl_property_provider_.NewRequest()) == - ZX_OK); - - auto get_profile_callback = [flutter_runner_engine = - weak_factory_.GetWeakPtr()]( - const fuchsia::intl::Profile& profile) { - if (!flutter_runner_engine) { - return; - } - if (!profile.has_locales()) { - FML_LOG(WARNING) << "Got intl Profile without locales"; - } - auto message = MakeLocalizationPlatformMessage(profile); - FML_VLOG(-1) << "Sending LocalizationPlatformMessage"; - flutter_runner_engine->shell_->GetPlatformView()->DispatchPlatformMessage( - message); - }; - - FML_VLOG(-1) << "Requesting intl Profile"; - - // Make the initial request - intl_property_provider_->GetProfile(get_profile_callback); - - // And register for changes - intl_property_provider_.events().OnChange = [this, runner_services, - get_profile_callback]() { - FML_VLOG(-1) << fuchsia::intl::PropertyProvider::Name_ << ": OnChange"; - runner_services->Connect(intl_property_provider_.NewRequest()); - intl_property_provider_->GetProfile(get_profile_callback); - }; - } - // Launch the engine in the appropriate configuration. auto run_configuration = flutter::RunConfiguration::InferFromSettings( settings_, task_runners.GetIOTaskRunner()); diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index bdfec3cd2e4d0..358574ef5872a 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -15,7 +15,6 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", - "fuchsia.intl.PropertyProvider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx index 912b534df93a2..cd8c9e09ae1e7 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx @@ -15,7 +15,6 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", - "fuchsia.intl.PropertyProvider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx index 912b534df93a2..cd8c9e09ae1e7 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -15,7 +15,6 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", - "fuchsia.intl.PropertyProvider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx index 015acc94b4e47..fedcb77867acb 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx @@ -9,7 +9,6 @@ ], "services": [ "fuchsia.accessibility.semantics.SemanticsManager", - "fuchsia.intl.PropertyProvider", "fuchsia.sys.Launcher" ] } From a9ef2410f63fd2fe11255b668a62245bb0743430 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Tue, 5 Nov 2019 19:00:51 -0800 Subject: [PATCH 023/591] [web] Don't send keyboard events from text fields to flutter (#13699) --- lib/web_ui/dev/chrome.dart | 1 + lib/web_ui/lib/src/engine/keyboard.dart | 35 ++++++++ .../src/engine/text_editing/text_editing.dart | 10 +++ lib/web_ui/test/keyboard_test.dart | 86 ++++++++++++++++++- 4 files changed, 131 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/dev/chrome.dart b/lib/web_ui/dev/chrome.dart index f2d1e3a6e9b63..6a560856b0e09 100644 --- a/lib/web_ui/dev/chrome.dart +++ b/lib/web_ui/dev/chrome.dart @@ -60,6 +60,7 @@ class Chrome extends Browser { '--window-size=$kMaxScreenshotWidth,$kMaxScreenshotHeight', // When headless, this is the actual size of the viewport '--disable-extensions', '--disable-popup-blocking', + // Indicates that the browser is in "browse without sign-in" (Guest session) mode. '--bwsi', '--no-first-run', '--no-default-browser-check', diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 86e04de57681f..9b3314ed8f64b 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -4,6 +4,23 @@ part of engine; +/// Contains a whitelist of keys that must be sent to Flutter under all +/// circumstances. +/// +/// When keys are pressed in a text field, we generally don't want to send them +/// to Flutter. This list of keys is the exception to that rule. Keys in this +/// list will be sent to Flutter even if pressed in a text field. +/// +/// A good example is the "Tab" and "Shift" keys which are used by the framework +/// to move focus between text fields. +const List _alwaysSentKeys = [ + 'Alt', + 'Control', + 'Meta', + 'Shift', + 'Tab', +]; + /// Provides keyboard bindings, such as the `flutter/keyevent` channel. class Keyboard { /// Initializes the [Keyboard] singleton. @@ -50,6 +67,10 @@ class Keyboard { static const JSONMessageCodec _messageCodec = JSONMessageCodec(); void _handleHtmlEvent(html.KeyboardEvent event) { + if (_shouldIgnoreEvent(event)) { + return; + } + if (_shouldPreventDefault(event)) { event.preventDefault(); } @@ -66,6 +87,20 @@ class Keyboard { _messageCodec.encodeMessage(eventData), _noopCallback); } + /// Whether the [Keyboard] class should ignore the given [html.KeyboardEvent]. + /// + /// When this method returns true, it prevents the keyboard event from being + /// sent to Flutter. + bool _shouldIgnoreEvent(html.KeyboardEvent event) { + // Keys in the [_alwaysSentKeys] list should never be ignored. + if (_alwaysSentKeys.contains(event.key)) { + return false; + } + // Other keys should be ignored if triggered on a text field. + return event.target is html.Element && + HybridTextEditing.isEditingElement(event.target); + } + bool _shouldPreventDefault(html.KeyboardEvent event) { switch (event.key) { case 'Tab': diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index a3c7ded6fb074..fe4df00071b65 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -17,6 +17,8 @@ void _emptyCallback(dynamic _) {} /// /// They are assigned once during the creation of the DOM element. void _setStaticStyleAttributes(html.HtmlElement domElement) { + domElement.classes.add(HybridTextEditing.textEditingClass); + final html.CssStyleDeclaration elementStyle = domElement.style; elementStyle ..whiteSpace = 'pre-wrap' @@ -534,6 +536,14 @@ class HybridTextEditing { return _defaultEditingElement; } + /// A CSS class name used to identify all elements used for text editing. + @visibleForTesting + static const String textEditingClass = 'flt-text-editing'; + + static bool isEditingElement(html.Element element) { + return element.classes.contains(textEditingClass); + } + /// Requests that [customEditingElement] is used for managing text editing state /// instead of the hidden default element. /// diff --git a/lib/web_ui/test/keyboard_test.dart b/lib/web_ui/test/keyboard_test.dart index a0054d26e6609..061accb96e492 100644 --- a/lib/web_ui/test/keyboard_test.dart +++ b/lib/web_ui/test/keyboard_test.dart @@ -13,6 +13,17 @@ import 'package:test/test.dart'; void main() { group('Keyboard', () { + /// Used to save and restore [ui.window.onPlatformMessage] after each test. + ui.PlatformMessageCallback savedCallback; + + setUp(() { + savedCallback = ui.window.onPlatformMessage; + }); + + tearDown(() { + ui.window.onPlatformMessage = savedCallback; + }); + test('initializes and disposes', () { expect(Keyboard.instance, isNull); Keyboard.initialize(); @@ -204,6 +215,12 @@ void main() { test('prevents default when "Tab" is pressed', () { Keyboard.initialize(); + int count = 0; + ui.window.onPlatformMessage = (String channel, ByteData data, + ui.PlatformMessageResponseCallback callback) { + count += 1; + }; + final html.KeyboardEvent event = dispatchKeyboardEvent( 'keydown', key: 'Tab', @@ -211,14 +228,78 @@ void main() { ); expect(event.defaultPrevented, isTrue); + expect(count, 1); + + Keyboard.instance.dispose(); + }); + + test('ignores keyboard events triggered on text fields', () { + Keyboard.initialize(); + + int count = 0; + ui.window.onPlatformMessage = (String channel, ByteData data, + ui.PlatformMessageResponseCallback callback) { + count += 1; + }; + + useTextEditingElement((html.Element element) { + final html.KeyboardEvent event = dispatchKeyboardEvent( + 'keydown', + key: 'SomeKey', + code: 'SomeCode', + target: element, + ); + + expect(event.defaultPrevented, isFalse); + expect(count, 0); + }); + + Keyboard.instance.dispose(); + }); + + test('the "Tab" key should never be ignored', () { + Keyboard.initialize(); + + int count = 0; + ui.window.onPlatformMessage = (String channel, ByteData data, + ui.PlatformMessageResponseCallback callback) { + count += 1; + }; + + useTextEditingElement((html.Element element) { + final html.KeyboardEvent event = dispatchKeyboardEvent( + 'keydown', + key: 'Tab', + code: 'Tab', + target: element, + ); + + expect(event.defaultPrevented, isTrue); + expect(count, 1); + }); Keyboard.instance.dispose(); }); }); } +typedef ElementCallback = void Function(html.Element element); + +void useTextEditingElement(ElementCallback callback) { + final html.InputElement input = html.InputElement(); + input.classes.add(HybridTextEditing.textEditingClass); + + try { + html.document.body.append(input); + callback(input); + } finally { + input.remove(); + } +} + html.KeyboardEvent dispatchKeyboardEvent( String type, { + html.EventTarget target, String key, String code, bool repeat = false, @@ -227,6 +308,8 @@ html.KeyboardEvent dispatchKeyboardEvent( bool isControlPressed = false, bool isMetaPressed = false, }) { + target ??= html.window; + final Function jsKeyboardEvent = js_util.getProperty(html.window, 'KeyboardEvent'); final List eventArgs = [ @@ -239,12 +322,13 @@ html.KeyboardEvent dispatchKeyboardEvent( 'altKey': isAltPressed, 'ctrlKey': isControlPressed, 'metaKey': isMetaPressed, + 'bubbles': true, 'cancelable': true, } ]; final html.KeyboardEvent event = js_util.callConstructor(jsKeyboardEvent, js_util.jsify(eventArgs)); - html.window.dispatchEvent(event); + target.dispatchEvent(event); return event; } From 572e3d16c04238749dce95df3fd28713cea99b55 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 5 Nov 2019 19:17:52 -0800 Subject: [PATCH 024/591] Fix editing selection and deletion on macOS (#13702) This fixes the selection and deletion during text editing on macOS so that it can handle selections that have a base (selection start) that is after the extent (selection end) in the string. Apparently the insertText:replacementRange on macOS can supply an NSRange object that has negative values for the length (even though the length is an unsigned NSUInteger). This change checks for that case, and fixes the range of characters replaced in the text to have the right bounds. Also, I added an additional updateEditState after the state is set from the framework, since there is a timing problem: when the delete key is pressed, it sends an insertText message, which updates the framework state after the framework has already sent its state update to delete the selected region. This additional updateEditState puts the engine and framework back in sync again. Ideally, we would just avoid sending the insertText message, but there's really no way for the engine to know if the pressed key is part of an edit sequence or not. --- .../Source/FlutterTextInputPlugin.mm | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index d444399a57d10..8ead075c9aca8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -107,6 +107,10 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([method isEqualToString:kSetEditingStateMethod]) { NSDictionary* state = call.arguments; self.activeModel.state = state; + // Close the loop, since the framework state could have been updated by the + // engine since it sent this update, and needs to now be made to match the + // engine's version of the state. + [self updateEditState]; } else { handled = NO; NSLog(@"Unhandled text input method '%@'", method); @@ -121,7 +125,6 @@ - (void)updateEditState { if (self.activeModel == nil) { return; } - [_channel invokeMethod:kUpdateEditStateResponseMethod arguments:@[ self.activeModel.clientID, self.activeModel.state ]]; } @@ -200,12 +203,28 @@ - (void)insertText:(id)string replacementRange:(NSRange)range { // Use selection range = self.activeModel.selectedRange; } - if (range.location > self.activeModel.text.length) - range.location = self.activeModel.text.length; - if (range.length > (self.activeModel.text.length - range.location)) - range.length = self.activeModel.text.length - range.location; - [self.activeModel.text replaceCharactersInRange:range withString:string]; - self.activeModel.selectedRange = NSMakeRange(range.location + ((NSString*)string).length, 0); + // The selected range can actually have negative numbers, since it can start + // at the end of the range if the user selected the text going backwards. + // NSRange uses NSUIntegers, however, so we have to cast them to know if the + // selection is reversed or not. + long signedLength = static_cast(range.length); + + NSUInteger length; + NSUInteger location; + if (signedLength >= 0) { + location = range.location; + length = range.length; + } else { + location = range.location + range.length; + length = ABS(signedLength); + } + if (location > self.activeModel.text.length) + location = self.activeModel.text.length; + if (length > (self.activeModel.text.length - location)) + length = self.activeModel.text.length - location; + [self.activeModel.text replaceCharactersInRange:NSMakeRange(location, length) + withString:string]; + self.activeModel.selectedRange = NSMakeRange(location + ((NSString*)string).length, 0); [self updateEditState]; } } From e924d71627544e3bcb7fbec4f3848c40c6eb44a9 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Wed, 6 Nov 2019 04:37:48 +0000 Subject: [PATCH 025/591] Fix splash screen lookup. (#44131) (#13660) --- .../android/io/flutter/embedding/android/FlutterActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 60f6d86a936d0..38370dd685ef5 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -494,8 +494,8 @@ private Drawable getSplashScreenFromManifest() { PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES ); Bundle metadata = activityInfo.metaData; - Integer splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : null; - return splashScreenId != null + int splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : null; + return splashScreenId != 0 ? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? getResources().getDrawable(splashScreenId, getTheme()) : getResources().getDrawable(splashScreenId) From 9726b4cb99d31f213ac4baf141dec902049f73fd Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 5 Nov 2019 23:45:39 -0500 Subject: [PATCH 026/591] Roll src/third_party/skia f3d4109a793b..cdc0c23f1a2e (36 commits) (#13707) https://skia.googlesource.com/skia.git/+log/f3d4109a793b..cdc0c23f1a2e git log f3d4109a793b..cdc0c23f1a2e --date=short --no-merges --format='%ad %ae %s' 2019-11-05 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-05 reed@google.com split out blendmodes for skvm 2019-11-05 reed@google.com split out serial functions for SkFont into separate impl 2019-11-05 michaelludwig@google.com Cache tessellation metadata between inset/outset calls 2019-11-05 senorblanco@chromium.org Dawn: fix mipmap width and height computation during upload. 2019-11-05 mtklein@google.com friendly wrapper to allocate uniforms 2019-11-05 senorblanco@chromium.org Roll Dawn to ToT. 2019-11-05 halcanary@google.com SkPDF/bublic.bzl: switch away from Sfntly subsetter. 2019-11-05 bungeman@google.com Revert "Fix empty run handling in trivial shaper iterators" 2019-11-05 robertphillips@google.com Revert "Reland "Implement sample mask and sample locations support in Vulkan"" 2019-11-05 robertphillips@google.com Revert "Add a "conservative raster" flag to GrPipeline and implement in Vulkan" 2019-11-05 bungeman@google.com Fix empty run handling in trivial shaper iterators 2019-11-05 fmalita@chromium.org [skottie] Streamlined gradient stop merger 2019-11-05 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-05 mtklein@google.com implement SkColorFilterShader::isOpaque() 2019-11-05 csmartdalton@google.com Add a "conservative raster" flag to GrPipeline and implement in Vulkan 2019-11-05 egdaniel@google.com Add new macro for checking device lost when making Vulkan calls. 2019-11-05 borenet@google.com [recipes] CanvasKit, PathKit, some others don't need bot_update 2019-11-05 ethannicholas@google.com Reland "Implement sample mask and sample locations support in Vulkan" 2019-11-05 mtklein@google.com unify program() and uniforms() 2019-11-05 brianosman@google.com Reland "Fully embrace skcms types in SkColorSpace API" 2019-11-05 bsalomon@google.com Cleanup kGray_8 readback. 2019-11-05 fmalita@chromium.org [skottie] Fix trim path mode interpretation 2019-11-05 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 6c7208f93d6e..67527cb45293 (10 commits) 2019-11-05 fmalita@chromium.org [skottie] Use SkColor4f for gradient color stops 2019-11-05 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-05 benjaminwagner@google.com Fail Skpbench on unsupported hardware 2019-11-05 herb@google.com Speed up diff canvas using small bit vector 2019-11-05 mtklein@google.com add color filter support 2019-11-05 mtklein@google.com x86-64 JIT support for Op::index 2019-11-05 csmartdalton@google.com ccpr: Fail gracefully when atlas instantiation fails 2019-11-05 bsalomon@google.com Add valgrind suppressions for GrClearImage 2019-11-05 robertphillips@google.com Revert "Fully embrace skcms types in SkColorSpace API" 2019-11-05 mtklein@google.com simpler uniforms() api 2019-11-05 mtklein@google.com merge key() and CanBuild() into CacheKey() 2019-11-05 mtklein@google.com indent loop so it stands out Created with: gclient setdep -r src/third_party/skia@cdc0c23f1a2e If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b09383ae22a74..6e4b82b7baa08 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f3d4109a793b2adafa1ae330ba092d8a7b38fbf6', + 'skia_revision': 'cdc0c23f1a2edfc0b3adeec14dc1c241a5a6558f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 5e4c3b15c71e0..933df03cecca8 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 25dad30fe021ac972ef0a9e2e3b7ab6b +Signature: 4395723ead34075392262caa18f40ccc UNUSED LICENSES: @@ -1359,6 +1359,7 @@ FILE: ../../../third_party/skia/infra/bots/assets/valgrind/VERSION FILE: ../../../third_party/skia/infra/bots/assets/win_ninja/VERSION FILE: ../../../third_party/skia/infra/bots/assets/win_toolchain/VERSION FILE: ../../../third_party/skia/infra/bots/calmbench.isolate +FILE: ../../../third_party/skia/infra/bots/canvaskit.isolate FILE: ../../../third_party/skia/infra/bots/cfg.json FILE: ../../../third_party/skia/infra/bots/compile.isolate FILE: ../../../third_party/skia/infra/bots/compile_android_framework.isolate @@ -1371,6 +1372,8 @@ FILE: ../../../third_party/skia/infra/bots/isolate_android_sdk_linux.isolate FILE: ../../../third_party/skia/infra/bots/isolate_gcloud_linux.isolate FILE: ../../../third_party/skia/infra/bots/isolate_go.isolate FILE: ../../../third_party/skia/infra/bots/jobs.json +FILE: ../../../third_party/skia/infra/bots/lottie_web.isolate +FILE: ../../../third_party/skia/infra/bots/pathkit.isolate FILE: ../../../third_party/skia/infra/bots/perf_skia_bundled.isolate FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json @@ -1427,6 +1430,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/flutter_trybot.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/parent_revision_trybot.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/docker/examples/full.expected/test.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/doxygen/examples/full.expected/doxygen.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/env/examples/full.expected/test.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json @@ -1635,7 +1639,9 @@ FILE: ../../../third_party/skia/infra/bots/recipes/upload_nano_results.expected/ FILE: ../../../third_party/skia/infra/bots/recipes/upload_nano_results.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/resources.isolate FILE: ../../../third_party/skia/infra/bots/run_recipe.isolate +FILE: ../../../third_party/skia/infra/bots/skottie_wasm.isolate FILE: ../../../third_party/skia/infra/bots/skpbench_skia_bundled.isolate +FILE: ../../../third_party/skia/infra/bots/skqp.isolate FILE: ../../../third_party/skia/infra/bots/swarm_recipe.isolate FILE: ../../../third_party/skia/infra/bots/task_drivers.isolate FILE: ../../../third_party/skia/infra/bots/tasks.json @@ -2238,6 +2244,7 @@ FILE: ../../../third_party/skia/src/core/SkDistanceFieldGen.cpp FILE: ../../../third_party/skia/src/core/SkDistanceFieldGen.h FILE: ../../../third_party/skia/src/core/SkDrawable.cpp FILE: ../../../third_party/skia/src/core/SkFont.cpp +FILE: ../../../third_party/skia/src/core/SkFont_serial.cpp FILE: ../../../third_party/skia/src/core/SkForceCPlusPlusLinking.cpp FILE: ../../../third_party/skia/src/core/SkHalf.cpp FILE: ../../../third_party/skia/src/core/SkImageGenerator.cpp @@ -3461,6 +3468,7 @@ FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.cpp FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.h FILE: ../../../third_party/skia/src/core/SkStrikeSpec.h FILE: ../../../third_party/skia/src/core/SkVMBlitter.cpp +FILE: ../../../third_party/skia/src/core/SkVMBlitter.h FILE: ../../../third_party/skia/src/core/SkYUVMath.cpp FILE: ../../../third_party/skia/src/core/SkYUVMath.h FILE: ../../../third_party/skia/src/core/SkZip.h From 28c4c5b1168da604298bc9779a8e2a038b36ffcf Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 5 Nov 2019 21:42:31 -0800 Subject: [PATCH 027/591] Roll src/third_party/dart 8bdca37e98..51016532e8 (21 commits) dart-lang/sdk@51016532e8 DEPS: update d8 to 7.8.279 dart-lang/sdk@11b1433756 Allow downwards inference of sound constructor calls. dart-lang/sdk@c06674e558 Apply review feedback from: dart-lang/sdk@1d90df9279 Migrate recent changes in sdk/BUILD.gn to sdk_nnbd/BUILD.gn. dart-lang/sdk@e9c57d2e04 [dart2js] New RTI: Lower is-checks to `instanceof` in-place where possible. dart-lang/sdk@5134490b78 [analysis_server] add crash reporting dart-lang/sdk@af37dcf540 [analyzer] remove older crash reporting from the dartanalyzer cli dart-lang/sdk@1d774abeea [analysis_server] Remove old crash reporting dart-lang/sdk@1b947a51cc [dart2js] new-rti: Copy environment input type before refinement removal dart-lang/sdk@e31c7ff591 [vm/concurrency] Move idle timeout detection from message handler to [Isolate] dart-lang/sdk@f908fb67ce [ VM / Service ] Fix potential race in service test helper dart-lang/sdk@1f8ef384d9 [vm] First pieces of late modifier implementation. dart-lang/sdk@b8cd8eee5d Migrate dart:internal to NNBD. dart-lang/sdk@d3f19cf7ae Reland "Enable visibleForTesting on unnamed constructors" dart-lang/sdk@e65d95e8c8 (dart2js) add indirect dynamic calls to global inference and use it to model operator== dart-lang/sdk@9ba71b0fdd Modify local inference to generate constraints with respect to variance. dart-lang/sdk@689447d0fb Revert "[ddk] Don't use Library.isExternal" dart-lang/sdk@8b050df2db [infra] Remove duplicate args_tag from DEPS dart-lang/sdk@2db4368eb8 [infra] Update CHANGELOG.md for 2.6.0 dart-lang/sdk@d635bdd49c [cfe] Move variance tests to general/ dart-lang/sdk@9ddad67f73 [vm] Add aot option to protobuf_aware_treeshaker. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6e4b82b7baa08..50b8a7aa224db 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8bdca37e98a59bdb7f52daea9da838b1a60c1cdf', + 'dart_revision': '51016532e804570dd62edd5445768c49ad6e27f7', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 79966cccb864c..630b20b3c06e9 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 62624acd0f795337602c13cd2e2e2fdf +Signature: 2a5fa516e6ddf01f0e8048becd7a8037 UNUSED LICENSES: From 08a7d21db67118dad2f97b2493a86d92957cf5be Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 03:36:22 -0500 Subject: [PATCH 028/591] Roll src/third_party/skia cdc0c23f1a2e..f00524707da7 (2 commits) (#13712) https://skia.googlesource.com/skia.git/+log/cdc0c23f1a2e..f00524707da7 git log cdc0c23f1a2e..f00524707da7 --date=short --no-merges --format='%ad %ae %s' 2019-11-06 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 67527cb45293..1d09b983031b (5 commits) 2019-11-06 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader aaa64b76c0b4..215bc7949b35 (2 commits) Created with: gclient setdep -r src/third_party/skia@f00524707da7 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 50b8a7aa224db..8572e177d6bd6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'cdc0c23f1a2edfc0b3adeec14dc1c241a5a6558f', + 'skia_revision': 'f00524707da7a79e4b8dcbc6122f0773f7e9fba5', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 933df03cecca8..a64554b88fd3f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 4395723ead34075392262caa18f40ccc +Signature: 75ca46a2aafed425f6f75cf668e6d480 UNUSED LICENSES: From bf92b9c97807d0c55dfe95ead153ec8d23f1163c Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 6 Nov 2019 00:37:34 -0800 Subject: [PATCH 029/591] Roll src/third_party/dart 51016532e8..80fc4d54e5 (2 commits) dart-lang/sdk@80fc4d54e5 Add a NNBD test that needs NNBD migrated SDK. dart-lang/sdk@17443b2ab7 Add a preview-port option to dartfix and rename outputDir to preview-dir for consistency. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8572e177d6bd6..f57696d269b1c 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '51016532e804570dd62edd5445768c49ad6e27f7', + 'dart_revision': '80fc4d54e5246a1b63384fe26d72278f8c090b16', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 76c38338419c1443983427680079123ab817b584 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 05:55:06 -0500 Subject: [PATCH 030/591] Roll fuchsia/sdk/core/mac-amd64 from bCFtP... to 6Du8E... (#13714) Roll fuchsia/sdk/core/mac-amd64 from bCFtP... to 6Du8E... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f57696d269b1c..38d5bf0250b7e 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'bCFtPAMdFsXQFQeOaZ7EdGSMTVN-tZ7RVs5bT3BueMUC' + 'version': '6Du8EyM7O_xsuN2tkEpl-MyD4iDAs4h4wsnw3MgU-2EC' } ], 'condition': 'host_os == "mac"', From f55b12d57b384532b99e1381ebadece686d5fc3a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 09:27:47 -0500 Subject: [PATCH 031/591] Roll src/third_party/skia f00524707da7..6e54a299cce9 (1 commits) (#13715) https://skia.googlesource.com/skia.git/+log/f00524707da7..6e54a299cce9 git log f00524707da7..6e54a299cce9 --date=short --no-merges --format='%ad %ae %s' 2019-11-06 robertphillips@google.com Assert we are now in the world of single-primitive-type render passes Created with: gclient setdep -r src/third_party/skia@6e54a299cce9 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 38d5bf0250b7e..d83cc981a02e8 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f00524707da7a79e4b8dcbc6122f0773f7e9fba5', + 'skia_revision': '6e54a299cce92871bffab6341ef0b41f51ff3525', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a64554b88fd3f..06c06872d2f09 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 75ca46a2aafed425f6f75cf668e6d480 +Signature: f38817fef6d5c82164e147c0a8cdd2e0 UNUSED LICENSES: From 83e9a5752fb92715189c2f78114e657a68dff423 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 6 Nov 2019 06:37:38 -0800 Subject: [PATCH 032/591] Roll src/third_party/dart 80fc4d54e5..24f1f313a2 (5 commits) dart-lang/sdk@24f1f313a2 [cfe] Implement the rest of the NNBD-aware subtype relation dart-lang/sdk@37d14ad2ce [ddk] Don't use Library.isExternal (second try) dart-lang/sdk@fe3afde2eb [CFE] Parser: Don't report (recovered) operator as endClassConstructor dart-lang/sdk@e1f19de968 [cfe] Add MemberBuilder.read/write/invokeTarget dart-lang/sdk@239ff1408d [cfe] Add MemberBuilder.buildMembers --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d83cc981a02e8..6b0249c3b527d 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '80fc4d54e5246a1b63384fe26d72278f8c090b16', + 'dart_revision': '24f1f313a22c6b83b9eb1abed01b83ee4f681057', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From b37ee31f7e953192f1b558c58e3ca80e077ea838 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 6 Nov 2019 09:42:14 -0800 Subject: [PATCH 033/591] Roll src/third_party/dart 24f1f313a2..462a448ac8 (2 commits) dart-lang/sdk@462a448ac8 [cfe] Integrate definite assignment, nullability and type promotion analysis dart-lang/sdk@cfe29c45fd [doc/ffi] Update API docs message to be consistent with website --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6b0249c3b527d..fe75321d15372 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '24f1f313a22c6b83b9eb1abed01b83ee4f681057', + 'dart_revision': '462a448ac8ef12239407bedf5fe6972261ccad44', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 630b20b3c06e9..d6d294112939c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 2a5fa516e6ddf01f0e8048becd7a8037 +Signature: 02e7844a72324090905134c61f4f48fe UNUSED LICENSES: From 9358e7527cef46cef18dd4d72c52a1a182b1661c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 13:21:25 -0500 Subject: [PATCH 034/591] Roll src/third_party/skia 6e54a299cce9..6790423f84ef (11 commits) (#13718) https://skia.googlesource.com/skia.git/+log/6e54a299cce9..6790423f84ef git log 6e54a299cce9..6790423f84ef --date=short --no-merges --format='%ad %ae %s' 2019-11-06 bsalomon@google.com fix tolerance in read pixels tests 2019-11-06 robertphillips@google.com Revert "Fully delineate GL usage w/ skia_use_gl" 2019-11-06 mtklein@google.com guard all SkVMBlitter debug features together 2019-11-06 nifong@google.com find min and max, separared by comma. not minmax. 2019-11-06 nigeltao@google.com Inline SkWuffsCodec::readFrames 2019-11-06 rosasco@google.com Fully delineate GL usage w/ skia_use_gl 2019-11-06 borenet@google.com [infra] Remove Calmbench and ParentRevision bots 2019-11-06 nifong@google.com Record min and max frame times from skpbench in perf 2019-11-06 egdaniel@google.com Have vulkan command and descriptor sets handle failed creation. 2019-11-06 bungeman@google.com Reland "Fix empty run handling in trivial shaper iterators" 2019-11-06 michaelludwig@google.com Combine mask and edge distance in TessellationHelper Created with: gclient setdep -r src/third_party/skia@6790423f84ef If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/DEPS b/DEPS index fe75321d15372..eac00081e2a4b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6e54a299cce92871bffab6341ef0b41f51ff3525', + 'skia_revision': '6790423f84efd3d03666e7cb34b9eefc8d898d8f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 06c06872d2f09..c8cfb05f49d65 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f38817fef6d5c82164e147c0a8cdd2e0 +Signature: 1cbd9233e4bef369fa7a4ee0ef232811 UNUSED LICENSES: @@ -1424,11 +1424,9 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/bu FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Win-Clang-x86_64-Release-ParentRevision.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/cross_repo_trybot.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/flutter_trybot.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/parent_revision_trybot.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/docker/examples/full.expected/test.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/doxygen/examples/full.expected/doxygen.json @@ -1486,8 +1484,6 @@ FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/andr FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_trybot_failure.json FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_unrecognized_target.json -FILE: ../../../third_party/skia/infra/bots/recipes/calmbench.expected/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All.json -FILE: ../../../third_party/skia/infra/bots/recipes/calmbench.expected/Calmbench-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json FILE: ../../../third_party/skia/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json FILE: ../../../third_party/skia/infra/bots/recipes/compute_buildstats.expected/normal_bot.json @@ -1552,7 +1548,6 @@ FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10- FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/skqp_test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json -FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-x86_64-Release-ParentRevision.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-GCC-x86_64-Release-Flutter_Android.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json @@ -1628,8 +1623,6 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/pathkit FILE: ../../../third_party/skia/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian9-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_buildstats_results.expected/normal_bot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_buildstats_results.expected/trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/upload_calmbench_results.expected/normal_bot.json -FILE: ../../../third_party/skia/infra/bots/recipes/upload_calmbench_results.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/failed_all.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/failed_once.json From 47579164adcef77936c2288604945995605daf16 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 6 Nov 2019 10:31:41 -0800 Subject: [PATCH 035/591] Fix NPE in splash screen lookup (#13719) --- .../android/io/flutter/embedding/android/FlutterActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 38370dd685ef5..47dfe7abd169f 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -494,7 +494,7 @@ private Drawable getSplashScreenFromManifest() { PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES ); Bundle metadata = activityInfo.metaData; - int splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : null; + int splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : 0; return splashScreenId != 0 ? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? getResources().getDrawable(splashScreenId, getTheme()) From d59b1583b0357a25d1cddc24e31d236e87fda847 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 6 Nov 2019 11:16:18 -0800 Subject: [PATCH 036/591] Ensure that the device pixel ratio is taken into account with window metrics in physical pixels. (#13708) This was originally patched in https://github.com/flutter/engine/pull/13193 but the unit-tests were written under the mistaken assumption (that has been documented already) that the Flutter window metrics used logical pixel coordinates. That mistake has been corrected and additional tests to verify rendering intent has been added. Fixes https://github.com/flutter/flutter/issues/43906 Fixes https://b.corp.google.com/issues/143529469 --- ci/licenses_golden/licenses_flutter | 2 ++ shell/platform/embedder/BUILD.gn | 10 +++--- .../embedder_external_view_embedder.cc | 17 +++++---- .../embedder/fixtures/dpr_noxform.png | Bin 0 -> 36606 bytes .../platform/embedder/fixtures/dpr_xform.png | Bin 0 -> 127639 bytes shell/platform/embedder/fixtures/main.dart | 9 +++-- .../embedder/tests/embedder_unittests.cc | 33 +++++++++++++----- 7 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 shell/platform/embedder/fixtures/dpr_noxform.png create mode 100644 shell/platform/embedder/fixtures/dpr_xform.png diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index d9b30258d49e8..4e36d64a55529 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -869,6 +869,8 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_root_surface_ FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_software.png FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_platform_layer_on_bottom.png FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_root_layer_only.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/dpr_noxform.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/dpr_xform.png FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient.png FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_xform.png FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 50b700228dde0..e6d0ccdcfc2e4 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -87,15 +87,17 @@ test_fixtures("fixtures") { dart_main = "fixtures/main.dart" fixtures = [ "fixtures/compositor.png", - "fixtures/gradient.png", - "fixtures/verifyb143464703.png", - "fixtures/gradient_xform.png", + "fixtures/compositor_root_surface_xformation.png", "fixtures/compositor_software.png", "fixtures/compositor_with_platform_layer_on_bottom.png", "fixtures/compositor_with_root_layer_only.png", - "fixtures/compositor_root_surface_xformation.png", + "fixtures/dpr_noxform.png", + "fixtures/dpr_xform.png", + "fixtures/gradient.png", + "fixtures/gradient_xform.png", "fixtures/scene_without_custom_compositor.png", "fixtures/scene_without_custom_compositor_with_xform.png", + "fixtures/verifyb143464703.png", ] } diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index bdbfb69985a02..b742bc043b527 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -148,8 +148,7 @@ SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) { static FlutterLayer MakeBackingStoreLayer( const SkISize& frame_size, const FlutterBackingStore* store, - const SkMatrix& surface_transformation, - double device_pixel_ratio) { + const SkMatrix& surface_transformation) { FlutterLayer layer = {}; layer.struct_size = sizeof(layer); @@ -160,9 +159,7 @@ static FlutterLayer MakeBackingStoreLayer( SkRect::MakeWH(frame_size.width(), frame_size.height()); const auto transformed_layer_bounds = - SkMatrix::Concat(surface_transformation, - SkMatrix::MakeScale(device_pixel_ratio)) - .mapRect(layer_bounds); + surface_transformation.mapRect(layer_bounds); layer.offset.x = transformed_layer_bounds.x(); layer.offset.y = transformed_layer_bounds.y(); @@ -231,6 +228,8 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { // while making sure to take into account any surface transformations. if (auto root_canvas = root_render_target_->GetRenderSurface()->getCanvas()) { root_canvas->setMatrix(pending_surface_transformation_); + root_canvas->scale(pending_device_pixel_ratio_, + pending_device_pixel_ratio_); root_canvas->clear(SK_ColorTRANSPARENT); root_canvas->drawPicture( root_picture_recorder_->finishRecordingAsPicture()); @@ -243,8 +242,7 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { presented_layers.push_back(MakeBackingStoreLayer( pending_frame_size_, // frame size root_render_target_->GetBackingStore(), // backing store - pending_surface_transformation_, // surface transformation - pending_device_pixel_ratio_ // device pixel ratio + pending_surface_transformation_ // surface transformation )); } @@ -318,6 +316,8 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { } render_canvas->setMatrix(pending_surface_transformation_); + render_canvas->scale(pending_device_pixel_ratio_, + pending_device_pixel_ratio_); render_canvas->clear(SK_ColorTRANSPARENT); render_canvas->drawPicture(picture); render_canvas->flush(); @@ -326,8 +326,7 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { presented_layers.push_back(MakeBackingStoreLayer( pending_frame_size_, // frame size render_target->GetBackingStore(), // backing store - pending_surface_transformation_, // surface transformation - pending_device_pixel_ratio_ // device pixel ratio + pending_surface_transformation_ // surface transformation )); } diff --git a/shell/platform/embedder/fixtures/dpr_noxform.png b/shell/platform/embedder/fixtures/dpr_noxform.png new file mode 100644 index 0000000000000000000000000000000000000000..092591aae5c16cec7c7e74ae9feca32089b72b62 GIT binary patch literal 36606 zcmb_lc|4Ts7td9ws9d*VtZ`dzsmPXW715^BV#_{d8xmv5GKjKdncQ1ldr7%!tTAP> zr;N&)Wn``FVJ2AyWBEPrphhuC0iu3>;8jX&yo!?HY8RYfcg!~LOAinD2LW_$F4+Q#&Vn563a@6KD8) zPHr$Y2H%bJeJ0NTFjYFQd^ktY$TP&X?M8lBrg`D-?8i+k*5Tum-;{d_wFj{k+OKEM z%G~mTVoYd|$RgyNn&_`@IVlgfFxAYjG06`ceGL8KC_fWZH4EX;_H&Ziy4FGV0yt|T zPZL6`ICuGVsXHv92f3EV;q5|<9^>|I;dwPv#rK-G5HEwFY4Z z7QDMxxtDdk(tABOY@JHO7wNcI$r=`JSTouC=}I?qnEngb-pmeKODGE@6#+{yQC$vy zeuJq=pX=jz3IaM?!)=ozy?tAwQza!ZMu^mVrfzEB&x@<6h^NI$avtyB9&V8MRCOP^ zK^k&}9qoo*FJQ-*H^i*`trCtqEQZ(P58bEC?B%yHq@?F_ob$$@!R*`ZX8c##d+y-O zxD#L1%X?kTHnu3Y2=*RZng1E=g|Oxo-bxJ^f|)&tL^)8uTv0M;7x0}%v{GN-!!Vr{=L1%VPk&J6e&~CR zaJD$-_3Fu*=S8B_E=9Qdkn7g>?>=B0^pGi_eEHR`RJ}h#;Yn+XZ$T&LS7hqwP?;4_ zINEfyKP`Aai{sk{iNKmq)&lu?O(~L{l6a=cYeo)W5g!Gc3hi$d)-6+Rp}3bzUv;9c zgNQS^+`cP3bvg4t+OU^<8jJ{h9BeOS7Fw_5^m=zNzFsSCu)$vf7GCefA4X=-dbt)} zuM}9XIBea^VLzkeTH<+`2;nS2#}b7C*WT4_O1TOXFD34nny$CnxgqbY_n?HN5Zffx zfYUj?l+rhdJYW#3!FsuQ7cvM>@A1F#N+IXaqcMn&iCwPLlbh{t!H-zF&46#hg}ub} zUo9gg`G0Y*m4Zxi_pQmXp<%8v?eMU3DOhc>GaNN#z|!4>Z-ntbtdP!=L%3k;U|sTd z^$_qSg?g-9g%IV?zP5$!IJ!+Ze;LxjhzJ((LUAF7TgEzZ#xsfsw(goW&3X^)aj3zW zT}eif^~CogH`D&iOlB~O^4tpn};JhYC_DJ7l}Noh(2kwk&%ry%s#wfe{L6R3$$K$n)P1&tkwaA zs#)&lXB?St51{1QrG-M;h3OAkQ}JP^#A@|OO4Yl=9U=8l0CapccI=ck+#*j{@_>>{>3n_ehZFcK=?_RexoGmernFMcVh`6H?KDCA+yI~uSBScrO1eK>{Gu~-aO#l z2TtPP)~ukeZ9!NjpF;BZ{y`A4f!ELWHi=TBec+RyP++;SAXJ%0Tg+#_ReQa=#DM*d zQ|(vRbVK-F@}r0I?r)%|7OV6MV82%u1hfm@W72{>iqK~TXdLd|XvNR=FjgWSssMd* z1F(l}>d70z&E`imywMPK8$<&wE~vjPKFHOfh?RWj-1-#yUVZV&-~Xu5hNZBnuL~7; zd&n!K?Q>on9JmHqWPvRXvdU|2_ilvTi2R~HH29r1uaUW1^V+jY`=)6Qiy84g&oMDE z|Az-ek?f|BAB~E50fNz0eX#%dd@;kbox3<1n56vL)o_?jfkjLhCjX!L44AsXQeZ1O z7X-8?Oz!Ztagg=2QX`yGv8ZIf)Rj4xt2e#eI?jCXU;~yh&T;c0#4={~_PiSq_+_Vv z#f3;kZV1Q0j#oKFjMdM$u zkTBKL->S^~#I->jwFAmlkWyFXLkCCG^`hHcD~j(iz5ffuH;r(EoswmeHDt~3yAG1< zo4+#6TCdIYdRH#6-mxIj+DNe8H4CrTFJuL8amI62@abS%{6ONpkLl&b)gO+`6EK3r zF-BKIPfK{ST!7q7&4Ria+Rf)J+-nXP@{ZKfQZ#PF)Ot17EU;b^9-S0f9@x!uu@@)) ztdxnMZxS$_6sACo>uYevzb|YO6<5+$wHLF)_$7sQ0FRS9T8X*d9yUcP*NoN`;oGB) z(>K`kv#|{MJiK}ra8`5-lg>_;<9E->dHYlzOgrJNHfM~JdSZm!1HSao+p!~YHq!KC zh8V_ZO(5pX1@rxy!E$jnD>(kUZkoh$avs`VL`d+G6>CvtQczN6DsEpB7N+C)f%vK z9B%y(s25ZnoMuiR=Qcq&7bT+1yypx98;uM*C$<74rQ3n^#rM6ukQ7H{EK}T=6=mHD zr%j%XPJo~;pv?Tc_=0XY9C1P{MWWnOWp!y$wRZ2`0J_-W6?!5iKM=DTgg+H@_xMCSt80_z^ zmcT+wT0UD&P*nsX<#$a%S{h^2og(}$tXpn!_`}Kl(XTXQn;y|+FkYclIbx$!?>gAc z%tancZJ$P9g=ba}IdtI)XZ*f z1p$p>ky`D->XKr+7s2SaBmI1^n`X9b_-crhnEfCC4&b$EB%c|lnR>L_eLCJODytZ< zyFgq^=rbUuB2uRgY+Iq=9#!Ud3!}h{Ct23Te9c_u!C`Z9QD6XUP_Le>QFEWEYv$nO z0YG7;zNb|GDHYOa-sYgTQ%EQ&oyg8an~XgQ^gw`faAN?Fjf(m4SwWk*wFAB#;n_kw z;NUiVw_BpTjn}x#o8B3f4*ft1PX*v&ueQkdWLDRZB??#6*O(UE?SBP>MQI zSRe5FUe;3wEZ!M#B-(1!36DUUB2NX7e*=>vqC&QKMcWYcl!huiBQ+#k2QugqZ|1@V z+v0klfnouK6c0vu{Lymq;&DW|r<>vg(9r4RJF|}Q-cwR6Uo4X>BVqKFyjRdgy~bkE ztv$oU9Q0Rt*gDvvo=_^B4d+7%@z3>HS7`CeYAT(C3mF|m{lJA|WfIsH^4$H9mV1{_6;z|T`7 z`j?%W)tnPiu$zlT9PAM4mJ~%&NOC$d^dGOpRM&hLnIk8-SAW;=?*nCo+fYW%wOr*^ zA4Mc>yr@QigH6gA+wz8Pc;H6j#ce>jsmA2c0||oD1VG~3;#viF-?j`c_B20~Uj=G+OUWJb&UpJRb6XvZ&@c zM^S~do({c~feB9B+ineXJlzC8`}SuChQ&G_!Y24Uf}|GvkoytMYKqT33D7%EG}_r7 zNH~bpZS9;^qAj0IO=XSoH5A1+h(v;C`3yamm|dbnj>1K0>fq9#5b4&~b3E05F9L0% zmno2_gJF{0hu$%zjA=LDPpzt$=>?cm7@CE&>AcG?6<2@hs0%9TNIT7Jx43|*-9y<{ zmR#XUFQ&K^#|A*P$U}hB!2vOrc)LM<2_uyu7*+PK$m_5@j2{Us{MxOc509?=%4f*MycEWCKrq?##PL z#N{v-&H$b^J{*-c>tGi?%p#71CpkQl8f&X8gUbRJ3rJ#i0CXj^=z?ylgkK0Zr~%1- zOBVn`;4^Mkla0nO#Vn2keb{pU9Q2g^$sZMH>~~R*nIsX_#+k6V;F1SCm8XxFuA@dB z2z;5bFXgq)AN_37cWfOb(jrq@%z>wd4|A|F#)RUtOUx8Z9)1kWsff;L?mnnio`7Ee-l$9+Y@Va*komxn81Mq?Y8Uuhm~Rf@P0rFV>3J zSvTW#T-}7nR~8)z-l-{93c8AZuFm00g;Y)S_-k85Y2rfn zj#yzOoJ6oG^RIbnzCXBhF|pOoyZlVS-AsZ?WJj_@FXGnNVk3{x*Btcej(WNv+{142 zXJPeK^-@~Ot2PBR?^!05nEq;+PMFI;5>MOP)P+u^sjzRS_Mn&+fC_e<=oPNM_K=;q zEUlgLNeK)SLlGG^1*tJyhx&4lq-2e+7W;$W^qrUz>-MTA4?Xgsmq$|#Iv+ZxzTtWK zgvaYXKD9m59U3$Qc&{#3#p91w$_cqXWaw#!<$f#Sq8n^iQCk+?17Yc=bVR&=y#7h| z3kdrqIdG&ZS6`e449D6U&qu1FYQcdZle%lUa9UcNL5fY_shszKcgse%9G>o&n03CF zgxxzvd9kX0)iirm?~#{4Eb@ud{WCsWA|DctHsmZR5}k?HEma}qr zBVUvtX%f~nU-CEKGv5yP%aOW+EHS1$Gs3c$6~iWi_xG#NO#o@bvb2TpS6n8-?Fs8r zKwCCw%O~|%dk?@9k;zQ5+wpR=4Z(9Z8_besGJ7f85R}Cq^tlMXQP^ADJH+Os$*IQ< zAJyUv&!j80t*5phrWu3&Ve6;HToum+w9Xh{z_ORXHK%|q8yt4@zck@l;zT6|^*Cm( zE|%|7DMv9n68v)%AhvHD-WGTT3=n~iDn2PbpkCiy21`IFPbl4lfGe6Q?u8r z?7CHpG;^OVUKQ2tN0VO}w&a$ZI^LpIW9MlCn59JqHh@R|(jgh{=K|Phf2Jki-nkwe zq&~IJ2N0(3fPP*GgtU`Vb2gW)qjqn!-H+qnp1V4Q6lves+ z>H$V|Gp6URDzFSiQcfNGZvVjR`#M+ska>)n@Q9JUHu z{@B1UA9o4d;#E3GgmtUyQ#cdW8e`!Xmf-QD;eIud4{%K$%P~>`evAv7Q^{AQx$tKo zos^Is;~}MoESNuf)-nsK=GcZC^q%3Evoq!;wRImCZ{hof^ji-lo=}zRGw3{~=vDok z>s^KNPX(lTB05}>(>}?GP)n#9SM)lx1A#9JuXz*WkG!i()0I72wmusQjj$B^fUf6s zo^Uo*r(FL|ex$m7GcbM~l`4Am<2jTCkeF!LtaGt+*Xqp#c}AD(^6oQ_`XHZYsAgu= ztd<=&A$6;xZ=~FVwUxZx6*hb3XFjlt^}mPA&Mc&;H+ooI>@7OPMX1* z`IAp}_2sdyH7Wyv595+1*fbFvIF}ILrd=NY9>n!z;PQr#P<#*RH0jJ@t$8_U7xi(( zwleQvk^(q%*P1D_izeTRuHQM;d%pnQnRfP5(ctVmI1_3b)28&S zzN}{h4VR`H`ROB`M)IS71oQ7}CG)FU={)mz+ zySl40W|7jv_Yl8t0u&~OlnxSPz<`*v3S-gj%p~khe>_1i`IQoM>hJmzr^0MU_QMXx zw^K7BhIaD!km(s5THRZf>p+K#Q;@Ve|0LUnY?t1>M=Nz@C2fgkj(s1=nOpDwX75tr z&nG~Lk$ARP1rhl9CfC-`^rAY>eRm-_Iw zWub(QH&L0Hs!9Hx5x#mzl6yJ;`gNLeiWQ3lyB>t_FwAAAy8wt3mf0`(r>YQ%lmXSW znW?dP#0l|mwta+51YdnsqsQ_9gF|QLI{#FjpQ~CBwNpR{yA7Y9~vOSJw;zV}5mi_1ICB*8Zr8pH@G}IdhPwvlH)itb1udbJ02wDO8K^b~V zZG;lnAn|56l(;rB+p($b`jdgC9je&KG=!=`d^RkQQD2^YM;>jb2PnqiDyOMdr*vW^ zZ^_Ky5`DZAS3RQz9r;5?xBFDIFHxW6^mGqhK)o>=*gYmYmg;V?4tI5>5=V4?Zry}~ z*3C7bGCfr^fHcR#P|`CXD%F7BSG*lAuv4;Ia_$+CpJ+S)x*QAPPHd}lmF2Jp>owOE zT}3M-rD2P(^FTsX-{kM3oA~aULl}PoSxGYt73pr}^L=b~rlVpexTQLs*$MzwxA&WO zRXw%#Df;+UaQQIkrDZgA`Tor5h|^;Pw4?}T56M=mfvf8}e>dOO0PZ-|JD&sNnYfsyWCCUSln613lT$glBBYb9@ zls=lb$gAs5if9kqekdYI0t{?c<$x}ft66_`pX6Uhd<;FTm835D(YZ=6b}?_8+H?UI z(pLjzhfs$NyPG{$j(5r*Yym#Oh|2iIU8R$(>yJb}gu5%`rq%;VW&#Q(EKD$7_3kLK zqYPqqWZENylGh>3qMx{|ys8UqF*E=XH<(maez-sFrxA!baSNY=mLtcKI7NxR^3%ij z3u|p50-W0sF)dN|XLnyS5S(WKcD`O>R=JOZe>fGut zBPe|owhf7Qg3GfAKji0v5p%TGfb^8g*5uEhA&+s2?h`S$mr8Vc=%Y&atl!@pnvx@A z3i&9C(M{-45$%Sx&W|?06c{TJ9jH>fmNE6ffTuSj^d^^I3sBw*s7j^F*1&f&!$S+? zq%BgMHblI`TF^#vtv&n45(2$A1t)E}@0cA;u?45*vVIBAEJpkd_O%7yg*7)cuBaaVeJUu^dJeXY)Ge> zto)De0rNfbziFvUR(7YxIO<~r%{xu&#&`OQqb-DCx`f3OIiTK`=Tz#&I zFI|bO5EQq)K6sWh`)+x~upZmf3(z zeB33GQUOwna|gN7a50RgBS>k^i@lA&v4MJ&EW{u45uQ$SrNIDP;^+1wQBR9RLqrlt z!{n8@G;;5_DY;K>9#GzE#}wqD{3qQoZFShyav13p zR5HLpwc9vjMzQIE&dw-jfk8mTodtSfE{S>=XQeu{k;cVdHuXRs^O>7G7C3GX=)@Wz zQdeCETE%8H(oGQ*tFy7p(mri7jdZI`buEgkfbw950Oi%X+(HUCZY`g}ysO+6tm?c@;0Db^mlPBTnS!YbGMi>M2K&Qs{WKM?jaOy0Vt;#R zy1#BiZF(HCUlMTfun0eU;`{MV*X02@dnDaCLcnb-^R@jyka)7G7c^igNF)-%a>Uzf z$sbWlNgltQE(E^rvv$YA8k<+OAm`Bz$)8SL?vuM7*^}=@NI;`xqSK3Q3-a-hb%(?_3vinZwph>A*-FHyqAni3M$kD0vOCxAaE%%*(=^jffyGmWmM^F|&lNjd? ziVMA<0*UR|L?}inO^E#YRF+)BD85&UB~n>y@nxl9fOHZXnD<8*D;;{OU4zOBG4f~^ z4k-V0&BdvCe&1dxxLJ8CL!B- zLDz%Y9Pq;~B!K)M2zY6YhbmpPEY0aBD;yS}G4Vt}I@R;V>&J)3o%X-Em*5DQAR`sj zxwqlUOw3@iiu4k!`Ivtl0aI261B z3>;J$%3=Dp$6+0^x1p_it5njHwl+fQkFKF{7c{v^d#}ng^#5QA)<(L?glCWnMXg#T z_K^XjudlO}Qt#G(6ce&=_><+R94!omH{-Wc^O;i51Tba6Nb;5^a&RzwlmH3NJ_g64 z)iNOsveX_@pzdo$X)r|Uv+i4rS1Z^|JHaTqC3j1&EYu!2Ys_Pc-^INz_8dvmn;8&@ z2&si%{5-eGo8yF@8*RF{*GpJnXe+C zx9i?eM?e`023J(#4S+H;6l(T5w8cF=$!UxtI0Wy{Uu#^wjwWe+JL~ zCu7gvf9S-JAOh+T1yiI9RTigBz_mE-Lb0uqE($aV9kLjL0zUWGvxl65xCc6`d8@b@sGp2qt#(Pfloo z79|7RINOG(BT#XP5v?Q3*8C7q>o}HrYyyGza@(Gwdq5NoD{_*0@_Jw;wvv`XML9g z(Kcs^Gez6DfH4V}*R=ScmME=E)}TFi*aK zudyErUn-<3Kz+ksAs`X$iSS&!*Q^4^3;QQ_2#RdBx0V`SOq=n2T6Q474QAf34N_*P zqkb+Z`43BqWd|jceU!RB|D}c5P*oT(deipY_NZIIHcJS$x4{5P$?+y?#ZDCDx>LIG zS<;@bn`kfL`60*t&seva8`(qA9&wM{ZMP>8CAcE~m$$>t{DvWm`rOTYfKz@L(0A84i!|F3L~Oq z)Yv(`cMuivP+K$;&2=jWQ_atTe%Q%QJ(uYu?6DKi$W7YouzpZnXEO2xH>J{zvSEYZ zOHLjlf)>2YuV8|Ism#o#6;zd;O!besq{m7vR97Jx>)r+01eA{Mtf$zH@mHEx<)HOk z!POmDVvc>Q$s9r~-N3tsq8JPm{*NFnl1ShORoA5j8WA!ovIO5zNdfHa&0Pj%=s6;d zQZfYbpfBz2QFXLq`VwfWGng>#xIE5vvpK98@!Jo>+~^W$G|56*!e~NthJ$ zpjxtRwtE((?_W6*<#2)Ib0U-MHIa>fFA-DQ0Td=AP8r89AnHbFPSO{Yvi^N<&!?~i z<0x{4zQ7f2WQ*x#AZz7~sL7EsX~qwZ_7U-h0_I@Kf-C^FHoByTLsbcl?6*a}tuz;~ zeXl;w((d4G6x*Dl*TT?IgqLlptngS}E2dWmT!~M)n zV-JJh?|uVq!MJ`KH*K#V16hkV5j=trfKRQ>{E17z&O?A+mwr?-Z7YNME*T_tgL%FC zhyEG@UHNKXg_W0J2irb_iN80XNR&9_bQ}J2iLTPhG;d62R~@)Rx6dIhEl3^F3`-@o zxQt&Rx9q+P>O``vpj+Vi_Ac{>kE+o(7MH(n00m?n3XwBmp$+gG?^Ie94aDw5s2FFi z7i&;39va^Lj6w!2>YK}A^_7!vBRX;f?;a~yhXO;Jm)#x!F^ZAi1F~3RSp=Jb?-sAz zJH6w3CxR^`3jpx00ZAO&#`Hp6+GaYcuuGqrzXmf74|Ay|nf|Wov)ZA>C2tWIA+Ln0 zK&vefbf@`x)Fw5)xDk^qpz#E?7T6$+h_BMiZOoMc&Fj-eEIIP{upZ1O44S(b24(ue z6E}l(7wpEEV@E)P!xN@aD0#X)rU9<}?!*mN&h!GzT>m|-d(+<@e?Y+pZ`&5z^lw+UjC$gIqYsaLc4-VspffZ_;-Z#ocWeM?nIJ zbDzdQCsY&(cdy`Z$ei4S)r_WOm$^9Y}_6Bl6!!>XX(Kfu7SqZ*3@ zQqyLDA~BGLcdB=o{;ZgZ(kR-i1L>r`1IYdgFhV@xvQk|8XzxTes71W)Hz$;Xt-_J; z9q+^vK-oVK#0G4W!pWs<$ZgOv#RuAv!4N`21ews?@61VB1r% zcpEuy0Wjt5Bs6v4Mt`0vRd8G_zaN(wcN+-60zrr>M26rQU$KNpvmV$z5)`e3SAVvYj{mtLPM3%UfI$M@JCiWO3RiVCj1NX|{oN!%8mqG4C^C=}wp}Vn=p>MRnv+cjU0ziG})|=1BQ9f#ueGMH_{zaFF6XyGWP~ z0+69aHjD1D=!cG9gDGaPpUbebXP4r&JFP`!KIMHVwx{Lk0E z-BggSI8#4(N^TjJi>o+6QMBvLAEVC8+%N3>b`AM-vkcux+-wgaBd8xGI_=RaSacwM z3bnAUfij-}LU1I#%gq>$JH?~H0NUz6EWIS-E$qYV+Unwup@&LKr^)20PsGy0tv3o74SMjzmfyF4Uoi@}c7t@Y?al_K@aXBR1hf$R-GD9fkT4LTBiReyfV#mrTe zFWmV6k)VwCDgpGl$+chYV&&<4- zrE-?oF>APecI|}W7UO+Qpt50nf)S1;94 z(C3kPTkMk|R7(n7^nUm@*U7WGb3J!h!j6;;p;~3Dq+`x^M2ov57L|B?f7R9W1qYmz z3XebJK_jPb7uK$~B11LXhaxUTupyI5ZhvoBKq)D1OasVKjIO-(j7Pq1Q%(_Tos=0Z#)XoFW7M=<5#DU2TqGjaHS5sxhC6sEII2}45v36bOa0!oA)w3`mw${>xxv7xl~q=#q*)TKyv8; zaF@^5z-LIciCuGDSj;$;-3KPv+V>*@k^_Kq+<$4rH9GYYn^P0>v_{X-En?h^cK}D< zGQ_9&g751?iM@(=lV3s|MMwh~7!kPnllVC6;?b$5;n`gQ>>xb#4Fd~uxI4L1JKEMM zsmOu$?RZNjGEaw}Zv*wYKal0`e+`n+V!m!&?42#T{Wb@u-oUf|2c>P+ojkOiRk@%q zd>1mK=4r>5^B8a!gHfJ<_(7x{T7dX%-fOnDJGjI!Dx4Gz)%GPy_pz)fKcRT}7afjb^ zdb^j#5N#=nU$atIDx9G0QDP{jwG0Cr+&3a9%*ScZ1m0_OgXTrEzd(g-L6N=m>Mv5n zDLjgX>UZmA%yCKkqk9)6`06d3>p2*6|Nb&X#`XsX0)PKdJ@dhho^DpO?@wEjCJ|q9 zwPu6izx%*hj6shaHYacMje3ixJU906Sm;{K(WHVWj`EkWv?=I#cqlP{)U?aG*Q zb%55PQsW+hb{JaG1U5gYdsC38+I0QkFN8QA)eYT`WdpC*otV)S2pq%ZO6RpcWK9jBi8r{lzB-K5?zeslWWkl2wnIGDBAl(81 z{z3GTJHPiPY%Hd)?3R*!?a2o{A1itw5}IOAuR5}5;R0Qc9A-ln{ac!6 z3F)}0zhpawQ12KqjY>Ng>sf=qv#di){=devrJmgPSGy6od(PQsNLRX)j`%sAn~kZr zi>fyMGO<VUn}qI|U<5gW8rQbeOW#x&$6BZqSjqlUHc8LHa;; zs<(P~HbR9c(raU*kOae_Rg~ZCL=3kPLnvPoh8@{Zj@fqX&+k;Xb8@H1XoqfVKT)8` zd(0^BC^zrN+n?u&BSLGx3fR*d&BoWAj37;_gL{h zJ3(2Fzuywy7J}#LCGXJQfj{3HpWL@K*!I$E?KKjZyqxPg;^@3~6PS%z;3@}qFdLQQ zUvB8Deys`7<1*aMZCZqS06U#zQ}bavEMAH!4JjDiS9q=5jIaa$89~y1i;vTR%P#B2 zg)u!95cyrgsU~o9ig@UC^;0%IMwKokSP11XrT#n`qe>L1xM}wm=8mJZDQ5VPu2a9_ z9zw@j;3|`=_jP|0qNEizbvaAIwN%d1DvmDxVD}`@2MjL`p$va@c`4)1Z71mFeuC(Y zxbiPk$VN|>OO3YX?WN#-&MXN&*N>QSv9Z^#3OxKdwpk*w6dSGywjifmq^v?QZfMjo zR~^%Hj-b(AWbm@lRYA|BCWKOCaZ}IHQ1NfdJ=f78trX2*&BXY$!Ml44ahu_?#RA!jHU>t+=$b%!}6I)cZjTU=NiX;z~B8?U!c?ef9GHPVUXBu*SUJvVzW!?IQJ zcaFcFC4Brh(~0Pf%;luooOjYitn+e~9*zxLZs00YstbSTPWio1b?+IO2TprOaiH@x zNL-EB^0u3^`A?G~)R&Pk+*+!mz_s*`iIFO3V*NVE`ITVQ+36%Woxb+@@&VbZm_c;qS?A0Y-%-QwYf4Pu0e)zy9V##8lIN*Y;P*gr zRH2|t4+EUYuY0w^SH51jWwH&qKK+e>1OLU~ooL> zMu~h<*6}t3k5AkkcLLkQ(pL4H4_)8SX0&#^H6REHVRgd*6+9cooNvkQ}abAye2 z8q^+dY8y4>NVH1K}#P3mZ!T+P>1iyG}Uc`lbsJrnVMt&JL{>qATH z`Q>WUn=(~f72iz^)wDbYjwSh^;faqSQ3fffS71Q>b@5JJ%?LH4ye1>#`=N3i=dDGr zGVL(7x+dYE3|8O2)nV)FCgJ!(V3#HQ|#zo!K^sGaa` zeL}H21&p{q*9&WRcxSxeFKmedIp5HBH}#w~4PEiNiqa*pw@lh)CG6m>bRk)CZxVj1 zbk2mcM%1_x{2J8inwwnor0fL!eQuqv`SNz$9pFUD6n-z(mt^e(A7852xXJ#Q13rdo zpWG?14O@65%wB8%#;mz`GbwLi^=NjTRJ$hOqxV-8x?9OM5P7W7E>vg5(9U9TO|@j6YTEPbhPwM1jlZaXnBrD-0DO_q7fB z+59dJe2x#d_S~~NnX%mmJ2A8DWRGd--&6=*Y)D!S4l5eVvkrIqSL`GO0ql)1v8v{c zI7OFrv7UnisM+f{1y1GI1Zi}hXLkMz6>%rn(>my^U(~XhefAkxqozcl4vH3`+?*9Y zMX0dj)+(wIm8%nn#;ZNojox_PQeGS-1??fn>VE>m(k=kE!z|i&yc*Q*`yxBc_O~95 zao3ylhew{@bElwqdy0**X0b>LF0` zl1LxfUp132*arMyZj<`x`h?r?0j&BDVd!;cZ1HxnsAqEXgdM$B#Rn`;SLm$!g?%BR zILsi@LmPNfl>Wd#p&h3B=U@ei=R}7t;3&Wg$rH*%rQGfarjQ-i zM{UQKOn3ilRrK4T-1*h;5+d6!tNK#>9khB{q200?qy)2-g6f~7;60c;`zB&-e%xS_ zlrTAD=-D%YRsi=aaBG`nl0W_%(BNAIm~$2{&yS|_B2P!^PM+s&G3gg!&o^ZkRFn-; zNAm3i4Gqx4TR4@8-en<_Qw|tZv&Cg1=Kt+dJs$4(43dP9-NRE#Ns}+dOLy|TH8x#e z`%G*{J7=xl@s{2|jhNCh(AFx$Ioh4#U+x;Dzt)4lfE|ZEtNdoL1g3Y08N21f%Ldlm zNs`A3MB5ciT@h+F$8|D0rzhc8N*$`ENl zn0qhPHR!S4DZnAgaeT5?`~&Q-6JIzk&6JA0#xEvLCBPth#~o6omTR^*3L%yFWTP!A zMlCuPb-Z`5*F8e>Jtir(I3(LIlkAf5G69gxN+DcNju1Jd^R-3BKa>yTNu6KyuE=Q0 z2leQ25T7a@S6^mie`r!js%za~>1{TS%wGFr*f#z^eLlKrWDzR4P*>mo zGx+Et1taa1n1!`g4YrJ)q#Txdpx2xL4*nG4x0{r5sfI}ieB!KQmk_H!x?Y#7tX_~tWkuZfJ2539 zVn#M8Vw^0c)R;proeXa9eSE%`a>DxzE|=_)`+tU@f`|rI!>e|`YC=L}4Z^7jutUn0 z?=u*|AFUujBsR6(ks7lLq1*(H>ODjf`)`xu^Pl+eqA+OtvoX?T^#6H}l#?4+Nt9p4 y=^|7^R#sc4LtqO%PAcjV9sm8644UjGAs^I;nR literal 0 HcmV?d00001 diff --git a/shell/platform/embedder/fixtures/dpr_xform.png b/shell/platform/embedder/fixtures/dpr_xform.png new file mode 100644 index 0000000000000000000000000000000000000000..3c6928c2c121954d7489283f7b9377b0ae0cec46 GIT binary patch literal 127639 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�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 literal 0 HcmV?d00001 diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 44cc8306f7368..2d5ae8f535b85 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -437,10 +437,13 @@ void verify_b141980393() { void can_display_platform_view_with_pixel_ratio() { window.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); - builder.pushOffset(0.0, 20.0); + builder.pushOffset(0.0, 0.0); // base + builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); + builder.pushOffset(0.0, 20.0); // offset builder.addPlatformView(42, width: 400.0, height: 280.0); - builder.addPicture(Offset(0.0, 0.0), CreateSimplePicture()); - builder.pop(); + builder.pop(); // offset + builder.addPicture(Offset(0.0, 0.0), CreateColoredBox(Color.fromARGB(128, 255, 0, 0), Size(400.0, 300.0))); + builder.pop(); // base window.render(builder.build()); }; window.scheduleFrame(); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 3a7697e3eb2a9..13d0453a1603d 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -2587,7 +2587,14 @@ TEST_F(EmbedderTest, context.GetCompositor().SetRenderTargetType( EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); - fml::AutoResetWaitableEvent latch; + sk_sp renderered_scene; + fml::CountDownLatch latch(2); + + context.SetNextSceneCallback([&](auto image) { + renderered_scene = std::move(image); + latch.CountDown(); + }); + context.GetCompositor().SetNextPresentCallback( [&](const FlutterLayer** layers, size_t layers_count) { ASSERT_EQ(layers_count, 3u); @@ -2642,7 +2649,7 @@ TEST_F(EmbedderTest, ASSERT_EQ(*layers[2], layer); } - latch.Signal(); + latch.CountDown(); }); auto engine = builder.LaunchEngine(); @@ -2650,14 +2657,16 @@ TEST_F(EmbedderTest, // Send a window metrics events so frames may be scheduled. FlutterWindowMetricsEvent event = {}; event.struct_size = sizeof(event); - event.width = 400; - event.height = 300; + event.width = 400 * 2.0; + event.height = 300 * 2.0; event.pixel_ratio = 2.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); + + ASSERT_TRUE(ImageMatchesFixture("dpr_noxform.png", renderered_scene)); } TEST_F( @@ -2677,8 +2686,14 @@ TEST_F( SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0); context.SetRootSurfaceTransformation(root_surface_transformation); + sk_sp renderered_scene; + fml::CountDownLatch latch(2); + + context.SetNextSceneCallback([&](auto image) { + renderered_scene = std::move(image); + latch.CountDown(); + }); - fml::AutoResetWaitableEvent latch; context.GetCompositor().SetNextPresentCallback( [&](const FlutterLayer** layers, size_t layers_count) { ASSERT_EQ(layers_count, 3u); @@ -2733,7 +2748,7 @@ TEST_F( ASSERT_EQ(*layers[2], layer); } - latch.Signal(); + latch.CountDown(); }); auto engine = builder.LaunchEngine(); @@ -2741,14 +2756,16 @@ TEST_F( // Send a window metrics events so frames may be scheduled. FlutterWindowMetricsEvent event = {}; event.struct_size = sizeof(event); - event.width = 400; - event.height = 300; + event.width = 400 * 2.0; + event.height = 300 * 2.0; event.pixel_ratio = 2.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); + + ASSERT_TRUE(ImageMatchesFixture("dpr_xform.png", renderered_scene)); } TEST_F(EmbedderTest, CanUpdateLocales) { From 8ebb318401344793daa10c3bec97c34891cf7cc8 Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Wed, 6 Nov 2019 11:41:36 -0800 Subject: [PATCH 037/591] Revert "Issues/39832 reland (#13642)" (#13720) This reverts commit 1bfb928e071674a21779cee94908fbcae1c2e657. --- lib/ui/window.dart | 16 +++-- lib/web_ui/lib/src/ui/window.dart | 13 ++-- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- .../FlutterActivityAndFragmentDelegate.java | 2 - .../systemchannels/LifecycleChannel.java | 4 -- ...lutterActivityAndFragmentDelegateTest.java | 11 ---- .../ios/framework/Source/FlutterEngine.mm | 24 +++---- .../ScenariosTests/AppLifecycleTests.m | 66 ------------------- 9 files changed, 29 insertions(+), 111 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 954640ab79167..3bd28a248bbba 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -171,16 +171,18 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// Android apps in this state should assume that they may enter the + /// [suspending] state at any time. paused, - /// The application is still hosted on a flutter engine but is detached from - /// any host views. + /// The application will be suspended momentarily. + /// + /// When the application is in this state, the engine will not call the + /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. /// - /// When the application is in this state, the engine is running without - /// a view. It can either be in the progress of attaching a view when engine - /// was first initializes, or after the view being destroyed due to a Navigator - /// pop. - detached, + /// On iOS, this state is currently unused. + suspending, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 731874e6dff97..58c82410de7c6 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -73,13 +73,18 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// Android apps in this state should assume that they may enter the + /// [suspending] state at any time. paused, - /// The application is detached from view. + /// The application will be suspended momentarily. + /// + /// When the application is in this state, the engine will not call the + /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. /// - /// When the application is in this state, the engine is running without - /// a platform UI. - detached, + /// On iOS, this state is currently unused. + suspending, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 5ade4672cbece..c0ee45d762e81 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -120,7 +120,7 @@ class RuntimeController final : public WindowClient { std::string variant_code; std::vector locale_data; std::string user_settings_data = "{}"; - std::string lifecycle_state = "AppLifecycleState.detached"; + std::string lifecycle_state; bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 77b6577271511..38b034d52d0d0 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -311,7 +311,7 @@ bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { const auto& data = message->data(); std::string state(reinterpret_cast(data.data()), data.size()); if (state == "AppLifecycleState.paused" || - state == "AppLifecycleState.detached") { + state == "AppLifecycleState.suspending") { activity_running_ = false; StopAnimator(); } else if (state == "AppLifecycleState.resumed" || diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 6cebfcbef6232..eddacf2b25531 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -461,8 +461,6 @@ void onDetach() { platformPlugin = null; } - flutterEngine.getLifecycleChannel().appIsDetached(); - // Destroy our FlutterEngine if we're not set to retain it. if (host.shouldDestroyEngineWithHost()) { flutterEngine.destroy(); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java index cd244ff8251e1..abc6323907d0b 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java @@ -39,8 +39,4 @@ public void appIsPaused() { channel.send("AppLifecycleState.paused"); } - public void appIsDetached() { - Log.v(TAG, "Sending AppLifecycleState.detached message."); - channel.send("AppLifecycleState.detached"); - } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index d335fa5064dbc..154c59f173191 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -85,21 +85,18 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter. delegate.onResume(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is paused, an inactive message should have been sent to Flutter. delegate.onPause(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is stopped, a paused message should have been sent to Flutter. // Notice that Flutter uses the term "paused" in a different way, and at a different time @@ -108,14 +105,6 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); - - // When activity detaches, a detached message should have been sent to Flutter. - delegate.onDetach(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); - verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsDetached(); } @Test diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index a9f20708f15b5..83551d2aed042 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -175,23 +175,18 @@ - (void)ensureSemanticsEnabled { - (void)setViewController:(FlutterViewController*)viewController { FML_DCHECK(self.iosPlatformView); - _viewController = - viewController ? [viewController getWeakPtr] : fml::WeakPtr(); + _viewController = [viewController getWeakPtr]; self.iosPlatformView->SetOwnerViewController(_viewController); [self maybeSetupPlatformViewChannels]; - if (viewController) { - __block FlutterEngine* blockSelf = self; - self.flutterViewControllerWillDeallocObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc - object:viewController - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification* note) { - [blockSelf notifyViewControllerDeallocated]; - }]; - } else { - self.flutterViewControllerWillDeallocObserver = nil; - } + __block FlutterEngine* blockSelf = self; + self.flutterViewControllerWillDeallocObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc + object:viewController + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* note) { + [blockSelf notifyViewControllerDeallocated]; + }]; } - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { @@ -206,7 +201,6 @@ - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { } - (void)notifyViewControllerDeallocated { - [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"]; if (!_allowHeadlessExecution) { [self destroyContext]; } else { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m index 7f8f6902bde2a..3646e3fd5ee40 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m @@ -225,70 +225,4 @@ - (void)testVisibleFlutterViewControllerRespondsToApplicationLifecycle { [engine setViewController:nil]; } -- (void)testFlutterViewControllerDetachingSendsApplicationLifecycle { - XCTestExpectation* engineStartedExpectation = [self expectationWithDescription:@"Engine started"]; - - // Let the engine finish booting (at the end of which the channels are properly set-up) before - // moving onto the next step of showing the next view controller. - ScreenBeforeFlutter* rootVC = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:^void() { - [engineStartedExpectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:5 handler:nil]; - - UIApplication* application = UIApplication.sharedApplication; - application.delegate.window.rootViewController = rootVC; - FlutterEngine* engine = rootVC.engine; - - NSMutableArray* lifecycleExpectations = [NSMutableArray arrayWithCapacity:10]; - - // Expected sequence from showing the FlutterViewController is inactive and resumed. - [lifecycleExpectations addObjectsFromArray:@[ - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" - forStep:@"showing a FlutterViewController"], - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed" - forStep:@"showing a FlutterViewController"] - ]]; - // At the end of Flutter VC, we want to make sure it deallocs and sends detached signal. - // Using autoreleasepool will guarantee that. - FlutterViewController* flutterVC; - @autoreleasepool { - flutterVC = [rootVC showFlutter]; - [engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) { - if (lifecycleExpectations.count == 0) { - XCTFail(@"Unexpected lifecycle transition: %@", message); - return; - } - XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0]; - if (![[nextExpectation expectedLifecycle] isEqualToString:message]) { - XCTFail(@"Expected lifecycle %@ but instead received %@", - [nextExpectation expectedLifecycle], message); - return; - } - - [nextExpectation fulfill]; - [lifecycleExpectations removeObjectAtIndex:0]; - }]; - - [self waitForExpectations:lifecycleExpectations timeout:5]; - - // Starts dealloc flutter VC. - [lifecycleExpectations addObjectsFromArray:@[ - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" - forStep:@"detaching a FlutterViewController"], - [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.paused" - forStep:@"detaching a FlutterViewController"], - [[XCAppLifecycleTestExpectation alloc] - initForLifecycle:@"AppLifecycleState.detached" - forStep:@"detaching a FlutterViewController"] - ]]; - [flutterVC dismissViewControllerAnimated:NO completion:nil]; - flutterVC = nil; - } - [self waitForExpectations:lifecycleExpectations timeout:5]; - - [engine.lifecycleChannel setMessageHandler:nil]; - [engine setViewController:nil]; -} - @end From bc7a007978ba5db8d343988d50348335d3c45a98 Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 6 Nov 2019 13:01:54 -0800 Subject: [PATCH 038/591] Package fml_unittests in a .far file for fml unit tests on Fuchsia (#13471) Add fml_tests target for the fml unit tests on Fuchsia --- BUILD.gn | 5 +++++ fml/BUILD.gn | 32 ++++++++++++++++++++++----- testing/fuchsia/meta/fuchsia_test.cmx | 15 +++++++++++++ tools/fuchsia/fuchsia_archive.gni | 26 +++++++++++++++++++--- 4 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 testing/fuchsia/meta/fuchsia_test.cmx diff --git a/BUILD.gn b/BUILD.gn index 1cffa58132c4c..5fd4f5482f601 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -91,6 +91,11 @@ group("flutter") { ] } } + + # Fuchsia currently only supports a subset of our unit tests + if (is_fuchsia) { + public_deps += [ "$flutter_root/fml:fml_tests" ] + } } config("config") { diff --git a/fml/BUILD.gn b/fml/BUILD.gn index bc66d23626633..75ff7aeae8c08 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -5,6 +5,10 @@ import("//build/fuchsia/sdk.gni") import("$flutter_root/testing/testing.gni") +if (is_fuchsia) { + import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") +} + source_set("fml") { sources = [ "base32.cc", @@ -199,26 +203,32 @@ executable("fml_unittests") { sources = [ "base32_unittest.cc", "command_line_unittest.cc", - "file_unittest.cc", - "gpu_thread_merger_unittests.cc", "memory/ref_counted_unittest.cc", "memory/weak_ptr_unittest.cc", "message_loop_task_queues_merge_unmerge_unittests.cc", - "message_loop_task_queues_unittests.cc", - "message_loop_unittests.cc", "message_unittests.cc", "paths_unittests.cc", "platform/darwin/string_range_sanitization_unittests.mm", - "synchronization/count_down_latch_unittests.cc", "synchronization/semaphore_unittest.cc", "synchronization/waitable_event_unittest.cc", "thread_local_unittests.cc", - "thread_unittests.cc", "time/time_delta_unittest.cc", "time/time_point_unittest.cc", "time/time_unittest.cc", ] + # TODO(gw280): Figure out why these tests don't work currently on Fuchsia + if (!is_fuchsia) { + sources += [ + "file_unittest.cc", + "gpu_thread_merger_unittests.cc", + "message_loop_task_queues_unittests.cc", + "message_loop_unittests.cc", + "synchronization/count_down_latch_unittests.cc", + "thread_unittests.cc", + ] + } + deps = [ ":fml_fixtures", "$flutter_root/fml", @@ -227,6 +237,16 @@ executable("fml_unittests") { ] } +if (is_fuchsia) { + fuchsia_test_archive("fml_tests") { + deps = [ + ":fml_unittests", + ] + + binary = "fml_unittests" + } +} + executable("fml_benchmarks") { testonly = true diff --git a/testing/fuchsia/meta/fuchsia_test.cmx b/testing/fuchsia/meta/fuchsia_test.cmx new file mode 100644 index 0000000000000..fedcb77867acb --- /dev/null +++ b/testing/fuchsia/meta/fuchsia_test.cmx @@ -0,0 +1,15 @@ +{ + "program": { + "binary": "bin/app" + }, + "sandbox": { + "features": [ + "vulkan", + "deprecated-ambient-replace-as-executable" + ], + "services": [ + "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.sys.Launcher" + ] + } +} diff --git a/tools/fuchsia/fuchsia_archive.gni b/tools/fuchsia/fuchsia_archive.gni index a5c0a0cb05917..3d0f0707fc9e5 100644 --- a/tools/fuchsia/fuchsia_archive.gni +++ b/tools/fuchsia/fuchsia_archive.gni @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("$flutter_root/tools/fuchsia/common_libs.gni") import("$flutter_root/tools/fuchsia/fuchsia_debug_symbols.gni") # Creates a Fuchsia archive (.far) file using PM from the Fuchsia SDK. @@ -54,13 +55,17 @@ template("fuchsia_archive") { copy_outputs += [ "$far_base_dir/lib/${lib.name}" ] } - meta_dir = pkg.meta_dir - cmx_target = "$pkg_target_name.copy_cmx" + if (defined(invoker.cmx_file)) { + cmx_file = invoker.cmx_file + } else { + cmx_file = "${pkg.meta_dir}/${pkg_target_name}.cmx" + } + copy("$cmx_target") { sources = [ - "${meta_dir}/${pkg_target_name}.cmx", + "$cmx_file", ] outputs = [ "$far_base_dir/meta/{{source_file_part}}", @@ -123,3 +128,18 @@ template("fuchsia_archive") { testonly = pkg_testonly } } + +template("fuchsia_test_archive") { + fuchsia_archive(target_name) { + testonly = true + libraries = common_libs + + assert(defined(invoker.deps), "package must define deps") + + deps = invoker.deps + binary = invoker.binary + + meta_dir = "$flutter_root/testing/fuchsia/meta" + cmx_file = "$meta_dir/fuchsia_test.cmx" + } +} From 2a8f6dca6006bc110cc813d80f5a158abb795451 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 6 Nov 2019 12:47:05 -0800 Subject: [PATCH 039/591] Roll src/third_party/dart 462a448ac8..c154677699 (2 commits) dart-lang/sdk@c154677699 [vm] Handle trivial initializers for late fields dart-lang/sdk@46ae3e4e7a [vm] not remove port if socket was listening by other isolates --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index eac00081e2a4b..f21aa03da9651 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '462a448ac8ef12239407bedf5fe6972261ccad44', + 'dart_revision': 'c1546776990efaff496b3286fd14528ff0ac17c3', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index d6d294112939c..81b0516ac8586 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 02e7844a72324090905134c61f4f48fe +Signature: 9eca262fe53992c95ca532d5e2e05c78 UNUSED LICENSES: From abaa85a394c898f232fb8898b052d95b38a51310 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 6 Nov 2019 13:20:23 -0800 Subject: [PATCH 040/591] [web] Proper support for text field's obscureText (#13722) --- .../src/engine/text_editing/text_editing.dart | 3 +++ lib/web_ui/test/text_editing_test.dart | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index fe4df00071b65..8484483f6f46a 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -369,6 +369,9 @@ class TextEditingElement { void _initDomElement(InputConfiguration inputConfig) { domElement = inputConfig.inputType.createDomElement(); inputConfig.inputType.configureDomElement(domElement); + if (inputConfig.obscureText) { + domElement.setAttribute('type', 'password'); + } _setStaticStyleAttributes(domElement); owner._setDynamicStyleAttributes(domElement); domRenderer.glassPaneElement.append(domElement); diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index 902d4dd09116d..669ff6eb972ef 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -85,6 +85,7 @@ void main() { // Now the editing element should have focus. expect(document.activeElement, input); expect(editingElement.domElement, input); + expect(input.getAttribute('type'), null); // Input is appended to the glass pane. expect(domRenderer.glassPaneElement.contains(editingElement.domElement), @@ -99,6 +100,25 @@ void main() { expect(document.activeElement, document.body); }); + test('Knows how to create password fields', () { + final InputConfiguration config = InputConfiguration( + inputType: EngineInputType.text, + inputAction: 'TextInputAction.done', + obscureText: true, + ); + editingElement.enable( + config, + onChange: trackEditingState, + onAction: trackInputAction, + ); + expect(document.getElementsByTagName('input'), hasLength(1)); + final InputElement input = document.getElementsByTagName('input')[0]; + expect(editingElement.domElement, input); + expect(input.getAttribute('type'), 'password'); + + editingElement.disable(); + }); + test('Can read editing state correctly', () { editingElement.enable( singlelineConfig, From 3701083bbde8257411500909f3c6553247bbd080 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 17:15:43 -0500 Subject: [PATCH 041/591] Roll src/third_party/skia 6790423f84ef..714f8cc3ff4b (8 commits) (#13724) https://skia.googlesource.com/skia.git/+log/6790423f84ef..714f8cc3ff4b git log 6790423f84ef..714f8cc3ff4b --date=short --no-merges --format='%ad %ae %s' 2019-11-06 mtklein@google.com add vcmpps 2019-11-06 herb@google.com Improve the dynamic range of SkPackedGlyphID ctor 2019-11-06 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-06 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-06 mtklein@google.com unnest matrix multiply 2019-11-06 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-06 jvanverth@google.com Fix issue with RenderPipelineDescriptor creation 2019-11-06 mtklein@google.com JIT splat(0) as xor Created with: gclient setdep -r src/third_party/skia@714f8cc3ff4b If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f21aa03da9651..2c5f9a510e260 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6790423f84efd3d03666e7cb34b9eefc8d898d8f', + 'skia_revision': '714f8cc3ff4be40ced5183a171816bef30665fbd', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index c8cfb05f49d65..0de327650e619 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1cbd9233e4bef369fa7a4ee0ef232811 +Signature: 606561d37591bed08fd0041f615674c7 UNUSED LICENSES: From 34a40cfbfd369544cbee9d19f212fbbe06e739c2 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 6 Nov 2019 15:37:54 -0800 Subject: [PATCH 042/591] Roll src/third_party/dart c154677699..bdd9e74afc (3 commits) dart-lang/sdk@bdd9e74afc Only distribute a single libtensorflowlite_c per distribution dart-lang/sdk@c6919a6653 Add a test for issue 39254 dart-lang/sdk@89dd099cad Update TypeParameterElement.== as discussed, add tests. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 2c5f9a510e260..c6ea51b60a55e 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'c1546776990efaff496b3286fd14528ff0ac17c3', + 'dart_revision': 'bdd9e74afc1681ca7e43945e3547641321486ab7', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From d68c00947e6ee6bdca046c070a4307846996f82f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 19:42:06 -0500 Subject: [PATCH 043/591] Roll fuchsia/sdk/core/mac-amd64 from 6Du8E... to r1tS4... (#13726) Roll fuchsia/sdk/core/mac-amd64 from 6Du8E... to r1tS4... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c6ea51b60a55e..e9e94b6b2925e 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '6Du8EyM7O_xsuN2tkEpl-MyD4iDAs4h4wsnw3MgU-2EC' + 'version': 'r1tS4axX0-ckDY7aEpRBJO9H_DHKR9-FLwp7D4qF9VoC' } ], 'condition': 'host_os == "mac"', From c448d39d53079b62383de761f384024c700f6c0f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 6 Nov 2019 21:09:35 -0500 Subject: [PATCH 044/591] Roll src/third_party/skia 714f8cc3ff4b..f66ec5d69d04 (3 commits) (#13729) https://skia.googlesource.com/skia.git/+log/714f8cc3ff4b..f66ec5d69d04 git log 714f8cc3ff4b..f66ec5d69d04 --date=short --no-merges --format='%ad %ae %s' 2019-11-07 rosasco@google.com No GL support on Fuchsia. 2019-11-06 csmartdalton@google.com Rename stencil face terminology to clockwise/counter-clockwise 2019-11-06 herb@google.com Allow more sub-pixel position bits Created with: gclient setdep -r src/third_party/skia@f66ec5d69d04 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e9e94b6b2925e..c6bef69b9b8ec 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '714f8cc3ff4be40ced5183a171816bef30665fbd', + 'skia_revision': 'f66ec5d69d04a06c5415e6db03d8c07fe60dbc2a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0de327650e619..087a99831aea5 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 606561d37591bed08fd0041f615674c7 +Signature: db7828c99f2f6e82257e2d4c49201440 UNUSED LICENSES: From 9113543f0562a53ffc9209f18f0198b7ccb67d3b Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 6 Nov 2019 18:43:03 -0800 Subject: [PATCH 045/591] Roll src/third_party/dart bdd9e74afc..4b9638aaa1 (7 commits) dart-lang/sdk@4b9638aaa1 [VM/nnbd] Use a single byte in snapshot to write flags and nullability of types. dart-lang/sdk@f6efefc09c [dartdevc] Move the custom type test logic to patch files dart-lang/sdk@1994c5c323 Improve error reporting in ID tests dart-lang/sdk@e93bf30171 fix deprecated messages for implicit getters/setters dart-lang/sdk@b647e327aa Fix an assertion to support migration of part files dart-lang/sdk@335b85fea1 [nnbd_migration] for tool/trial_migration, accept interest argument. dart-lang/sdk@3e3a926690 Fix spread and Set/Map literal disambiguation when NNBD. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c6bef69b9b8ec..c6abac0e52b42 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'bdd9e74afc1681ca7e43945e3547641321486ab7', + 'dart_revision': '4b9638aaa17203b4e4f9ceb8492faf6a11301999', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 81b0516ac8586..e861d5dcdbf5f 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 9eca262fe53992c95ca532d5e2e05c78 +Signature: cb6a78a49c674537ebcffa756ccdea6b UNUSED LICENSES: From ddceed5f7af13531ec38d522916736c0e42e4619 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Thu, 7 Nov 2019 13:34:55 -0800 Subject: [PATCH 046/591] Cleanup obsolete --strong option of front-end server (#13735) --- testing/run_tests.py | 1 - testing/scenario_app/compile_ios_jit.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/run_tests.py b/testing/run_tests.py index 686430c1c82eb..d345bb887c0ac 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -148,7 +148,6 @@ def SnapshotTest(build_dir, dart_file, kernel_file_output, verbose_dart_snapshot '--sdk-root', flutter_patched_sdk, '--incremental', - '--strong', '--target=flutter', '--packages', test_packages, diff --git a/testing/scenario_app/compile_ios_jit.sh b/testing/scenario_app/compile_ios_jit.sh index fc27b4854b1fc..6b3f25c3359b5 100755 --- a/testing/scenario_app/compile_ios_jit.sh +++ b/testing/scenario_app/compile_ios_jit.sh @@ -39,7 +39,7 @@ echo "Compiling to kernel..." "$HOST_TOOLS/dart" \ "$HOST_TOOLS/gen/frontend_server.dart.snapshot" \ --sdk-root "$HOST_TOOLS/flutter_patched_sdk" \ - --strong --target=flutter \ + --target=flutter \ --no-link-platform \ --output-dill "$OUTDIR/App.framework/flutter_assets/kernel_blob.bin" \ "${BASH_SOURCE%/*}/lib/main.dart" From 629930e8887c175611f4905a12fdc38105ad582e Mon Sep 17 00:00:00 2001 From: liyuqian Date: Thu, 7 Nov 2019 14:05:43 -0800 Subject: [PATCH 047/591] Prefer SchedulerBinding.addTimingsCallback (#13728) This doc update solves our TODO in https://github.com/flutter/flutter/pull/43676/files#diff-6a35a9a692c9af7473ba1a0a74c979c8R223 --- lib/ui/window.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 3bd28a248bbba..794b9364f625e 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -69,7 +69,12 @@ enum FramePhase { /// Time-related performance metrics of a frame. /// -/// See [Window.onReportTimings] for how to get this. +/// If you're using the whole Flutter framework, please use +/// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using +/// [Window.onReportTimings] directly because +/// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If +/// [SchedulerBinding] is unavailable, then see [Window.onReportTimings] for how +/// to get this. /// /// The metrics in debug mode (`flutter run` without any flags) may be very /// different from those in profile and release modes due to the debug overhead. @@ -933,6 +938,10 @@ class Window { /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// + /// It's prefered to use [SchedulerBinding.addTimingsCallback] than to use + /// [Window.onReportTimings] directly because + /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. + /// /// This can be used to see if the application has missed frames (through /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high /// latencies (through [FrameTiming.totalSpan]). From 306dffa1748f103c3a6123c83c29926ed8b7a2c6 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 7 Nov 2019 17:07:49 -0500 Subject: [PATCH 048/591] Roll fuchsia/sdk/core/mac-amd64 from r1tS4... to H_5HL... (#13734) Roll fuchsia/sdk/core/mac-amd64 from r1tS4... to H_5HL... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c6abac0e52b42..b43a3f5ab457a 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'r1tS4axX0-ckDY7aEpRBJO9H_DHKR9-FLwp7D4qF9VoC' + 'version': 'H_5HL34zOj71EeF7wJSU-U66_qIO1F0bmJ9xYAnX-dwC' } ], 'condition': 'host_os == "mac"', From 7ed9c9fc5885fbf76cdda22331cb07f1da75ccf5 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 7 Nov 2019 17:11:16 -0500 Subject: [PATCH 049/591] Roll src/third_party/skia f66ec5d69d04..345a2735e2c8 (1 commits) (#13732) https://skia.googlesource.com/skia.git/+log/f66ec5d69d04..345a2735e2c8 git log f66ec5d69d04..345a2735e2c8 --date=short --no-merges --format='%ad %ae %s' 2019-11-07 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 215bc7949b35..27a3d31d7a9d (2 commits) Created with: gclient setdep -r src/third_party/skia@345a2735e2c8 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b43a3f5ab457a..089790bcc70b6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f66ec5d69d04a06c5415e6db03d8c07fe60dbc2a', + 'skia_revision': '345a2735e2c8e3188f01938cdc62848bf64cf97f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 087a99831aea5..221f81e677a27 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: db7828c99f2f6e82257e2d4c49201440 +Signature: aba11c4957d170f239906fde5d332435 UNUSED LICENSES: From 6fd58edd3e9cf15d2a29442ae86804530dd171a5 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 7 Nov 2019 22:54:10 +0000 Subject: [PATCH 050/591] Removed scary experimental warnings for new embedding. (#44314) (#13738) --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 2 -- .../android/io/flutter/embedding/engine/FlutterJNI.java | 3 --- .../android/io/flutter/embedding/engine/FlutterShellArgs.java | 3 --- .../io/flutter/embedding/engine/dart/DartMessenger.java | 3 --- .../flutter/embedding/engine/dart/PlatformMessageHandler.java | 3 +-- 5 files changed, 1 insertion(+), 13 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index f8580519d5603..ca03bc9419699 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -37,8 +37,6 @@ /** * A single Flutter execution environment. *

- * WARNING: THIS CLASS IS CURRENTLY EXPERIMENTAL. USE AT YOUR OWN RISK. - *

* The {@code FlutterEngine} is the container through which Dart code can be run in an Android * application. *

diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 2bd663f03106a..9ec51f7d8e48a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -33,9 +33,6 @@ /** * Interface between Flutter embedding's Java code and Flutter engine's C/C++ code. * - * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. - * IF YOU USE IT, WE WILL BREAK YOU. - * * Flutter's engine is built with C/C++. The Android Flutter embedding is responsible for * coordinating Android OS events and app user interactions with the C/C++ engine. Such coordination * requires messaging from an Android app in Java code to the C/C++ engine code. This diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java index 544411b275742..65c25d5a9f5f9 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java @@ -13,9 +13,6 @@ /** * Arguments that can be delivered to the Flutter shell when it is created. *

- * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. - * IF YOU USE IT, WE WILL BREAK YOU. - *

* The term "shell" refers to the native code that adapts Flutter to different platforms. Flutter's * Android Java code initializes a native "shell" and passes these arguments to that native shell * when it is initialized. See {@link io.flutter.view.FlutterMain#ensureInitializationComplete(Context, String[])} diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java b/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java index 3d23b4fd09179..f403ada727478 100644 --- a/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java +++ b/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java @@ -20,9 +20,6 @@ /** * Message conduit for 2-way communication between Android and Dart. *

- * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. - * IF YOU USE IT, WE WILL BREAK YOU. - *

* See {@link BinaryMessenger}, which sends messages from Android to Dart *

- * TODO(mattcarroll): explain ActivityAware when it's added to the new plugin API surface. + * Some plugins may require access to the {@code Activity} that is displaying a Flutter experience, + * or may need to react to {@code Activity} lifecycle events, e.g., {@code onCreate()}, + * {@code onStart()}, {@code onResume()}, {@code onPause()}, {@code onStop()}, {@code onDestroy()}. + * Any such plugin should implement + * {@link io.flutter.embedding.engine.plugins.activity.ActivityAware} in addition to implementing + * {@code FlutterPlugin}. {@code ActivityAware} provides callback hooks that expose access to an + * associated {@code Activity} and its {@code Lifecycle}. All plugins must respect the possibility + * that a Flutter experience may never be associated with an {@code Activity}, e.g., when Flutter + * is used for background behavior. Additionally, all plugins must respect that a {@code Activity}s + * may come and go over time, thus requiring plugins to cleanup resources and recreate those + * resources as the {@code Activity} comes and goes. */ public interface FlutterPlugin { diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java index 028287e06d09e..ee3196835928a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java @@ -14,6 +14,10 @@ /** * Binding that gives {@link ActivityAware} plugins access to an associated {@link Activity} and * the {@link Activity}'s lifecycle methods. + *

+ * To obtain an instance of an {@code ActivityPluginBinding} in a Flutter plugin, implement the + * {@link ActivityAware} interface. A binding is provided in {@link ActivityAware#onAttachedToActivity(ActivityPluginBinding)} + * and {@link ActivityAware#onReattachedToActivityForConfigChanges(ActivityPluginBinding)}. */ public interface ActivityPluginBinding { diff --git a/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java index 3b1e4a1855ea5..09805ef678fc5 100644 --- a/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java +++ b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java @@ -7,6 +7,10 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.platform.PlatformViewRegistry; import io.flutter.view.FlutterNativeView; import io.flutter.view.FlutterView; @@ -59,6 +63,9 @@ public interface PluginRegistry { /** * Receiver of registrations from a single plugin. + * + *

This registrar is for Flutter's v1 embedding. For instructions on migrating a plugin from + * Flutter's v1 Android embedding to v2, visit http://flutter.dev/go/android-plugin-migration */ interface Registrar { /** @@ -73,30 +80,64 @@ interface Registrar { *

When there is no foreground activity in the application, this * will return null. If a {@link Context} is needed, use context() to * get the application's context.

+ * + *

This registrar is for Flutter's v1 embedding. To access an {@code Activity} from a + * plugin using the v2 embedding, see {@link ActivityPluginBinding#getActivity()}. To obtain + * an instance of an {@link ActivityPluginBinding} in a Flutter plugin, implement the + * {@link ActivityAware} interface. A binding is provided in {@link ActivityAware#onAttachedToActivity(ActivityPluginBinding)} + * and {@link ActivityAware#onReattachedToActivityForConfigChanges(ActivityPluginBinding)}. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration */ Activity activity(); /** * Returns the {@link android.app.Application}'s {@link Context}. + * + *

This registrar is for Flutter's v1 embedding. To access a {@code Context} from a + * plugin using the v2 embedding, see {@link FlutterPlugin.FlutterPluginBinding#getApplicationContext()} + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration */ Context context(); /** - * Returns the active {@link Context}. - * - * @return the current {@link #activity() Activity}, if not null, otherwise the {@link #context() Application}. - */ + * Returns the active {@link Context}. + * + *

This registrar is for Flutter's v1 embedding. In the v2 embedding, there is no + * concept of an "active context". Either use the application {@code Context} or an attached + * {@code Activity}. See {@link #context()} and {@link #activity()} for more details. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * + * @return the current {@link #activity() Activity}, if not null, otherwise the {@link #context() Application}. + */ Context activeContext(); /** * Returns a {@link BinaryMessenger} which the plugin can use for * creating channels for communicating with the Dart side. + * + *

This registrar is for Flutter's v1 embedding. To access a {@code BinaryMessenger} from + * a plugin using the v2 embedding, see {@link FlutterPlugin.FlutterPluginBinding#getBinaryMessenger()} + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration */ BinaryMessenger messenger(); /** * Returns a {@link TextureRegistry} which the plugin can use for * managing backend textures. + * + *

This registrar is for Flutter's v1 embedding. To access a {@code TextureRegistry} from + * a plugin using the v2 embedding, see {@link FlutterPlugin.FlutterPluginBinding#getTextureRegistry()} + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration */ TextureRegistry textures(); @@ -104,12 +145,27 @@ interface Registrar { * Returns the application's {@link PlatformViewRegistry}. * * Plugins can use the platform registry to register their view factories. + * + *

This registrar is for Flutter's v1 embedding. To access a {@code PlatformViewRegistry} + * from a plugin using the v2 embedding, see {@link FlutterPlugin.FlutterPluginBinding#getPlatformViewRegistry()} + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration */ PlatformViewRegistry platformViewRegistry(); /** * Returns the {@link FlutterView} that's instantiated by this plugin's * {@link #activity() activity}. + * + *

This registrar is for Flutter's v1 embedding. The {@link FlutterView} referenced by + * this method does not exist in the v2 embedding. Additionally, no {@code View} is exposed + * to any plugins in the v2 embedding. Platform views can access their containing + * {@code View} using the platform views APIs. If you have a use-case that absolutely + * requires a plugin to access an Android {@code View}, please file a ticket on GitHub. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration */ FlutterView view(); @@ -119,6 +175,8 @@ interface Registrar { * The returned file name can be used to access the asset in the APK * through the {@link android.content.res.AssetManager} API. * + * TODO(mattcarroll): point this method towards new lookup method. + * * @param asset the name of the asset. The name can be hierarchical * @return the filename to be used with {@link android.content.res.AssetManager} */ @@ -129,6 +187,8 @@ interface Registrar { * specified packageName. The returned file name can be used to access * the asset in the APK through the {@link android.content.res.AssetManager} API. * + * TODO(mattcarroll): point this method towards new lookup method. + * * @param asset the name of the asset. The name can be hierarchical * @param packageName the name of the package from which the asset originates * @return the file name to be used with {@link android.content.res.AssetManager} @@ -148,6 +208,12 @@ interface Registrar { * *

Overwrites any previously published value.

* + *

This registrar is for Flutter's v1 embedding. The concept of publishing values from + * plugins is not supported in the v2 embedding. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * * @param value the value, possibly null. * @return this {@link Registrar}. */ @@ -158,6 +224,12 @@ interface Registrar { * calls to {@code Activity#onRequestPermissionsResult(int, String[], int[])} * or {@code android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}. * + *

This registrar is for Flutter's v1 embedding. To listen for permission results in the + * v2 embedding, use {@link ActivityPluginBinding#addRequestPermissionsResultListener(RequestPermissionsResultListener)}. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * * @param listener a {@link RequestPermissionsResultListener} callback. * @return this {@link Registrar}. */ @@ -167,6 +239,12 @@ interface Registrar { * Adds a callback allowing the plugin to take part in handling incoming * calls to {@link Activity#onActivityResult(int, int, Intent)}. * + *

This registrar is for Flutter's v1 embedding. To listen for {@code Activity} results + * in the v2 embedding, use {@link ActivityPluginBinding#addActivityResultListener(ActivityResultListener)}. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * * @param listener an {@link ActivityResultListener} callback. * @return this {@link Registrar}. */ @@ -176,6 +254,12 @@ interface Registrar { * Adds a callback allowing the plugin to take part in handling incoming * calls to {@link Activity#onNewIntent(Intent)}. * + *

This registrar is for Flutter's v1 embedding. To listen for new {@code Intent}s in the + * v2 embedding, use {@link ActivityPluginBinding#addOnNewIntentListener(NewIntentListener)}. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * * @param listener a {@link NewIntentListener} callback. * @return this {@link Registrar}. */ @@ -185,6 +269,12 @@ interface Registrar { * Adds a callback allowing the plugin to take part in handling incoming * calls to {@link Activity#onUserLeaveHint()}. * + *

This registrar is for Flutter's v1 embedding. To listen for leave hints in the + * v2 embedding, use {@link ActivityPluginBinding#addOnUserLeaveHintListener(UserLeaveHintListener)}. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * * @param listener a {@link UserLeaveHintListener} callback. * @return this {@link Registrar}. */ @@ -194,9 +284,24 @@ interface Registrar { * Adds a callback allowing the plugin to take part in handling incoming * calls to {@link Activity#onDestroy()}. * + *

This registrar is for Flutter's v1 embedding. The concept of {@code View} + * destruction does not exist in the v2 embedding. However, plugins in the v2 embedding + * can respond to {@link ActivityAware#onDetachedFromActivityForConfigChanges()} and + * {@link ActivityAware#onDetachedFromActivity()}, which indicate the loss of a visual + * context for the running Flutter experience. Developers should implement + * {@link ActivityAware} for their {@link FlutterPlugin} if such callbacks are needed. Also, + * plugins can respond to + * {@link FlutterPlugin#onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding)}, which + * indicates that the given plugin has been completely disconnected from the associated + * Flutter experience and should clean up any resources. + * + *

For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, + * visit http://flutter.dev/go/android-plugin-migration + * * @param listener a {@link ViewDestroyListener} callback. * @return this {@link Registrar}. */ + // TODO(amirh): Add a line in the javadoc above that points to a Platform Views website guide when one is available (but not a website API doc) Registrar addViewDestroyListener(ViewDestroyListener listener); } From 2dcfaaeb5d3caee2bf7488e90abadf0456f43922 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Nov 2019 20:09:18 -0800 Subject: [PATCH 078/591] Reland "Guarding EAGLContext used by Flutter #13314" (#13759) --- ci/licenses_golden/licenses_flutter | 4 + shell/common/BUILD.gn | 2 + shell/common/rasterizer.cc | 4 +- .../common/renderer_context_switch_manager.cc | 30 +++++ .../common/renderer_context_switch_manager.h | 116 ++++++++++++++++++ shell/common/shell_test.cc | 12 +- shell/common/shell_test.h | 6 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 +++-- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/gpu/gpu_surface_metal.h | 3 +- shell/gpu/gpu_surface_metal.mm | 5 +- shell/platform/android/android_surface_gl.cc | 13 +- shell/platform/android/android_surface_gl.h | 7 +- shell/platform/darwin/ios/BUILD.gn | 2 + .../framework/Source/FlutterPlatformViews.mm | 16 ++- .../Source/FlutterPlatformViews_Internal.h | 6 + shell/platform/darwin/ios/ios_gl_context.h | 17 ++- shell/platform/darwin/ios/ios_gl_context.mm | 22 ++-- .../ios/ios_gl_context_switch_manager.h | 65 ++++++++++ .../ios/ios_gl_context_switch_manager.mm | 79 ++++++++++++ .../darwin/ios/ios_gl_render_target.h | 16 +-- .../darwin/ios/ios_gl_render_target.mm | 43 ++++--- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 10 +- shell/platform/darwin/ios/ios_surface_gl.mm | 18 ++- shell/platform/darwin/ios/ios_surface_metal.h | 3 +- .../platform/darwin/ios/ios_surface_metal.mm | 5 +- .../darwin/ios/ios_surface_software.h | 5 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 +-- .../platform/embedder/embedder_surface_gl.cc | 13 +- shell/platform/embedder/embedder_surface_gl.h | 7 +- .../Scenarios.xcodeproj/project.pbxproj | 16 ++- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ++++ .../Scenarios/Scenarios/GLTestPlatformView.h | 30 +++++ .../Scenarios/Scenarios/GLTestPlatformView.m | 90 ++++++++++++++ .../ScenariosUITests/PlatformViewGLTests.m | 39 ++++++ testing/scenario_app/lib/main.dart | 1 + .../scenario_app/lib/src/platform_view.dart | 36 ++++-- testing/scenario_app/lib/src/texture.dart | 0 44 files changed, 745 insertions(+), 123 deletions(-) create mode 100644 shell/common/renderer_context_switch_manager.cc create mode 100644 shell/common/renderer_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m create mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5587e1b6bdc5b..db712f544e80c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -511,6 +511,8 @@ FILE: ../../../flutter/shell/common/pointer_data_dispatcher.cc FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h FILE: ../../../flutter/shell/common/rasterizer.cc FILE: ../../../flutter/shell/common/rasterizer.h +FILE: ../../../flutter/shell/common/renderer_context_switch_manager.cc +FILE: ../../../flutter/shell/common/renderer_context_switch_manager.h FILE: ../../../flutter/shell/common/run_configuration.cc FILE: ../../../flutter/shell/common/run_configuration.h FILE: ../../../flutter/shell/common/shell.cc @@ -800,6 +802,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f93bca63478f0..d87b6abede0e5 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -78,6 +78,8 @@ source_set("common") { "pointer_data_dispatcher.h", "rasterizer.cc", "rasterizer.h", + "renderer_context_switch_manager.cc", + "renderer_context_switch_manager.h", "run_configuration.cc", "run_configuration.h", "shell.cc", diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d61e1ff86ddc3..056fa419aea8b 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -163,7 +163,9 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, // happen in case of software rendering. surface = SkSurface::MakeRaster(image_info); } else { - if (!surface_->MakeRenderContextCurrent()) { + std::unique_ptr + context_switch = surface_->MakeRenderContextCurrent(); + if (context_switch->GetSwitchResult()) { return nullptr; } diff --git a/shell/common/renderer_context_switch_manager.cc b/shell/common/renderer_context_switch_manager.cc new file mode 100644 index 0000000000000..6bd5dd12832c7 --- /dev/null +++ b/shell/common/renderer_context_switch_manager.cc @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "renderer_context_switch_manager.h" + +namespace flutter { + +RendererContextSwitchManager::RendererContextSwitchManager() = default; + +RendererContextSwitchManager::~RendererContextSwitchManager() = default; + +RendererContextSwitchManager::RendererContextSwitch::RendererContextSwitch() = + default; + +RendererContextSwitchManager::RendererContextSwitch::~RendererContextSwitch(){}; + +RendererContextSwitchManager::RendererContextSwitchPureResult:: + RendererContextSwitchPureResult(bool switch_result) + : switch_result_(switch_result){}; + +RendererContextSwitchManager::RendererContextSwitchPureResult:: + ~RendererContextSwitchPureResult() = default; + +bool RendererContextSwitchManager::RendererContextSwitchPureResult:: + GetSwitchResult() { + return switch_result_; +} + +} // namespace flutter diff --git a/shell/common/renderer_context_switch_manager.h b/shell/common/renderer_context_switch_manager.h new file mode 100644 index 0000000000000..323a3a5e5af32 --- /dev/null +++ b/shell/common/renderer_context_switch_manager.h @@ -0,0 +1,116 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ + +#include +#include "flutter/fml/macros.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// Manages `RendererContextSwitch`. +/// +/// Should be subclassed for platforms that uses GL and requires context +/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the +/// `RendererContextSwitchManager` to set gl contexts. +/// +class RendererContextSwitchManager { + public: + //------------------------------------------------------------------------------ + /// Switches the gl context to the flutter's contexts. + /// + /// Should be subclassed for each platform embedder that uses GL. + /// In construction, it should set the current context to a flutter's context + /// In destruction, it should rest the current context. + /// + class RendererContextSwitch { + public: + RendererContextSwitch(); + + virtual ~RendererContextSwitch(); + + virtual bool GetSwitchResult() = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitch); + }; + + RendererContextSwitchManager(); + ~RendererContextSwitchManager(); + + //---------------------------------------------------------------------------- + /// @brief Creates a shell instance using the provided settings. The + /// callbacks to create the various shell subcomponents will be + /// called on the appropriate threads before this method returns. + /// If this is the first instance of a shell in the process, this + /// call also bootstraps the Dart VM. + /// + /// @param[in] task_runners The task runners + /// @param[in] settings The settings + /// @param[in] on_create_platform_view The callback that must return a + /// platform view. This will be called on + /// the platform task runner before this + /// method returns. + /// @param[in] on_create_rasterizer That callback that must provide a + /// valid rasterizer. This will be called + /// on the render task runner before this + /// method returns. + /// + /// @return A full initialized shell if the settings and callbacks are + /// valid. The root isolate has been created but not yet launched. + /// It may be launched by obtaining the engine weak pointer and + /// posting a task onto the UI task runner with a valid run + /// configuration to run the isolate. The embedder must always + /// check the validity of the shell (using the IsSetup call) + /// immediately after getting a pointer to it. + /// + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's context as current context. + /// + /// @return A `RendererContextSwitch` with `GetSwitchResult` returning + /// true if the setting process is succesful. + virtual std::unique_ptr MakeCurrent() = 0; + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's resources context as current context. + /// + /// @return A `RendererContextSwitch` with `GetSwitchResult` returning + /// true if the setting process is succesful. + virtual std::unique_ptr ResourceMakeCurrent() = 0; + + //------------------------------------------------------------------------------ + /// A representation of a `RendererContextSwitch` that doesn't require actual + /// context switching. + /// + class RendererContextSwitchPureResult final : public RendererContextSwitch { + public: + // Constructor that creates an `RendererContextSwitchPureResult`. + // The `GetSwitchResult` will return the same value as `switch_result`. + + //---------------------------------------------------------------------------- + /// @brief Constructs a `RendererContextSwitchPureResult`. + /// + /// @param[in] switch_result the switch result that will be returned + /// in `GetSwitchResult()` + /// + RendererContextSwitchPureResult(bool switch_result); + + ~RendererContextSwitchPureResult(); + + bool GetSwitchResult() override; + + private: + bool switch_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchPureResult); + }; + + FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchManager); +}; + +} // namespace flutter + +#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 51370d082862b..3fe5a2b9e0f87 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,8 +355,11 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -bool ShellTestPlatformView::GLContextMakeCurrent() { - return gl_surface_.MakeCurrent(); +std::unique_ptr +ShellTestPlatformView::GLContextMakeCurrent() { + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>( + gl_surface_.MakeCurrent()); } // |GPUSurfaceGLDelegate| @@ -387,5 +390,10 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } +std::shared_ptr +ShellTestPlatformView::GetRendererContextSwitchManager() { + return nullptr; +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index fdee9653b71ce..27d32f058ae0e 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,7 +144,8 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -161,6 +162,9 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + std::shared_ptr + GetRendererContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index b8a77ca1811d4..2b876e0c0f558 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,8 +60,10 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -bool Surface::MakeRenderContextCurrent() { - return true; +std::unique_ptr +Surface::MakeRenderContextCurrent() { + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>(true); } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index 7bbc16e24c690..0b19725da3b74 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,6 +10,7 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -58,7 +59,8 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual bool MakeRenderContextCurrent(); + virtual std::unique_ptr + MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5f30a48375d33..a7648b7f71d39 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -9,6 +9,7 @@ #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" #include "flutter/shell/common/persistent_cache.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -39,7 +40,10 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -87,8 +91,6 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; - - delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -98,7 +100,9 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -114,8 +118,9 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -126,8 +131,6 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; - - delegate_->GLContextClearCurrent(); } // |Surface| @@ -253,7 +256,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -285,7 +290,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - return std::make_unique(surface, submit_callback); + std::unique_ptr result = + std::make_unique(surface, submit_callback); + return result; } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -293,6 +300,8 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -329,7 +338,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } - return true; } @@ -360,7 +368,8 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -bool GPUSurfaceGL::MakeRenderContextCurrent() { +std::unique_ptr +GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 97325569bfd16..eb67440eee07c 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,7 +44,8 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - bool MakeRenderContextCurrent() override; + std::unique_ptr + MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dfe0ce7f468db..444e7301c1939 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,6 +7,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -16,7 +17,8 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual bool GLContextMakeCurrent() = 0; + virtual std::unique_ptr + GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -59,6 +61,9 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; + + virtual std::shared_ptr + GetRendererContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index fc6b0964766ce..acb5ca02a7162 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -49,7 +49,8 @@ class GPUSurfaceMetal : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - bool MakeRenderContextCurrent() override; + std::unique_ptr MakeRenderContextCurrent() + override; void ReleaseUnusedDrawableIfNecessary(); diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 81abf740d48f6..2aba2cd41f376 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -175,9 +175,10 @@ } // |Surface| -bool GPUSurfaceMetal::MakeRenderContextCurrent() { +std::unique_ptr +GPUSurfaceMetal::MakeRenderContextCurrent() { // This backend has no such concept. - return true; + return std::make_unique(true); } void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() { diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 737d9f293a518..a06b13e68f6bb 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,9 +104,12 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -bool AndroidSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr +AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return onscreen_context_->MakeCurrent(); + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>( + onscreen_context_->MakeCurrent()); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -130,4 +133,10 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +AndroidSurfaceGL::GetRendererContextSwitchManager() { + return nullptr; +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index d59302ad66509..bfe7b2ce06fb2 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,7 +47,8 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -61,6 +62,10 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr + GetRendererContextSwitchManager() override; + private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index a66c3bf48c5ae..3e67beb97e717 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,6 +86,8 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", + "ios_gl_context_switch_manager.h", + "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33ca14d9fabea..487fd42f5ea0d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,6 +160,11 @@ frame_size_ = frame_size; } +void FlutterPlatformViewsController::SetRendererContextSwitchManager( + std::shared_ptr gl_context_guard_manager) { + renderer_context_switch_manager_ = gl_context_guard_manager; +} + void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -368,7 +373,12 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, gl_context, gr_context); + if (renderer_context_switch_manager_ != nullptr) { + std::unique_ptr contextSwitch = + renderer_context_switch_manager_->MakeCurrent(); + } + + EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -455,6 +465,10 @@ std::shared_ptr gl_context, GrContext* gr_context) { FML_DCHECK(flutter_view_); + if (renderer_context_switch_manager_ != nullptr) { + std::unique_ptr contextSwitch = + renderer_context_switch_manager_->MakeCurrent(); + } auto overlay_it = overlays_.find(overlay_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c8daeaa605946..77b8ece44dc5d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,6 +11,7 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -80,6 +81,9 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); + void SetRendererContextSwitchManager( + std::shared_ptr gl_context_guard_manager); + void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -204,6 +208,8 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + std::shared_ptr renderer_context_switch_manager_; + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 232645d9c8592..8c73ce75dc028 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,16 +26,25 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - bool MakeCurrent(); + std::unique_ptr + MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); + + std::shared_ptr GetIOSGLContextSwitchManager() { + return renderer_context_switch_manager_; + } sk_sp ColorSpace() const { return color_space_; } + fml::scoped_nsobject GetContext() const { + return renderer_context_switch_manager_->GetContext(); + } + private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; sk_sp color_space_; + std::shared_ptr renderer_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 52fb85f8f19a9..a54387731e8d9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,15 +13,7 @@ namespace flutter { IOSGLContext::IOSGLContext() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } + renderer_context_switch_manager_ = std::make_shared(); // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -48,16 +40,16 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), context_.get(), - resource_context_.get()); + return std::make_unique(std::move(layer), renderer_context_switch_manager_); } -bool IOSGLContext::MakeCurrent() { - return [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr IOSGLContext::MakeCurrent() { + return renderer_context_switch_manager_->MakeCurrent(); } -bool IOSGLContext::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr +IOSGLContext::ResourceMakeCurrent() { + return renderer_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h new file mode 100644 index 0000000000000..56ee4f2eabb3e --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ + +#define GLES_SILENCE_DEPRECATION + +#import +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// The iOS implementation of `RendererContextSwitchManager`. +/// +/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and +/// sets the flutter's gl context as current. +/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to +/// current. +/// +class IOSGLContextSwitchManager final : public RendererContextSwitchManager { + public: + class IOSGLContextSwitch final : public RendererContextSwitch { + public: + IOSGLContextSwitch(IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context); + + ~IOSGLContextSwitch(); + + bool GetSwitchResult() override; + + private: + IOSGLContextSwitchManager& manager_; + bool switch_result_; + bool has_pushed_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); + }; + + IOSGLContextSwitchManager(); + + ~IOSGLContextSwitchManager(); + + std::unique_ptr MakeCurrent() override; + std::unique_ptr ResourceMakeCurrent() override; + + fml::scoped_nsobject GetContext(); + + private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; + fml::scoped_nsobject stored_; + + bool PushContext(fml::scoped_nsobject context); + void PopContext(); + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); +}; + +} + +#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm new file mode 100644 index 0000000000000..0be0a3bcb4df7 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios_gl_context_switch_manager.h" + +namespace flutter { + +IOSGLContextSwitchManager::IOSGLContextSwitchManager() { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } +}; + +IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; + +std::unique_ptr +IOSGLContextSwitchManager::MakeCurrent() { + return std::make_unique(*this, context_); +} + +std::unique_ptr +IOSGLContextSwitchManager::ResourceMakeCurrent() { + return std::make_unique(*this, resource_context_); +} + +fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { + return context_; +} + +bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { + EAGLContext* current = [EAGLContext currentContext]; + if (current == nil) { + [stored_.get() addObject:[NSNull null]]; + } else { + [stored_.get() addObject:current]; + } + bool result = [EAGLContext setCurrentContext:context.get()]; + return result; +} + +void IOSGLContextSwitchManager::PopContext() { + EAGLContext* last = [stored_.get() lastObject]; + [stored_.get() removeLastObject]; + if ([last isEqual:[NSNull null]]) { + [EAGLContext setCurrentContext:nil]; + return; + } + [EAGLContext setCurrentContext:last]; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( + IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context) + : manager_(manager) { + bool result = manager_.PushContext(context); + has_pushed_context_ = true; + switch_result_ = result; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { + if (!has_pushed_context_) { + return; + } + manager_.PopContext(); +} + +bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { + return switch_result_; +} +} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index b2eafe16e0950..f52c562fd96d0 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,14 +13,15 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context); + IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager); ~IOSGLRenderTarget(); @@ -32,16 +33,17 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - bool MakeCurrent(); + std::unique_ptr + MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; + std::shared_ptr renderer_context_switch_manager_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a57ba9c46b414..a7037746369db 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,22 +12,20 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context) +IOSGLRenderTarget::IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager) : layer_(std::move(layer)), - context_([context retain]), - resource_context_([resource_context retain]), + renderer_context_switch_manager_(gl_context_guard_manager), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - FML_DCHECK(context_ != nullptr); - FML_DCHECK(resource_context_ != nullptr); - - bool context_current = [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + renderer_context_switch_manager_->MakeCurrent(); + bool context_current = context_switch->GetSwitchResult(); FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -62,8 +60,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - EAGLContext* context = EAGLContext.currentContext; - [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + renderer_context_switch_manager_->MakeCurrent(); FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -71,7 +69,6 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -104,8 +101,9 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - - if (![EAGLContext setCurrentContext:context_]) { + std::unique_ptr context_switch = + renderer_context_switch_manager_->MakeCurrent(); + if (!context_switch->GetSwitchResult()) { return false; } @@ -116,7 +114,8 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { + if (![renderer_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER + fromDrawable:layer_.get()]) { return false; } @@ -132,12 +131,18 @@ return true; } -bool IOSGLRenderTarget::MakeCurrent() { - return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr +IOSGLRenderTarget::MakeCurrent() { + bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); + if (!isUpdateSuccessful) { + return std::make_unique(false); + } + return renderer_context_switch_manager_->MakeCurrent(); } -bool IOSGLRenderTarget::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr +IOSGLRenderTarget::ResourceMakeCurrent() { + return renderer_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 49f40f9eec76a..b852a63ee4e9a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -27,7 +28,8 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual bool ResourceContextMakeCurrent() = 0; + virtual std::unique_ptr + ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index c1019bb442bb0..708413c239400 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,6 +9,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -32,7 +33,8 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() + override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -40,7 +42,8 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - bool GLContextMakeCurrent() override; + std::unique_ptr GLContextMakeCurrent() + override; bool GLContextClearCurrent() override; @@ -53,6 +56,9 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetRendererContextSwitchManager() override; + // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 48e70e00a4e7a..6417e7d899b42 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,7 +28,8 @@ return render_target_->IsValid(); } -bool IOSSurfaceGL::ResourceContextMakeCurrent() { +std::unique_ptr +IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -56,11 +57,12 @@ return true; } -bool IOSSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr +IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return false; + return std::make_unique(false); } - return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); + return render_target_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -73,6 +75,11 @@ return IsValid() && render_target_->PresentRenderBuffer(); } +// |GPUSurfaceGLDelegate| +std::shared_ptr IOSSurfaceGL::GetRendererContextSwitchManager() { + return context_->GetIOSGLContextSwitchManager(); +} + // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -144,7 +151,8 @@ if (platform_views_controller == nullptr) { return true; } - + platform_views_controller->SetRendererContextSwitchManager( + context_->GetIOSGLContextSwitchManager()); bool submitted = platform_views_controller->SubmitFrame(std::move(context), context_); [CATransaction commit]; return submitted; diff --git a/shell/platform/darwin/ios/ios_surface_metal.h b/shell/platform/darwin/ios/ios_surface_metal.h index 2b001ea2b692d..1263571870bd4 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.h +++ b/shell/platform/darwin/ios/ios_surface_metal.h @@ -30,7 +30,8 @@ class IOSSurfaceMetal final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() + override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_metal.mm b/shell/platform/darwin/ios/ios_surface_metal.mm index 58a5fc328647b..827db668afa1f 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.mm +++ b/shell/platform/darwin/ios/ios_surface_metal.mm @@ -22,8 +22,9 @@ } // |IOSSurface| -bool IOSSurfaceMetal::ResourceContextMakeCurrent() { - return false; +std::unique_ptr +IOSSurfaceMetal::ResourceContextMakeCurrent() { + return std::make_unique(false); } // |IOSSurface| diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index daac2ffc77231..c0b6298e47bf7 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" - @class CALayer; namespace flutter { @@ -28,7 +28,8 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() + override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index ab5490cf25140..8c38ab5861e82 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,8 +27,9 @@ return layer_; } -bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return false; +std::unique_ptr +IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return std::make_unique(false); } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index bb37fa9610b2b..d867fc68fc6c8 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,15 +107,18 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + if (gl_context_ != nullptr) { + std::unique_ptr context_switch = + gl_context_->ResourceMakeCurrent(); + if (context_switch->GetSwitchResult()) { + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + } } - - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index d37b03aae8d9e..2efa1c01baf46 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,8 +34,11 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextMakeCurrent() { - return gl_dispatch_table_.gl_make_current_callback(); +std::unique_ptr +EmbedderSurfaceGL::GLContextMakeCurrent() { + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>( + gl_dispatch_table_.gl_make_current_callback()); } // |GPUSurfaceGLDelegate| @@ -79,6 +82,12 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +EmbedderSurfaceGL::GetRendererContextSwitchManager() { + return nullptr; +} + // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index a01fa05d4e62c..12f264f383c37 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,7 +50,8 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -73,6 +74,10 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; + // |GPUSurfaceGLDelegate| + std::shared_ptr + GetRendererContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index e667c4c88678d..dd2238aba3181 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -136,6 +138,9 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -202,6 +207,8 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -239,6 +246,7 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -398,6 +406,7 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -418,6 +427,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -492,7 +502,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -545,7 +555,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -561,6 +571,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -584,6 +595,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index 87799ad5e6434..cd1c5b7366a89 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - - - - - - - - * registrar = + [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; + [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; + self.window.rootViewController = flutterViewController; +} + @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h new file mode 100644 index 0000000000000..19cefe7025986 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GLTestPlatformView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject*)messenger; + +- (UIView*)view; + +@end + +@interface GLTestPlatformViewFactory : NSObject + +- (instancetype)initWithMessenger:(NSObject*)messenger; + +@end + +@interface GLTestView : UIView + +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m new file mode 100644 index 0000000000000..8142550e52863 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m @@ -0,0 +1,90 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GLTestPlatformView.h" + +#define GLES_SILENCE_DEPRECATION + +@implementation GLTestPlatformView { + int64_t _viewId; + GLTestView* _view; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id)args + binaryMessenger:(NSObject*)messenger { + if ([super init]) { + _viewId = viewId; + _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; + } + return self; +} + +- (UIView*)view { + return _view; +} + +@end + +@implementation GLTestPlatformViewFactory { + NSObject* _messenger; +} + +- (instancetype)initWithMessenger:(NSObject*)messenger { + self = [super init]; + if (self) { + _messenger = messenger; + } + return self; +} + +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger]; + return platformView; +} + +- (NSObject*)createArgsCodec { + return [FlutterStringCodec sharedInstance]; +} + +@end + +@interface GLTestView () + +@property(strong, nonatomic) EAGLContext* context; + +@end + +@implementation GLTestView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + _context.debugLabel = @"platform view context"; + [EAGLContext setCurrentContext:_context]; + self.backgroundColor = [UIColor redColor]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [self checkEAGLContext]; + }); + } + return self; +} + +- (void)checkEAGLContext { + if ([EAGLContext currentContext] != _context) { + self.accessibilityIdentifier = @"gl_platformview_wrong_context"; + } else { + self.accessibilityIdentifier = @"gl_platformview_correct_context"; + } +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m new file mode 100644 index 0000000000000..d581926ae5a6a --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface PlatformViewGLTests : XCTestCase + +@property(nonatomic, strong) XCUIApplication* application; + +@end + +@implementation PlatformViewGLTests + +- (void)setUp { + self.continueAfterFailure = NO; + + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--platform-view-gl" ]; + [self.application launch]; +} + +- (void)testExample { + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || + [element.identifier isEqualToString:@"gl_platformview_correct_context"]; + }]; + XCUIElement* firstElement = + [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; + if (![firstElement waitForExistenceWithTimeout:30]) { + XCTFail(@"Failed due to not able to find platform view with 30 seconds"); + } + XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); +} + +@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 7a264b70008f0..82b589a6681fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,6 +25,7 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), + 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 05efe52fa8b25..86fb4df1df449 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -281,6 +281,24 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } +/// Platform view scenario for testing EAGLContext on iOS. +class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Constructs a platform view to test EAGLContext on iOS. + PlatformViewGLScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + finishBuilderByAddingPlatformViewAndPicture(builder, 6); + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -289,7 +307,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id) { + void createPlatformView(Window window, String text, int id, String viewType) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -313,8 +331,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - 'scenarios/textPlatformView'.length, - ...utf8.encode('scenarios/textPlatformView'), + viewType.length, + ...utf8.encode(viewType), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart new file mode 100644 index 0000000000000..e69de29bb2d1d From 745909b412db1e3705fd729b8c6d55fd619b5f76 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 23:54:39 -0500 Subject: [PATCH 079/591] Roll src/third_party/skia 4135cf0b57c2..0fd4f01b9b83 (1 commits) (#13770) https://skia.googlesource.com/skia.git/+log/4135cf0b57c2..0fd4f01b9b83 git log 4135cf0b57c2..0fd4f01b9b83 --date=short --no-merges --format='%ad %ae %s' 2019-11-08 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@0fd4f01b9b83 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b6cf17faa5958..6e320bf51236c 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4135cf0b57c2ef71b60ecf973d93eab37032f4f7', + 'skia_revision': '0fd4f01b9b83f9c24e1e07ba96124ae8e3836a62', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0de77d40ceafc..b23395b76caa2 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 032ddb8e2831962c302984a5b24f2911 +Signature: 38854b243b1fbdfe2021e227160d6f46 UNUSED LICENSES: From eec36a8d4f6d65c0e7f3a1ab677219bbd5b105d7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 9 Nov 2019 07:52:55 -0500 Subject: [PATCH 080/591] Roll fuchsia/sdk/core/mac-amd64 from eq_Rz... to xC_an... (#13771) Roll fuchsia/sdk/core/mac-amd64 from eq_Rz... to xC_an... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 6e320bf51236c..42d0afe862e3d 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'eq_RznhOOqBWirQ97NtrVCy1swWdw8dWkpk07aiMiqgC' + 'version': 'xC_anBYQWtjYKsv9CAjUQlxQjTXgSRcL8aErQCCT8UIC' } ], 'condition': 'host_os == "mac"', From 3fb5d855f2287c4c378f188f5ff95fad7451af21 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 9 Nov 2019 20:33:14 -0500 Subject: [PATCH 081/591] Roll fuchsia/sdk/core/mac-amd64 from xC_an... to 4JJNo... (#13773) Roll fuchsia/sdk/core/mac-amd64 from xC_an... to 4JJNo... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 42d0afe862e3d..dc3bd75cb364c 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'xC_anBYQWtjYKsv9CAjUQlxQjTXgSRcL8aErQCCT8UIC' + 'version': '4JJNoTknberO5uQ83n8HzFdmtLpaedI4RUaoxhYRh74C' } ], 'condition': 'host_os == "mac"', From 14cc5195c27f4f260af4657813db43c6996dce7a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 10 Nov 2019 09:09:11 -0500 Subject: [PATCH 082/591] Roll fuchsia/sdk/core/mac-amd64 from 4JJNo... to RrTFI... (#13774) Roll fuchsia/sdk/core/mac-amd64 from 4JJNo... to RrTFI... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index dc3bd75cb364c..5cf6b159c464c 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '4JJNoTknberO5uQ83n8HzFdmtLpaedI4RUaoxhYRh74C' + 'version': 'RrTFIJHQU4qXfAHlspDkeISRqP3wkyVJrn-AqsjiZiUC' } ], 'condition': 'host_os == "mac"', From 0172e08078c069ca083ca3b7c214d7ed98a8dede Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 11 Nov 2019 14:22:01 -0500 Subject: [PATCH 083/591] Roll fuchsia/sdk/core/mac-amd64 from RrTFI... to TnTbF... (#13775) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 5cf6b159c464c..d5e991017c6c3 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'RrTFIJHQU4qXfAHlspDkeISRqP3wkyVJrn-AqsjiZiUC' + 'version': 'TnTbFIhfmLxcE3pJs17ZcWXT-iDYa1Ax8ee-BTCmR6YC' } ], 'condition': 'host_os == "mac"', From b6e25aa7c9e5f0a746fdfe225b8d03298df47710 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 11 Nov 2019 13:46:33 -0800 Subject: [PATCH 084/591] Roll src/third_party/dart d45c3d15cb..7408ed4730 (20 commits) (#13778) dart-lang/sdk@7408ed4730 Added null-check selector to syntax of cascades, and test. dart-lang/sdk@6f70d7f5a3 Revert "[vm] Remove obsolete kernel constant expression evaluator." dart-lang/sdk@96822518f9 [frontend_server] Add reproduction test of incremental serializer bug dart-lang/sdk@64b0bfac68 [infra] Update CHANGELOG.md for 2.6.1 dart-lang/sdk@4a661e19e2 [cfe] Remove type_promotion_look_ahead_listener.dart dart-lang/sdk@12c92332f8 [CFE] Parser test can run scanner in non-nnbd-mode dart-lang/sdk@684c456ffc [cfe] Remove reify runtime dart-lang/sdk@e8de03915b [vm] Remove obsolete kernel constant expression evaluator. dart-lang/sdk@0e9eadcb71 Upgrade pub revision dart-lang/sdk@48f0b83ff2 [CFE] Allow ! in cascade dart-lang/sdk@cf2a474438 [CFE] Fix incremental serialization bug caused by missing parameter passing dart-lang/sdk@a7a54afbaa [cfe] Support failure log printing in unit_test_suites dart-lang/sdk@bbdb7860f6 [build]: Build on Arch Linux with python 2.7-3.8 dart-lang/sdk@64e65a7dab [dart2js] new-rti: Use local in setting up type$ dart-lang/sdk@8f1be54906 [dart2js] New RTI: Improve normalization of is-tests. dart-lang/sdk@1b344fc77e [dartfuzz] Add fuzzer support for extension methods for classes dart-lang/sdk@ed956daa3f Update the DDC dart:async patch file version to match the migrated lib. dart-lang/sdk@be2866b9b1 [analyzer] update lib/crash_reporting.dart dart-lang/sdk@8ca18f1a14 Extension method prefixed import tests. dart-lang/sdk@750a83b896 [dartdevc] Fix crash when source files contain non-UTF8 characters --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index d5e991017c6c3..eec20642c2386 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd45c3d15cb3cea0104a87697c085259666eec528', + 'dart_revision': '7408ed473006eeb41e23b9b21d2f5544bdd22b7b', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -78,7 +78,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '80ac76400ff58fde3c5a335d860d196c3febe837', + 'dart_pub_rev': 'd15067931a6b671a1c9dcc98b5923347676269cf', 'dart_pub_semver_tag': '1.4.2', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 931de974f3032..00281011526e5 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 9355024c19864ce227141256962ef660 +Signature: c56797567c2eb8b80b7d17fddc26d426 UNUSED LICENSES: From 10d5ed19f8ba948d2457cd08377970c9b24064e4 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 11 Nov 2019 15:38:30 -0800 Subject: [PATCH 085/591] [web] Fix path to svg for drrect (#13779) --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/path_to_svg.dart | 4 ++-- .../engine/canvas_draw_image_golden_test.dart | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index e8b87138a0033..bbd03eb46112a 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 009fbdd595aeec364eaff6b8f337f8ceb3c44ab9 +revision: 75729099b6f4d4e78ceb110b9305fc9905b91519 diff --git a/lib/web_ui/lib/src/engine/path_to_svg.dart b/lib/web_ui/lib/src/engine/path_to_svg.dart index 10dc5455740cd..24f0fcf93893f 100644 --- a/lib/web_ui/lib/src/engine/path_to_svg.dart +++ b/lib/web_ui/lib/src/engine/path_to_svg.dart @@ -96,9 +96,9 @@ void pathToSvg(ui.Path path, StringBuffer sb, final double blRadiusY = rrect.blRadiusY.abs(); final double brRadiusY = rrect.brRadiusY.abs(); - sb.write('L ${left + trRadiusX} $top '); + sb.write('M ${left + trRadiusX} $top '); // Top side and top-right corner - sb.write('M ${right - trRadiusX} $top '); + sb.write('L ${right - trRadiusX} $top '); _writeEllipse(sb, right - trRadiusX, top + trRadiusY, trRadiusX, trRadiusY, 0, 1.5 * math.pi, 2.0 * math.pi, false); // Right side and bottom-right corner diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart index 9601d71afa93f..ea789e03b77cf 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart @@ -31,7 +31,7 @@ void main() async { try { sceneElement.append(engineCanvas.rootElement); html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRate: 0.02); + await matchGoldenFile('$fileName.png', region: region, maxDiffRate: 0.2); } finally { // The page is reused across tests, so remove the element after taking the // Scuba screenshot. @@ -100,6 +100,19 @@ void main() async { await _checkScreenshot(rc, 'draw_image_rect_with_source'); }); + test('Paints image with source and destination and round clip', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + Image testImage = createTestImage(); + double testWidth = testImage.width.toDouble(); + double testHeight = testImage.height.toDouble(); + rc.save(); + rc.clipRRect(RRect.fromLTRBR(100, 30, 2 * testWidth, 2 * testHeight, Radius.circular(16))); + rc.drawImageRect(testImage, Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); + await _checkScreenshot(rc, 'draw_image_rect_with_source_and_clip'); + }); + test('Paints image with transform using source and destination', () async { final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); From ce0e9e79e61f6ca55924cbba8afcc48a60b175e9 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 11 Nov 2019 15:46:26 -0800 Subject: [PATCH 086/591] Fix RendererContextSwitch result check in Rasterizer::MakeRasterSnapshot (#13785) Fixes https://github.com/flutter/flutter/issues/31355 --- shell/common/rasterizer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 056fa419aea8b..e66049e0a07e4 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -165,7 +165,7 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, } else { std::unique_ptr context_switch = surface_->MakeRenderContextCurrent(); - if (context_switch->GetSwitchResult()) { + if (!context_switch->GetSwitchResult()) { return nullptr; } From f68de3f45168454301fa71c00abe8afdeededfc7 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 11 Nov 2019 16:42:40 -0800 Subject: [PATCH 087/591] Add line boundary information to LineMetrics. (#13727) This exposes the line boundary information a line by adding getLineBoundary to return the indices corresponding to a line around a TextPosition. The information is already calculated when calculating line metrics, so that we can enable moving the selection/cursor to the beginning/end of a line in a text field. --- lib/ui/text.dart | 37 ++++++++++++------- lib/ui/text/paragraph.cc | 18 +++++++++ lib/ui/text/paragraph.h | 1 + lib/web_ui/lib/src/engine/text/paragraph.dart | 31 +++++++--------- lib/web_ui/lib/src/ui/text.dart | 30 ++++++++++++--- 5 files changed, 79 insertions(+), 38 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index a2a8df205793c..6af9893c56243 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1866,23 +1866,32 @@ class Paragraph extends NativeFieldWrapperClass2 { } List _getPositionForOffset(double dx, double dy) native 'Paragraph_getPositionForOffset'; - /// Returns the [start, end] of the word at the given offset. Characters not - /// part of a word, such as spaces, symbols, and punctuation, have word breaks - /// on both sides. In such cases, this method will return [offset, offset+1]. - /// Word boundaries are defined more precisely in Unicode Standard Annex #29 - /// http://www.unicode.org/reports/tr29/#Word_Boundaries - List getWordBoundary(dynamic position) { - // TODO(gspencergoog): have this take only a TextPosition once the framework - // code is calling it with that. - if (position is TextPosition) { - return _getWordBoundary(position.offset); - } else { - final int offset = position; - return _getWordBoundary(offset); - } + /// Returns the [TextRange] of the word at the given [TextPosition]. + /// + /// Characters not part of a word, such as spaces, symbols, and punctuation, + /// have word breaks on both sides. In such cases, this method will return + /// [offset, offset+1]. Word boundaries are defined more precisely in Unicode + /// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries + TextRange getWordBoundary(TextPosition position) { + final List boundary = _getWordBoundary(position.offset); + return TextRange(start: boundary[0], end: boundary[1]); } List _getWordBoundary(int offset) native 'Paragraph_getWordBoundary'; + /// Returns the [TextRange] of the line at the given [TextPosition]. + /// + /// The newline (if any) is returned as part of the range. + /// + /// Not valid until after layout. + /// + /// This can potentially be expensive, since it needs to compute the line + /// metrics, so use it sparingly. + TextRange getLineBoundary(TextPosition position) { + final List boundary = _getLineBoundary(position.offset); + return TextRange(start: boundary[0], end: boundary[1]); + } + List _getLineBoundary(int offset) native 'Paragraph_getLineBoundary'; + // Redirecting the paint function in this way solves some dependency problems // in the C++ code. If we straighten out the C++ dependencies, we can remove // this indirection. diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 0025615e7f8fa..5680735b6019b 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -31,6 +31,7 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Paragraph); V(Paragraph, layout) \ V(Paragraph, paint) \ V(Paragraph, getWordBoundary) \ + V(Paragraph, getLineBoundary) \ V(Paragraph, getRectsForRange) \ V(Paragraph, getRectsForPlaceholders) \ V(Paragraph, getPositionForOffset) \ @@ -134,6 +135,23 @@ Dart_Handle Paragraph::getWordBoundary(unsigned offset) { return result; } +Dart_Handle Paragraph::getLineBoundary(unsigned offset) { + std::vector metrics = m_paragraph->GetLineMetrics(); + int line_start = -1; + int line_end = -1; + for (txt::LineMetrics& line : metrics) { + if (offset >= line.start_index && offset < line.end_index) { + line_start = line.start_index; + line_end = line.end_index; + break; + } + } + Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); + Dart_ListSetAt(result, 0, ToDart(line_start)); + Dart_ListSetAt(result, 1, ToDart(line_end)); + return result; +} + std::vector Paragraph::computeLineMetrics() { std::vector result; std::vector metrics = m_paragraph->GetLineMetrics(); diff --git a/lib/ui/text/paragraph.h b/lib/ui/text/paragraph.h index cdd45335d3e9e..7aea6079378e1 100644 --- a/lib/ui/text/paragraph.h +++ b/lib/ui/text/paragraph.h @@ -49,6 +49,7 @@ class Paragraph : public RefCountedDartWrappable { std::vector getRectsForPlaceholders(); Dart_Handle getPositionForOffset(double dx, double dy); Dart_Handle getWordBoundary(unsigned offset); + Dart_Handle getLineBoundary(unsigned offset); std::vector computeLineMetrics(); size_t GetAllocationSize() override; diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 0c53962ddc020..2aca66ff297c2 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -281,27 +281,22 @@ class EngineParagraph implements ui.Paragraph { } @override - List getWordBoundary(dynamic position) { - // TODO(gspencergoog): have this take only a TextPosition once the framework - // code is calling it with that. - if (position is ui.TextPosition) { - ui.TextPosition textPosition = position; - if (_plainText == null) { - return [textPosition.offset, textPosition.offset]; - } - - final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset); - final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset); - return [start, end]; - } - + ui.TextRange getWordBoundary(ui.TextPosition position) { + ui.TextPosition textPosition = position; if (_plainText == null) { - return [position, position]; + return ui.TextRange(start: textPosition.offset, end: textPosition.offset); } - final int start = WordBreaker.prevBreakIndex(_plainText, position); - final int end = WordBreaker.nextBreakIndex(_plainText, position); - return [start, end]; + final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset); + final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset); + return ui.TextRange(start: start, end: end); + } + + @override + ui.TextRange getLineBoundary(ui.TextPosition position) { + // TODO(flutter_web): https://github.com/flutter/flutter/issues/39537 + // Depends upon LineMetrics measurement. + return null; } @override diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 774787ae648f0..d003c7b6eda65 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1237,12 +1237,23 @@ abstract class Paragraph { /// within the text. TextPosition getPositionForOffset(Offset offset); - /// Returns the [start, end] of the word at the given offset. Characters not - /// part of a word, such as spaces, symbols, and punctuation, have word breaks - /// on both sides. In such cases, this method will return [offset, offset+1]. - /// Word boundaries are defined more precisely in Unicode Standard Annex #29 - /// http://www.unicode.org/reports/tr29/#Word_Boundaries - List getWordBoundary(dynamic position); + /// Returns the [TextRange] of the word at the given [TextPosition]. + /// + /// Characters not part of a word, such as spaces, symbols, and punctuation, + /// have word breaks on both sides. In such cases, this method will return + /// [offset, offset+1]. Word boundaries are defined more precisely in Unicode + /// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries + TextRange getWordBoundary(TextPosition position); + + /// Returns the [TextRange] of the line at the given [TextPosition]. + /// + /// The newline (if any) is returned as part of the range. + /// + /// Not valid until after layout. + /// + /// This can potentially be expensive, since it needs to compute the line + /// metrics, so use it sparingly. + TextRange getLineBoundary(TextPosition position); /// Returns a list of text boxes that enclose all placeholders in the paragraph. /// @@ -1252,6 +1263,13 @@ abstract class Paragraph { /// where positive y values indicate down. List getBoxesForPlaceholders(); + /// Returns the full list of [LineMetrics] that describe in detail the various + /// metrics of each laid out line. + /// + /// Not valid until after layout. + /// + /// This can potentially return a large amount of data, so it is not recommended + /// to repeatedly call this. Instead, cache the results. List computeLineMetrics(); } From 164df8e9e09a4debd478dce7fe192823fc3c4fe4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 11 Nov 2019 19:45:43 -0500 Subject: [PATCH 088/591] Roll src/third_party/skia 0fd4f01b9b83..3de645cbca78 (1 commits) (#13776) https://skia.googlesource.com/skia.git/+log/0fd4f01b9b83..3de645cbca78 git log 0fd4f01b9b83..3de645cbca78 --date=short --no-merges --format='%ad %ae %s' 2019-11-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader d9ed1c2732ba..31edef751a8d (3 commits) Created with: gclient setdep -r src/third_party/skia@3de645cbca78 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index eec20642c2386..c4097a5663e0f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '0fd4f01b9b83f9c24e1e07ba96124ae8e3836a62', + 'skia_revision': '3de645cbca784e8daf740fe5945befb1800ed8c6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b23395b76caa2..1dec20ca5d18b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 38854b243b1fbdfe2021e227160d6f46 +Signature: b2816ba7c238ea19879731426c08b7d3 UNUSED LICENSES: From 726e8f54dbddd294dec7d850bf08ac91b04c80bd Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 11 Nov 2019 16:54:28 -0800 Subject: [PATCH 089/591] Add Helvetica and sans-serif as fallback font families (#13784) * Add Helvetica and sans-serif as fallback font families This prevents us from using an ugly serif default font when the requested font isn't available. * Use Arial when not on iOS --- lib/web_ui/lib/src/engine/text/paragraph.dart | 10 ++--- lib/web_ui/lib/src/engine/text/ruler.dart | 4 +- lib/web_ui/lib/src/engine/util.dart | 16 ++++++-- lib/web_ui/test/text_test.dart | 41 +++++++++++++++++-- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 2aca66ff297c2..7b6faaf3ed209 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -1080,7 +1080,7 @@ void _applyParagraphStyleToElement({ style._fontStyle == ui.FontStyle.normal ? 'normal' : 'italic'; } if (style._effectiveFontFamily != null) { - cssStyle.fontFamily = quoteFontFamily(style._effectiveFontFamily); + cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily); } } else { if (style._textAlign != previousStyle._textAlign) { @@ -1106,7 +1106,7 @@ void _applyParagraphStyleToElement({ : null; } if (style._fontFamily != previousStyle._fontFamily) { - cssStyle.fontFamily = quoteFontFamily(style._fontFamily); + cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); } } } @@ -1146,11 +1146,11 @@ void _applyTextStyleToElement({ // consistently use Ahem font. if (isSpan && !ui.debugEmulateFlutterTesterEnvironment) { if (style._fontFamily != null) { - cssStyle.fontFamily = quoteFontFamily(style._fontFamily); + cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); } } else { if (style._effectiveFontFamily != null) { - cssStyle.fontFamily = quoteFontFamily(style._effectiveFontFamily); + cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily); } } if (style._letterSpacing != null) { @@ -1184,7 +1184,7 @@ void _applyTextStyleToElement({ : null; } if (style._fontFamily != previousStyle._fontFamily) { - cssStyle.fontFamily = quoteFontFamily(style._fontFamily); + cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); } if (style._letterSpacing != previousStyle._letterSpacing) { cssStyle.letterSpacing = '${style._letterSpacing}px'; diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index 41cbdf0dcb78f..e8c1944aff59c 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -86,7 +86,7 @@ class ParagraphGeometricStyle { result.write(DomRenderer.defaultFontSize); } result.write(' '); - result.write(quoteFontFamily(effectiveFontFamily)); + result.write(canonicalizeFontFamily(effectiveFontFamily)); return result.toString(); } @@ -227,7 +227,7 @@ class TextDimensions { void applyStyle(ParagraphGeometricStyle style) { _element.style ..fontSize = style.fontSize != null ? '${style.fontSize.floor()}px' : null - ..fontFamily = quoteFontFamily(style.effectiveFontFamily) + ..fontFamily = canonicalizeFontFamily(style.effectiveFontFamily) ..fontWeight = style.fontWeight != null ? fontWeightToCss(style.fontWeight) : null ..fontStyle = style.fontStyle != null diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 715d6a30ea3e1..0871364084b0b 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -258,10 +258,20 @@ const Set _genericFontFamilies = { 'fangsong', }; -/// Wraps a font family in quotes unless it is a generic font family. -String quoteFontFamily(String fontFamily) { +/// A default fallback font family in case an unloaded font has been requested. +/// +/// For iOS, default to Helvetica, where it should be available, otherwise +/// default to Arial. +final String _fallbackFontFamily = + operatingSystem == OperatingSystem.iOs ? 'Helvetica' : 'Arial'; + +/// Create a font-family string appropriate for CSS. +/// +/// If the given [fontFamily] is a generic font-family, then just return it. +/// Otherwise, wrap the family name in quotes and add a fallback font family. +String canonicalizeFontFamily(String fontFamily) { if (_genericFontFamilies.contains(fontFamily)) { return fontFamily; } - return '"$fontFamily"'; + return '"$fontFamily", $_fallbackFontFamily, sans-serif'; } diff --git a/lib/web_ui/test/text_test.dart b/lib/web_ui/test/text_test.dart index eecebd711ed06..6889e890faa70 100644 --- a/lib/web_ui/test/text_test.dart +++ b/lib/web_ui/test/text_test.dart @@ -241,9 +241,43 @@ void main() async { expect(paragraph.plainText, isNull); final List spans = paragraph.paragraphElement.querySelectorAll('span'); - expect(spans[0].style.fontFamily, 'Ahem'); + expect(spans[0].style.fontFamily, 'Ahem, Arial, sans-serif'); // The nested span here should not set it's family to default sans-serif. - expect(spans[1].style.fontFamily, 'Ahem'); + expect(spans[1].style.fontFamily, 'Ahem, Arial, sans-serif'); + }); + + test('adds Arial and sans-serif as fallback fonts', () { + // Set this to false so it doesn't default to 'Ahem' font. + debugEmulateFlutterTesterEnvironment = false; + + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'SomeFont', + fontSize: 12.0, + )); + + builder.addText('Hello'); + + final EngineParagraph paragraph = builder.build(); + expect(paragraph.paragraphElement.style.fontFamily, 'SomeFont, Arial, sans-serif'); + + debugEmulateFlutterTesterEnvironment = true; + }); + + test('does not add fallback fonts to generic families', () { + // Set this to false so it doesn't default to 'Ahem' font. + debugEmulateFlutterTesterEnvironment = false; + + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'serif', + fontSize: 12.0, + )); + + builder.addText('Hello'); + + final EngineParagraph paragraph = builder.build(); + expect(paragraph.paragraphElement.style.fontFamily, 'serif'); + + debugEmulateFlutterTesterEnvironment = true; }); test('can set font families that need to be quoted', () { @@ -258,10 +292,11 @@ void main() async { builder.addText('Hello'); final EngineParagraph paragraph = builder.build(); - expect(paragraph.paragraphElement.style.fontFamily, '"MyFont 2000"'); + expect(paragraph.paragraphElement.style.fontFamily, '"MyFont 2000", Arial, sans-serif'); debugEmulateFlutterTesterEnvironment = true; }); + group('TextRange', () { test('empty ranges are correct', () { const TextRange range = TextRange(start: -1, end: -1); From 6d66993b1d257312e61f5117e7788a49a295df5f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 11 Nov 2019 17:17:18 -0800 Subject: [PATCH 090/591] Revert "Reland "Guarding EAGLContext used by Flutter #13314" (#13759)" (#13788) This reverts commit 2dcfaaeb5d3caee2bf7488e90abadf0456f43922. --- ci/licenses_golden/licenses_flutter | 4 - shell/common/BUILD.gn | 2 - shell/common/rasterizer.cc | 4 +- .../common/renderer_context_switch_manager.cc | 30 ----- .../common/renderer_context_switch_manager.h | 116 ------------------ shell/common/shell_test.cc | 12 +- shell/common/shell_test.h | 6 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 ++--- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/gpu/gpu_surface_metal.h | 3 +- shell/gpu/gpu_surface_metal.mm | 5 +- shell/platform/android/android_surface_gl.cc | 13 +- shell/platform/android/android_surface_gl.h | 7 +- shell/platform/darwin/ios/BUILD.gn | 2 - .../framework/Source/FlutterPlatformViews.mm | 16 +-- .../Source/FlutterPlatformViews_Internal.h | 6 - shell/platform/darwin/ios/ios_gl_context.h | 17 +-- shell/platform/darwin/ios/ios_gl_context.mm | 22 ++-- .../ios/ios_gl_context_switch_manager.h | 65 ---------- .../ios/ios_gl_context_switch_manager.mm | 79 ------------ .../darwin/ios/ios_gl_render_target.h | 16 ++- .../darwin/ios/ios_gl_render_target.mm | 43 +++---- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 10 +- shell/platform/darwin/ios/ios_surface_gl.mm | 18 +-- shell/platform/darwin/ios/ios_surface_metal.h | 3 +- .../platform/darwin/ios/ios_surface_metal.mm | 5 +- .../darwin/ios/ios_surface_software.h | 5 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 ++- .../platform/embedder/embedder_surface_gl.cc | 13 +- shell/platform/embedder/embedder_surface_gl.h | 7 +- .../Scenarios.xcodeproj/project.pbxproj | 16 +-- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ---- .../Scenarios/Scenarios/GLTestPlatformView.h | 30 ----- .../Scenarios/Scenarios/GLTestPlatformView.m | 90 -------------- .../ScenariosUITests/PlatformViewGLTests.m | 39 ------ testing/scenario_app/lib/main.dart | 1 - .../scenario_app/lib/src/platform_view.dart | 36 ++---- testing/scenario_app/lib/src/texture.dart | 0 44 files changed, 123 insertions(+), 745 deletions(-) delete mode 100644 shell/common/renderer_context_switch_manager.cc delete mode 100644 shell/common/renderer_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m delete mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index db712f544e80c..5587e1b6bdc5b 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -511,8 +511,6 @@ FILE: ../../../flutter/shell/common/pointer_data_dispatcher.cc FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h FILE: ../../../flutter/shell/common/rasterizer.cc FILE: ../../../flutter/shell/common/rasterizer.h -FILE: ../../../flutter/shell/common/renderer_context_switch_manager.cc -FILE: ../../../flutter/shell/common/renderer_context_switch_manager.h FILE: ../../../flutter/shell/common/run_configuration.cc FILE: ../../../flutter/shell/common/run_configuration.h FILE: ../../../flutter/shell/common/shell.cc @@ -802,8 +800,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index d87b6abede0e5..f93bca63478f0 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -78,8 +78,6 @@ source_set("common") { "pointer_data_dispatcher.h", "rasterizer.cc", "rasterizer.h", - "renderer_context_switch_manager.cc", - "renderer_context_switch_manager.h", "run_configuration.cc", "run_configuration.h", "shell.cc", diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index e66049e0a07e4..d61e1ff86ddc3 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -163,9 +163,7 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, // happen in case of software rendering. surface = SkSurface::MakeRaster(image_info); } else { - std::unique_ptr - context_switch = surface_->MakeRenderContextCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!surface_->MakeRenderContextCurrent()) { return nullptr; } diff --git a/shell/common/renderer_context_switch_manager.cc b/shell/common/renderer_context_switch_manager.cc deleted file mode 100644 index 6bd5dd12832c7..0000000000000 --- a/shell/common/renderer_context_switch_manager.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "renderer_context_switch_manager.h" - -namespace flutter { - -RendererContextSwitchManager::RendererContextSwitchManager() = default; - -RendererContextSwitchManager::~RendererContextSwitchManager() = default; - -RendererContextSwitchManager::RendererContextSwitch::RendererContextSwitch() = - default; - -RendererContextSwitchManager::RendererContextSwitch::~RendererContextSwitch(){}; - -RendererContextSwitchManager::RendererContextSwitchPureResult:: - RendererContextSwitchPureResult(bool switch_result) - : switch_result_(switch_result){}; - -RendererContextSwitchManager::RendererContextSwitchPureResult:: - ~RendererContextSwitchPureResult() = default; - -bool RendererContextSwitchManager::RendererContextSwitchPureResult:: - GetSwitchResult() { - return switch_result_; -} - -} // namespace flutter diff --git a/shell/common/renderer_context_switch_manager.h b/shell/common/renderer_context_switch_manager.h deleted file mode 100644 index 323a3a5e5af32..0000000000000 --- a/shell/common/renderer_context_switch_manager.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ - -#include -#include "flutter/fml/macros.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// Manages `RendererContextSwitch`. -/// -/// Should be subclassed for platforms that uses GL and requires context -/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the -/// `RendererContextSwitchManager` to set gl contexts. -/// -class RendererContextSwitchManager { - public: - //------------------------------------------------------------------------------ - /// Switches the gl context to the flutter's contexts. - /// - /// Should be subclassed for each platform embedder that uses GL. - /// In construction, it should set the current context to a flutter's context - /// In destruction, it should rest the current context. - /// - class RendererContextSwitch { - public: - RendererContextSwitch(); - - virtual ~RendererContextSwitch(); - - virtual bool GetSwitchResult() = 0; - - FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitch); - }; - - RendererContextSwitchManager(); - ~RendererContextSwitchManager(); - - //---------------------------------------------------------------------------- - /// @brief Creates a shell instance using the provided settings. The - /// callbacks to create the various shell subcomponents will be - /// called on the appropriate threads before this method returns. - /// If this is the first instance of a shell in the process, this - /// call also bootstraps the Dart VM. - /// - /// @param[in] task_runners The task runners - /// @param[in] settings The settings - /// @param[in] on_create_platform_view The callback that must return a - /// platform view. This will be called on - /// the platform task runner before this - /// method returns. - /// @param[in] on_create_rasterizer That callback that must provide a - /// valid rasterizer. This will be called - /// on the render task runner before this - /// method returns. - /// - /// @return A full initialized shell if the settings and callbacks are - /// valid. The root isolate has been created but not yet launched. - /// It may be launched by obtaining the engine weak pointer and - /// posting a task onto the UI task runner with a valid run - /// configuration to run the isolate. The embedder must always - /// check the validity of the shell (using the IsSetup call) - /// immediately after getting a pointer to it. - /// - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's context as current context. - /// - /// @return A `RendererContextSwitch` with `GetSwitchResult` returning - /// true if the setting process is succesful. - virtual std::unique_ptr MakeCurrent() = 0; - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's resources context as current context. - /// - /// @return A `RendererContextSwitch` with `GetSwitchResult` returning - /// true if the setting process is succesful. - virtual std::unique_ptr ResourceMakeCurrent() = 0; - - //------------------------------------------------------------------------------ - /// A representation of a `RendererContextSwitch` that doesn't require actual - /// context switching. - /// - class RendererContextSwitchPureResult final : public RendererContextSwitch { - public: - // Constructor that creates an `RendererContextSwitchPureResult`. - // The `GetSwitchResult` will return the same value as `switch_result`. - - //---------------------------------------------------------------------------- - /// @brief Constructs a `RendererContextSwitchPureResult`. - /// - /// @param[in] switch_result the switch result that will be returned - /// in `GetSwitchResult()` - /// - RendererContextSwitchPureResult(bool switch_result); - - ~RendererContextSwitchPureResult(); - - bool GetSwitchResult() override; - - private: - bool switch_result_; - - FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchPureResult); - }; - - FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchManager); -}; - -} // namespace flutter - -#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 3fe5a2b9e0f87..51370d082862b 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,11 +355,8 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -std::unique_ptr -ShellTestPlatformView::GLContextMakeCurrent() { - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>( - gl_surface_.MakeCurrent()); +bool ShellTestPlatformView::GLContextMakeCurrent() { + return gl_surface_.MakeCurrent(); } // |GPUSurfaceGLDelegate| @@ -390,10 +387,5 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } -std::shared_ptr -ShellTestPlatformView::GetRendererContextSwitchManager() { - return nullptr; -} - } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 27d32f058ae0e..fdee9653b71ce 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,8 +144,7 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -162,9 +161,6 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - std::shared_ptr - GetRendererContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index 2b876e0c0f558..b8a77ca1811d4 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,10 +60,8 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -std::unique_ptr -Surface::MakeRenderContextCurrent() { - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>(true); +bool Surface::MakeRenderContextCurrent() { + return true; } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index 0b19725da3b74..7bbc16e24c690 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,7 +10,6 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -59,8 +58,7 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual std::unique_ptr - MakeRenderContextCurrent(); + virtual bool MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index a7648b7f71d39..5f30a48375d33 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -9,7 +9,6 @@ #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" #include "flutter/shell/common/persistent_cache.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -40,10 +39,7 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -91,6 +87,8 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; + + delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -100,9 +98,7 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -118,9 +114,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -131,6 +126,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; + + delegate_->GLContextClearCurrent(); } // |Surface| @@ -256,9 +253,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -290,9 +285,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - std::unique_ptr result = - std::make_unique(surface, submit_callback); - return result; + return std::make_unique(surface, submit_callback); } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -300,8 +293,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -338,6 +329,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } + return true; } @@ -368,8 +360,7 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -std::unique_ptr -GPUSurfaceGL::MakeRenderContextCurrent() { +bool GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index eb67440eee07c..97325569bfd16 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,8 +44,7 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - std::unique_ptr - MakeRenderContextCurrent() override; + bool MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 444e7301c1939..dfe0ce7f468db 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,7 +7,6 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -17,8 +16,7 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual std::unique_ptr - GLContextMakeCurrent() = 0; + virtual bool GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -61,9 +59,6 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; - - virtual std::shared_ptr - GetRendererContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index acb5ca02a7162..fc6b0964766ce 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -49,8 +49,7 @@ class GPUSurfaceMetal : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - std::unique_ptr MakeRenderContextCurrent() - override; + bool MakeRenderContextCurrent() override; void ReleaseUnusedDrawableIfNecessary(); diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 2aba2cd41f376..81abf740d48f6 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -175,10 +175,9 @@ } // |Surface| -std::unique_ptr -GPUSurfaceMetal::MakeRenderContextCurrent() { +bool GPUSurfaceMetal::MakeRenderContextCurrent() { // This backend has no such concept. - return std::make_unique(true); + return true; } void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() { diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index a06b13e68f6bb..737d9f293a518 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,12 +104,9 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -std::unique_ptr -AndroidSurfaceGL::GLContextMakeCurrent() { +bool AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>( - onscreen_context_->MakeCurrent()); + return onscreen_context_->MakeCurrent(); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -133,10 +130,4 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -AndroidSurfaceGL::GetRendererContextSwitchManager() { - return nullptr; -} - } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index bfe7b2ce06fb2..d59302ad66509 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,8 +47,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -62,10 +61,6 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr - GetRendererContextSwitchManager() override; - private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 3e67beb97e717..a66c3bf48c5ae 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,8 +86,6 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", - "ios_gl_context_switch_manager.h", - "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 487fd42f5ea0d..33ca14d9fabea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,11 +160,6 @@ frame_size_ = frame_size; } -void FlutterPlatformViewsController::SetRendererContextSwitchManager( - std::shared_ptr gl_context_guard_manager) { - renderer_context_switch_manager_ = gl_context_guard_manager; -} - void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -373,12 +368,7 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - if (renderer_context_switch_manager_ != nullptr) { - std::unique_ptr contextSwitch = - renderer_context_switch_manager_->MakeCurrent(); - } - - EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); + EnsureOverlayInitialized(view_id, gl_context, gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -465,10 +455,6 @@ std::shared_ptr gl_context, GrContext* gr_context) { FML_DCHECK(flutter_view_); - if (renderer_context_switch_manager_ != nullptr) { - std::unique_ptr contextSwitch = - renderer_context_switch_manager_->MakeCurrent(); - } auto overlay_it = overlays_.find(overlay_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 77b8ece44dc5d..c8daeaa605946 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,7 +11,6 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -81,9 +80,6 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); - void SetRendererContextSwitchManager( - std::shared_ptr gl_context_guard_manager); - void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -208,8 +204,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - std::shared_ptr renderer_context_switch_manager_; - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 8c73ce75dc028..232645d9c8592 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,25 +26,16 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - std::unique_ptr - MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); - - std::shared_ptr GetIOSGLContextSwitchManager() { - return renderer_context_switch_manager_; - } + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } - fml::scoped_nsobject GetContext() const { - return renderer_context_switch_manager_->GetContext(); - } - private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; sk_sp color_space_; - std::shared_ptr renderer_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index a54387731e8d9..52fb85f8f19a9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,7 +13,15 @@ namespace flutter { IOSGLContext::IOSGLContext() { - renderer_context_switch_manager_ = std::make_shared(); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -40,16 +48,16 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), renderer_context_switch_manager_); + return std::make_unique(std::move(layer), context_.get(), + resource_context_.get()); } -std::unique_ptr IOSGLContext::MakeCurrent() { - return renderer_context_switch_manager_->MakeCurrent(); +bool IOSGLContext::MakeCurrent() { + return [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr -IOSGLContext::ResourceMakeCurrent() { - return renderer_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLContext::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h deleted file mode 100644 index 56ee4f2eabb3e..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ - -#define GLES_SILENCE_DEPRECATION - -#import -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// The iOS implementation of `RendererContextSwitchManager`. -/// -/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and -/// sets the flutter's gl context as current. -/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to -/// current. -/// -class IOSGLContextSwitchManager final : public RendererContextSwitchManager { - public: - class IOSGLContextSwitch final : public RendererContextSwitch { - public: - IOSGLContextSwitch(IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context); - - ~IOSGLContextSwitch(); - - bool GetSwitchResult() override; - - private: - IOSGLContextSwitchManager& manager_; - bool switch_result_; - bool has_pushed_context_; - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); - }; - - IOSGLContextSwitchManager(); - - ~IOSGLContextSwitchManager(); - - std::unique_ptr MakeCurrent() override; - std::unique_ptr ResourceMakeCurrent() override; - - fml::scoped_nsobject GetContext(); - - private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; - fml::scoped_nsobject stored_; - - bool PushContext(fml::scoped_nsobject context); - void PopContext(); - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); -}; - -} - -#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm deleted file mode 100644 index 0be0a3bcb4df7..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ios_gl_context_switch_manager.h" - -namespace flutter { - -IOSGLContextSwitchManager::IOSGLContextSwitchManager() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } -}; - -IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; - -std::unique_ptr -IOSGLContextSwitchManager::MakeCurrent() { - return std::make_unique(*this, context_); -} - -std::unique_ptr -IOSGLContextSwitchManager::ResourceMakeCurrent() { - return std::make_unique(*this, resource_context_); -} - -fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { - return context_; -} - -bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { - EAGLContext* current = [EAGLContext currentContext]; - if (current == nil) { - [stored_.get() addObject:[NSNull null]]; - } else { - [stored_.get() addObject:current]; - } - bool result = [EAGLContext setCurrentContext:context.get()]; - return result; -} - -void IOSGLContextSwitchManager::PopContext() { - EAGLContext* last = [stored_.get() lastObject]; - [stored_.get() removeLastObject]; - if ([last isEqual:[NSNull null]]) { - [EAGLContext setCurrentContext:nil]; - return; - } - [EAGLContext setCurrentContext:last]; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( - IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context) - : manager_(manager) { - bool result = manager_.PushContext(context); - has_pushed_context_ = true; - switch_result_ = result; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { - if (!has_pushed_context_) { - return; - } - manager_.PopContext(); -} - -bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { - return switch_result_; -} -} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index f52c562fd96d0..b2eafe16e0950 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,15 +13,14 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager); + IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context); ~IOSGLRenderTarget(); @@ -33,17 +32,16 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - std::unique_ptr - MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - std::shared_ptr renderer_context_switch_manager_; + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a7037746369db..a57ba9c46b414 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,20 +12,22 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager) +IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context) : layer_(std::move(layer)), - renderer_context_switch_manager_(gl_context_guard_manager), + context_([context retain]), + resource_context_([resource_context retain]), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - std::unique_ptr context_switch = - renderer_context_switch_manager_->MakeCurrent(); - bool context_current = context_switch->GetSwitchResult(); + FML_DCHECK(context_ != nullptr); + FML_DCHECK(resource_context_ != nullptr); + + bool context_current = [EAGLContext setCurrentContext:context_]; FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -60,8 +62,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - std::unique_ptr context_switch = - renderer_context_switch_manager_->MakeCurrent(); + EAGLContext* context = EAGLContext.currentContext; + [EAGLContext setCurrentContext:context_]; FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -69,6 +71,7 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); + [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -101,9 +104,8 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - std::unique_ptr context_switch = - renderer_context_switch_manager_->MakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (![EAGLContext setCurrentContext:context_]) { return false; } @@ -114,8 +116,7 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![renderer_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER - fromDrawable:layer_.get()]) { + if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { return false; } @@ -131,18 +132,12 @@ return true; } -std::unique_ptr -IOSGLRenderTarget::MakeCurrent() { - bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); - if (!isUpdateSuccessful) { - return std::make_unique(false); - } - return renderer_context_switch_manager_->MakeCurrent(); +bool IOSGLRenderTarget::MakeCurrent() { + return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr -IOSGLRenderTarget::ResourceMakeCurrent() { - return renderer_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLRenderTarget::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index b852a63ee4e9a..49f40f9eec76a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,7 +12,6 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -28,8 +27,7 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual std::unique_ptr - ResourceContextMakeCurrent() = 0; + virtual bool ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 708413c239400..c1019bb442bb0 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,7 +9,6 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -33,8 +32,7 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() - override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -42,8 +40,7 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - std::unique_ptr GLContextMakeCurrent() - override; + bool GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -56,9 +53,6 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetRendererContextSwitchManager() override; - // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 6417e7d899b42..48e70e00a4e7a 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,8 +28,7 @@ return render_target_->IsValid(); } -std::unique_ptr -IOSSurfaceGL::ResourceContextMakeCurrent() { +bool IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -57,12 +56,11 @@ return true; } -std::unique_ptr -IOSSurfaceGL::GLContextMakeCurrent() { +bool IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return std::make_unique(false); + return false; } - return render_target_->MakeCurrent(); + return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -75,11 +73,6 @@ return IsValid() && render_target_->PresentRenderBuffer(); } -// |GPUSurfaceGLDelegate| -std::shared_ptr IOSSurfaceGL::GetRendererContextSwitchManager() { - return context_->GetIOSGLContextSwitchManager(); -} - // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -151,8 +144,7 @@ if (platform_views_controller == nullptr) { return true; } - platform_views_controller->SetRendererContextSwitchManager( - context_->GetIOSGLContextSwitchManager()); + bool submitted = platform_views_controller->SubmitFrame(std::move(context), context_); [CATransaction commit]; return submitted; diff --git a/shell/platform/darwin/ios/ios_surface_metal.h b/shell/platform/darwin/ios/ios_surface_metal.h index 1263571870bd4..2b001ea2b692d 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.h +++ b/shell/platform/darwin/ios/ios_surface_metal.h @@ -30,8 +30,7 @@ class IOSSurfaceMetal final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() - override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_metal.mm b/shell/platform/darwin/ios/ios_surface_metal.mm index 827db668afa1f..58a5fc328647b 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.mm +++ b/shell/platform/darwin/ios/ios_surface_metal.mm @@ -22,9 +22,8 @@ } // |IOSSurface| -std::unique_ptr -IOSSurfaceMetal::ResourceContextMakeCurrent() { - return std::make_unique(false); +bool IOSSurfaceMetal::ResourceContextMakeCurrent() { + return false; } // |IOSSurface| diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index c0b6298e47bf7..daac2ffc77231 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" + @class CALayer; namespace flutter { @@ -28,8 +28,7 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() - override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 8c38ab5861e82..ab5490cf25140 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,9 +27,8 @@ return layer_; } -std::unique_ptr -IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return std::make_unique(false); +bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return false; } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index d867fc68fc6c8..bb37fa9610b2b 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,18 +107,15 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (gl_context_ != nullptr) { - std::unique_ptr context_switch = - gl_context_->ResourceMakeCurrent(); - if (context_switch->GetSwitchResult()) { - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); - } + if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 2efa1c01baf46..d37b03aae8d9e 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,11 +34,8 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -std::unique_ptr -EmbedderSurfaceGL::GLContextMakeCurrent() { - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>( - gl_dispatch_table_.gl_make_current_callback()); +bool EmbedderSurfaceGL::GLContextMakeCurrent() { + return gl_dispatch_table_.gl_make_current_callback(); } // |GPUSurfaceGLDelegate| @@ -82,12 +79,6 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -EmbedderSurfaceGL::GetRendererContextSwitchManager() { - return nullptr; -} - // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 12f264f383c37..a01fa05d4e62c 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,8 +50,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -74,10 +73,6 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; - // |GPUSurfaceGLDelegate| - std::shared_ptr - GetRendererContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index dd2238aba3181..e667c4c88678d 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,8 +40,6 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -138,9 +136,6 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -207,8 +202,6 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -246,7 +239,6 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -406,7 +398,6 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -427,7 +418,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -502,7 +492,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -555,7 +545,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -571,7 +561,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -595,7 +584,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index cd1c5b7366a89..87799ad5e6434 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,15 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + + + + + * registrar = - [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; - [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; - self.window.rootViewController = flutterViewController; -} - @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h deleted file mode 100644 index 19cefe7025986..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GLTestPlatformView : NSObject - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; - -- (UIView*)view; - -@end - -@interface GLTestPlatformViewFactory : NSObject - -- (instancetype)initWithMessenger:(NSObject*)messenger; - -@end - -@interface GLTestView : UIView - -@end - -NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m deleted file mode 100644 index 8142550e52863..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "GLTestPlatformView.h" - -#define GLES_SILENCE_DEPRECATION - -@implementation GLTestPlatformView { - int64_t _viewId; - GLTestView* _view; -} - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id)args - binaryMessenger:(NSObject*)messenger { - if ([super init]) { - _viewId = viewId; - _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; - } - return self; -} - -- (UIView*)view { - return _view; -} - -@end - -@implementation GLTestPlatformViewFactory { - NSObject* _messenger; -} - -- (instancetype)initWithMessenger:(NSObject*)messenger { - self = [super init]; - if (self) { - _messenger = messenger; - } - return self; -} - -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger]; - return platformView; -} - -- (NSObject*)createArgsCodec { - return [FlutterStringCodec sharedInstance]; -} - -@end - -@interface GLTestView () - -@property(strong, nonatomic) EAGLContext* context; - -@end - -@implementation GLTestView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - _context.debugLabel = @"platform view context"; - [EAGLContext setCurrentContext:_context]; - self.backgroundColor = [UIColor redColor]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self checkEAGLContext]; - }); - } - return self; -} - -- (void)checkEAGLContext { - if ([EAGLContext currentContext] != _context) { - self.accessibilityIdentifier = @"gl_platformview_wrong_context"; - } else { - self.accessibilityIdentifier = @"gl_platformview_correct_context"; - } -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m deleted file mode 100644 index d581926ae5a6a..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface PlatformViewGLTests : XCTestCase - -@property(nonatomic, strong) XCUIApplication* application; - -@end - -@implementation PlatformViewGLTests - -- (void)setUp { - self.continueAfterFailure = NO; - - self.application = [[XCUIApplication alloc] init]; - self.application.launchArguments = @[ @"--platform-view-gl" ]; - [self.application launch]; -} - -- (void)testExample { - NSPredicate* predicateToFindPlatformView = - [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, - NSDictionary* _Nullable bindings) { - XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || - [element.identifier isEqualToString:@"gl_platformview_correct_context"]; - }]; - XCUIElement* firstElement = - [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; - if (![firstElement waitForExistenceWithTimeout:30]) { - XCTFail(@"Failed due to not able to find platform view with 30 seconds"); - } - XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); -} - -@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 82b589a6681fe..7a264b70008f0 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,7 +25,6 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), - 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 86fb4df1df449..05efe52fa8b25 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -281,24 +281,6 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } -/// Platform view scenario for testing EAGLContext on iOS. -class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Constructs a platform view to test EAGLContext on iOS. - PlatformViewGLScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - finishBuilderByAddingPlatformViewAndPicture(builder, 6); - } -} - mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -307,7 +289,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id, String viewType) { + void createPlatformView(Window window, String text, int id) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -331,8 +313,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - viewType.length, - ...utf8.encode(viewType), + 'scenarios/textPlatformView'.length, + ...utf8.encode('scenarios/textPlatformView'), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 63e8c6275e4ecdc9d0b26110b5ea2dea93bbfad8 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 11 Nov 2019 17:20:45 -0800 Subject: [PATCH 091/591] Add ImageFilter and BackdropFilter to CanvasKit backend (#13768) * Implement ImageFilter.blur and BackdropFilter * update licenses file * Respond to review comments --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 1 + .../lib/src/engine/compositor/canvas.dart | 13 ++++++++++ .../src/engine/compositor/image_filter.dart | 24 +++++++++++++++++++ .../src/engine/compositor/initialization.dart | 2 +- .../lib/src/engine/compositor/layer.dart | 13 ++++++++++ .../compositor/layer_scene_builder.dart | 3 ++- .../lib/src/engine/compositor/util.dart | 6 +++++ lib/web_ui/lib/src/engine/shader.dart | 10 ++++++++ .../src/engine/surface/backdrop_filter.dart | 3 ++- lib/web_ui/lib/src/ui/painting.dart | 21 +++++++--------- 11 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/compositor/image_filter.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5587e1b6bdc5b..7b86febc4a3b9 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -361,6 +361,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/fonts.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/image.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/image_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/initialization.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index d44e33dd9d176..7a130e0909e54 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -29,6 +29,7 @@ part 'engine/compositor/color_filter.dart'; part 'engine/compositor/engine_delegate.dart'; part 'engine/compositor/fonts.dart'; part 'engine/compositor/image.dart'; +part 'engine/compositor/image_filter.dart'; part 'engine/compositor/initialization.dart'; part 'engine/compositor/layer.dart'; part 'engine/compositor/layer_scene_builder.dart'; diff --git a/lib/web_ui/lib/src/engine/compositor/canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas.dart index cba6d481dcafb..b224faa0ad41c 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas.dart @@ -33,6 +33,19 @@ class SkCanvas { 'saveLayer', [makeSkRect(bounds), makeSkPaint(paint)]); } + int saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { + final SkImageFilter skImageFilter = filter; + return skCanvas.callMethod( + 'saveLayer', + [ + null, + skImageFilter.skImageFilter, + 0, + makeSkRect(bounds), + ], + ); + } + void restore() { skCanvas.callMethod('restore'); } diff --git a/lib/web_ui/lib/src/engine/compositor/image_filter.dart b/lib/web_ui/lib/src/engine/compositor/image_filter.dart new file mode 100644 index 0000000000000..b638ffc13a068 --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/image_filter.dart @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// The CanvasKit implementation of [ui.ImageFilter]. +/// +/// Currently only supports `blur`. +class SkImageFilter implements ui.ImageFilter { + js.JsObject skImageFilter; + + SkImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0}) { + skImageFilter = canvasKit['SkImageFilter'].callMethod( + 'MakeBlur', + [ + sigmaX, + sigmaY, + canvasKit['TileMode']['Clamp'], + null, + ], + ); + } +} diff --git a/lib/web_ui/lib/src/engine/compositor/initialization.dart b/lib/web_ui/lib/src/engine/compositor/initialization.dart index 5ac06638edd8b..9f043e15cf827 100644 --- a/lib/web_ui/lib/src/engine/compositor/initialization.dart +++ b/lib/web_ui/lib/src/engine/compositor/initialization.dart @@ -9,7 +9,7 @@ const bool experimentalUseSkia = bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false); /// The URL to use when downloading the CanvasKit script and associated wasm. -const String canvasKitBaseUrl = 'https://unpkg.com/canvaskit-wasm@0.7.0/bin/'; +const String canvasKitBaseUrl = 'https://particles.skia.org/static/'; /// Initialize the Skia backend. /// diff --git a/lib/web_ui/lib/src/engine/compositor/layer.dart b/lib/web_ui/lib/src/engine/compositor/layer.dart index 9c03a8b5c58dc..e952d090b105b 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer.dart @@ -93,6 +93,19 @@ abstract class ContainerLayer extends Layer { } } +class BackdropFilterLayer extends ContainerLayer { + final ui.ImageFilter _filter; + + BackdropFilterLayer(this._filter); + + @override + void paint(PaintContext context) { + context.canvas.saveLayerWithFilter(paintBounds, _filter); + paintChildren(context); + context.canvas.restore(); + } +} + /// A layer that clips its child layers by a given [Path]. class ClipPathLayer extends ContainerLayer { /// The path used to clip child layers. diff --git a/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart index 0bb05f096d3f2..b92e0f903284e 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart @@ -92,7 +92,8 @@ class LayerSceneBuilder implements ui.SceneBuilder { @override ui.BackdropFilterEngineLayer pushBackdropFilter(ui.ImageFilter filter, {ui.EngineLayer oldLayer}) { - throw UnimplementedError(); + pushLayer(BackdropFilterLayer(filter)); + return null; } @override diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index 157e3b9ffdcb1..d97ddfc965258 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -156,6 +156,12 @@ js.JsObject makeSkPaint(ui.Paint paint) { skPaint.callMethod('setMaskFilter', [skMaskFilter]); } + if (paint.imageFilter != null) { + final SkImageFilter skImageFilter = paint.imageFilter; + skPaint.callMethod( + 'setImageFilter', [skImageFilter.skImageFilter]); + } + if (paint.colorFilter != null) { EngineColorFilter engineFilter = paint.colorFilter; SkColorFilter skFilter = engineFilter._toSkColorFilter(); diff --git a/lib/web_ui/lib/src/engine/shader.dart b/lib/web_ui/lib/src/engine/shader.dart index fb591fa6f9900..cb1f37938be7d 100644 --- a/lib/web_ui/lib/src/engine/shader.dart +++ b/lib/web_ui/lib/src/engine/shader.dart @@ -231,3 +231,13 @@ class GradientConical extends EngineGradient { throw UnimplementedError(); } } + +/// Backend implementation of [ui.ImageFilter]. +/// +/// Currently only `blur` is supported. +class EngineImageFilter implements ui.ImageFilter { + EngineImageFilter.blur({this.sigmaX = 0.0, this.sigmaY = 0.0}); + + final double sigmaX; + final double sigmaY; +} diff --git a/lib/web_ui/lib/src/engine/surface/backdrop_filter.dart b/lib/web_ui/lib/src/engine/surface/backdrop_filter.dart index 1b87e2563752f..efb7b79de3af8 100644 --- a/lib/web_ui/lib/src/engine/surface/backdrop_filter.dart +++ b/lib/web_ui/lib/src/engine/surface/backdrop_filter.dart @@ -85,11 +85,12 @@ class PersistedBackdropFilter extends PersistedContainerSurface ..backgroundColor = '#000' ..opacity = '0.2'; } else { + final EngineImageFilter engineFilter = filter; // CSS uses pixel radius for blur. Flutter & SVG use sigma parameters. For // Gaussian blur with standard deviation (normal distribution), // the blur will fall within 2 * sigma pixels. domRenderer.setElementStyle(_filterElement, 'backdrop-filter', - 'blur(${math.max(filter.sigmaX, filter.sigmaY) * 2}px)'); + 'blur(${math.max(engineFilter.sigmaX, engineFilter.sigmaY) * 2}px)'); } } diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index 5befab9acbee5..ff280ad0bda16 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -1055,7 +1055,7 @@ class Paint { _paintData = _paintData.clone(); _frozen = false; } - _paintData.color = value.runtimeType == Color ? value : Color(value.value); + _paintData.color = value.runtimeType == Color ? value : Color(value.value); } /// Whether the colors of the image are inverted when drawn. @@ -1604,24 +1604,21 @@ enum FilterQuality { /// this class. class ImageFilter { /// Creates an image filter that applies a Gaussian blur. - ImageFilter.blur({this.sigmaX = 0.0, this.sigmaY = 0.0}) - : matrix4 = null, - filterQuality = FilterQuality.low; + factory ImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0}) { + if (engine.experimentalUseSkia) { + return engine.SkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY); + } + return engine.EngineImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY); + } - ImageFilter.matrix(this.matrix4, {this.filterQuality = FilterQuality.low}) - : sigmaX = 0.0, - sigmaY = 0.0 { + ImageFilter.matrix(Float64List matrix4, + {FilterQuality filterQuality = FilterQuality.low}) { // TODO(flutter_web): add implementation. throw UnimplementedError( 'ImageFilter.matrix not implemented for web platform.'); // if (matrix4.length != 16) // throw ArgumentError('"matrix4" must have 16 entries.'); } - - final Float64List matrix4; - final FilterQuality filterQuality; - final double sigmaX; - final double sigmaY; } /// The format in which image bytes should be returned when using From e150bf330571cd3cb54f97cc09abf7646bfa624d Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 11 Nov 2019 18:20:08 -0800 Subject: [PATCH 092/591] Take devicePixelRatio into account when drawing shadows (#13786) * Take devicePixelRatio into account when drawing shadows This fixes a bug where shadows were offset in CanvasKit * Respond to review comments --- .../lib/src/engine/compositor/canvas.dart | 3 +- .../lib/src/engine/compositor/layer.dart | 56 ++++++++++++++++++- .../engine/compositor/recording_canvas.dart | 3 +- .../lib/src/engine/compositor/util.dart | 12 +++- 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/lib/web_ui/lib/src/engine/compositor/canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas.dart index b224faa0ad41c..81109c047e9e2 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas.dart @@ -108,6 +108,7 @@ class SkCanvas { void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder); + drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, + ui.window.devicePixelRatio); } } diff --git a/lib/web_ui/lib/src/engine/compositor/layer.dart b/lib/web_ui/lib/src/engine/compositor/layer.dart index e952d090b105b..f50ae53f9ef3f 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer.dart @@ -341,8 +341,60 @@ class PhysicalShapeLayer extends ContainerLayer @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { prerollChildren(prerollContext, matrix); - paintBounds = - ElevationShadow.computeShadowRect(_path.getBounds(), _elevation); + + paintBounds = _path.getBounds(); + if (_elevation == 0.0) { + // No need to extend the paint bounds if there is no shadow. + return; + } else { + // Add some margin to the paint bounds to leave space for the shadow. + // We fill this whole region and clip children to it so we don't need to + // join the child paint bounds. + // The offset is calculated as follows: + + // .--- (kLightRadius) + // -------/ (light) + // | / + // | / + // |/ + // |O + // /| (kLightHeight) + // / | + // / | + // / | + // / | + // ------------- (layer) + // /| | + // / | | (elevation) + // A / | |B + // ------------------------------------------------ (canvas) + // --- (extent of shadow) + // + // E = lt } t = (r + w/2)/h + // } => + // r + w/2 = ht } E = (l/h)(r + w/2) + // + // Where: E = extent of shadow + // l = elevation of layer + // r = radius of the light source + // w = width of the layer + // h = light height + // t = tangent of AOB, i.e., multiplier for elevation to extent + final double devicePixelRatio = ui.window.devicePixelRatio; + + final double radius = kLightRadius * devicePixelRatio; + // tangent for x + double tx = (radius + paintBounds.width * 0.5) / kLightHeight; + // tangent for y + double ty = (radius + paintBounds.height * 0.5) / kLightHeight; + + paintBounds = ui.Rect.fromLTRB( + paintBounds.left - tx, + paintBounds.top - ty, + paintBounds.right + tx, + paintBounds.bottom + ty, + ); + } } @override diff --git a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart index e6ab693615f76..6cb4b33ef69e3 100644 --- a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart @@ -228,7 +228,8 @@ class SkRecordingCanvas implements RecordingCanvas { @override void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder); + drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, + ui.window.devicePixelRatio); } @override diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index d97ddfc965258..6575c73692eff 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -193,12 +193,17 @@ js.JsArray makeSkMatrix(Float64List matrix4) { return skMatrix; } +// These must be kept in sync with `flow/layers/physical_shape_layer.cc`. +const double kLightHeight = 600.0; +const double kLightRadius = 800.0; + void drawSkShadow( js.JsObject skCanvas, SkPath path, ui.Color color, double elevation, bool transparentOccluder, + double devicePixelRatio, ) { const double ambientAlpha = 0.039; const double spotAlpha = 0.25; @@ -222,9 +227,10 @@ void drawSkShadow( skCanvas.callMethod('drawShadow', [ path._skPath, - js.JsArray.from([0, 0, elevation]), - js.JsArray.from([shadowX, shadowY, 600]), - 800, + js.JsArray.from([0, 0, devicePixelRatio * elevation]), + js.JsArray.from( + [shadowX, shadowY, devicePixelRatio * kLightHeight]), + devicePixelRatio * kLightRadius, tonalColors['ambient'], tonalColors['spot'], flags, From 1a7bf8578d7e3e4bdc3cbb731e1e6eaf32e54594 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 11 Nov 2019 21:27:15 -0800 Subject: [PATCH 093/591] Move Path and PathMetrics from canvas.dart into their own files. No delta (#13772) * Move Path and PathMetrics into their own files. No delta * update file list in licenses_flutter --- ci/licenses_golden/licenses_flutter | 2 + lib/web_ui/lib/src/ui/canvas.dart | 1273 ----------------------- lib/web_ui/lib/src/ui/path.dart | 1107 ++++++++++++++++++++ lib/web_ui/lib/src/ui/path_metrics.dart | 176 ++++ lib/web_ui/lib/ui.dart | 2 + 5 files changed, 1287 insertions(+), 1273 deletions(-) create mode 100644 lib/web_ui/lib/src/ui/path.dart create mode 100644 lib/web_ui/lib/src/ui/path_metrics.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7b86febc4a3b9..9863294e890eb 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -446,6 +446,8 @@ FILE: ../../../flutter/lib/web_ui/lib/src/ui/initialization.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/lerp.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/natives.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/painting.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/path.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/path_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/pointer.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/semantics.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/test_embedding.dart diff --git a/lib/web_ui/lib/src/ui/canvas.dart b/lib/web_ui/lib/src/ui/canvas.dart index 941786abd5947..0b58e57b44226 100644 --- a/lib/web_ui/lib/src/ui/canvas.dart +++ b/lib/web_ui/lib/src/ui/canvas.dart @@ -1183,1279 +1183,6 @@ enum PathOperation { reverseDifference, } -/// A complex, one-dimensional subset of a plane. -/// -/// A path consists of a number of subpaths, and a _current point_. -/// -/// Subpaths consist of segments of various types, such as lines, -/// arcs, or beziers. Subpaths can be open or closed, and can -/// self-intersect. -/// -/// Closed subpaths enclose a (possibly discontiguous) region of the -/// plane based on the current [fillType]. -/// -/// The _current point_ is initially at the origin. After each -/// operation adding a segment to a subpath, the current point is -/// updated to the end of that segment. -/// -/// Paths can be drawn on canvases using [Canvas.drawPath], and can -/// used to create clip regions using [Canvas.clipPath]. -class Path { - final List subpaths; - PathFillType _fillType = PathFillType.nonZero; - - engine.Subpath get _currentSubpath => subpaths.isEmpty ? null : subpaths.last; - - List get _commands => _currentSubpath?.commands; - - /// The current x-coordinate for this path. - double get _currentX => _currentSubpath?.currentX ?? 0.0; - - /// The current y-coordinate for this path. - double get _currentY => _currentSubpath?.currentY ?? 0.0; - - /// Recorder used for hit testing paths. - static RawRecordingCanvas _rawRecorder; - - /// Create a new empty [Path] object. - factory Path() { - if (engine.experimentalUseSkia) { - return engine.SkPath(); - } else { - return Path._(); - } - } - - Path._() : subpaths = []; - - /// Creates a copy of another [Path]. - /// - /// This copy is fast and does not require additional memory unless either - /// the `source` path or the path returned by this constructor are modified. - Path.from(Path source) - : subpaths = List.from(source.subpaths); - - Path._clone(this.subpaths, this._fillType); - - /// Determines how the interior of this path is calculated. - /// - /// Defaults to the non-zero winding rule, [PathFillType.nonZero]. - PathFillType get fillType => _fillType; - set fillType(PathFillType value) { - _fillType = value; - } - - /// Opens a new subpath with starting point (x, y). - void _openNewSubpath(double x, double y) { - subpaths.add(engine.Subpath(x, y)); - _setCurrentPoint(x, y); - } - - /// Sets the current point to (x, y). - void _setCurrentPoint(double x, double y) { - _currentSubpath.currentX = x; - _currentSubpath.currentY = y; - } - - /// Starts a new subpath at the given coordinate. - void moveTo(double x, double y) { - _openNewSubpath(x, y); - _commands.add(engine.MoveTo(x, y)); - } - - /// Starts a new subpath at the given offset from the current point. - void relativeMoveTo(double dx, double dy) { - final double newX = _currentX + dx; - final double newY = _currentY + dy; - _openNewSubpath(newX, newY); - _commands.add(engine.MoveTo(newX, newY)); - } - - /// Adds a straight line segment from the current point to the given - /// point. - void lineTo(double x, double y) { - if (subpaths.isEmpty) { - moveTo(0.0, 0.0); - } - _commands.add(engine.LineTo(x, y)); - _setCurrentPoint(x, y); - } - - /// Adds a straight line segment from the current point to the point - /// at the given offset from the current point. - void relativeLineTo(double dx, double dy) { - final double newX = _currentX + dx; - final double newY = _currentY + dy; - if (subpaths.isEmpty) { - moveTo(0.0, 0.0); - } - _commands.add(engine.LineTo(newX, newY)); - _setCurrentPoint(newX, newY); - } - - void _ensurePathStarted() { - if (subpaths.isEmpty) { - subpaths.add(engine.Subpath(0.0, 0.0)); - } - } - - /// Adds a quadratic bezier segment that curves from the current - /// point to the given point (x2,y2), using the control point - /// (x1,y1). - void quadraticBezierTo(double x1, double y1, double x2, double y2) { - _ensurePathStarted(); - _commands.add(engine.QuadraticCurveTo(x1, y1, x2, y2)); - _setCurrentPoint(x2, y2); - } - - /// Adds a quadratic bezier segment that curves from the current - /// point to the point at the offset (x2,y2) from the current point, - /// using the control point at the offset (x1,y1) from the current - /// point. - void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { - _ensurePathStarted(); - _commands.add(engine.QuadraticCurveTo( - x1 + _currentX, y1 + _currentY, x2 + _currentX, y2 + _currentY)); - _setCurrentPoint(x2 + _currentX, y2 + _currentY); - } - - /// Adds a cubic bezier segment that curves from the current point - /// to the given point (x3,y3), using the control points (x1,y1) and - /// (x2,y2). - void cubicTo( - double x1, double y1, double x2, double y2, double x3, double y3) { - _ensurePathStarted(); - _commands.add(engine.BezierCurveTo(x1, y1, x2, y2, x3, y3)); - _setCurrentPoint(x3, y3); - } - - /// Adds a cubic bezier segment that curves from the current point - /// to the point at the offset (x3,y3) from the current point, using - /// the control points at the offsets (x1,y1) and (x2,y2) from the - /// current point. - void relativeCubicTo( - double x1, double y1, double x2, double y2, double x3, double y3) { - _ensurePathStarted(); - _commands.add(engine.BezierCurveTo(x1 + _currentX, y1 + _currentY, - x2 + _currentX, y2 + _currentY, x3 + _currentX, y3 + _currentY)); - _setCurrentPoint(x3 + _currentX, y3 + _currentY); - } - - /// Adds a bezier segment that curves from the current point to the - /// given point (x2,y2), using the control points (x1,y1) and the - /// weight w. If the weight is greater than 1, then the curve is a - /// hyperbola; if the weight equals 1, it's a parabola; and if it is - /// less than 1, it is an ellipse. - void conicTo(double x1, double y1, double x2, double y2, double w) { - final List quads = - engine.Conic(_currentX, _currentY, x1, y1, x2, y2, w).toQuads(); - final int len = quads.length; - for (int i = 1; i < len; i += 2) { - quadraticBezierTo( - quads[i].dx, quads[i].dy, quads[i + 1].dx, quads[i + 1].dy); - } - } - - /// Adds a bezier segment that curves from the current point to the - /// point at the offset (x2,y2) from the current point, using the - /// control point at the offset (x1,y1) from the current point and - /// the weight w. If the weight is greater than 1, then the curve is - /// a hyperbola; if the weight equals 1, it's a parabola; and if it - /// is less than 1, it is an ellipse. - void relativeConicTo(double x1, double y1, double x2, double y2, double w) { - conicTo(_currentX + x1, _currentY + y1, _currentX + x2, _currentY + y2, w); - } - - /// If the `forceMoveTo` argument is false, adds a straight line - /// segment and an arc segment. - /// - /// If the `forceMoveTo` argument is true, starts a new subpath - /// consisting of an arc segment. - /// - /// In either case, the arc segment consists of the arc that follows - /// the edge of the oval bounded by the given rectangle, from - /// startAngle radians around the oval up to startAngle + sweepAngle - /// radians around the oval, with zero radians being the point on - /// the right hand side of the oval that crosses the horizontal line - /// that intersects the center of the rectangle and with positive - /// angles going clockwise around the oval. - /// - /// The line segment added if `forceMoveTo` is false starts at the - /// current point and ends at the start of the arc. - void arcTo( - Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { - assert(engine.rectIsValid(rect)); - final Offset center = rect.center; - final double radiusX = rect.width / 2; - final double radiusY = rect.height / 2; - final double startX = radiusX * math.cos(startAngle) + center.dx; - final double startY = radiusY * math.sin(startAngle) + center.dy; - if (forceMoveTo) { - _openNewSubpath(startX, startY); - } else { - lineTo(startX, startY); - } - _commands.add(engine.Ellipse(center.dx, center.dy, radiusX, radiusY, 0.0, - startAngle, startAngle + sweepAngle, sweepAngle.isNegative)); - - _setCurrentPoint(radiusX * math.cos(startAngle + sweepAngle) + center.dx, - radiusY * math.sin(startAngle + sweepAngle) + center.dy); - } - - /// Appends up to four conic curves weighted to describe an oval of `radius` - /// and rotated by `rotation`. - /// - /// The first curve begins from the last point in the path and the last ends - /// at `arcEnd`. The curves follow a path in a direction determined by - /// `clockwise` and `largeArc` in such a way that the sweep angle - /// is always less than 360 degrees. - /// - /// A simple line is appended if either either radii are zero or the last - /// point in the path is `arcEnd`. The radii are scaled to fit the last path - /// point if both are greater than zero but too small to describe an arc. - /// - /// See Conversion from endpoint to center parametrization described in - /// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter - /// as reference for implementation. - void arcToPoint( - Offset arcEnd, { - Radius radius = Radius.zero, - double rotation = 0.0, - bool largeArc = false, - bool clockwise = true, - }) { - assert(engine.offsetIsValid(arcEnd)); - assert(engine.radiusIsValid(radius)); - // _currentX, _currentY are the coordinates of start point on path, - // arcEnd is final point of arc. - // rx,ry are the radii of the eclipse (semi-major/semi-minor axis) - // largeArc is false if arc is spanning less than or equal to 180 degrees. - // clockwise is false if arc sweeps through decreasing angles or true - // if sweeping through increasing angles. - // rotation is the angle from the x-axis of the current coordinate - // system to the x-axis of the eclipse. - - double rx = radius.x.abs(); - double ry = radius.y.abs(); - - // If the current point and target point for the arc are identical, it - // should be treated as a zero length path. This ensures continuity in - // animations. - final bool isSamePoint = _currentX == arcEnd.dx && _currentY == arcEnd.dy; - - // If rx = 0 or ry = 0 then this arc is treated as a straight line segment - // (a "lineto") joining the endpoints. - // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters - if (isSamePoint || rx.toInt() == 0 || ry.toInt() == 0) { - _commands.add(engine.LineTo(arcEnd.dx, arcEnd.dy)); - _setCurrentPoint(arcEnd.dx, arcEnd.dy); - return; - } - - // As an intermediate point to finding center parametrization, place the - // origin on the midpoint between start/end points and rotate to align - // coordinate axis with axes of the ellipse. - final double midPointX = (_currentX - arcEnd.dx) / 2.0; - final double midPointY = (_currentY - arcEnd.dy) / 2.0; - - // Convert rotation or radians. - final double xAxisRotation = math.pi * rotation / 180.0; - - // Cache cos/sin value. - final double cosXAxisRotation = math.cos(xAxisRotation); - final double sinXAxisRotation = math.sin(xAxisRotation); - - // Calculate rotate midpoint as x/yPrime. - final double xPrime = - (cosXAxisRotation * midPointX) + (sinXAxisRotation * midPointY); - final double yPrime = - (-sinXAxisRotation * midPointX) + (cosXAxisRotation * midPointY); - - // Check if the radii are big enough to draw the arc, scale radii if not. - // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii - double rxSquare = rx * rx; - double rySquare = ry * ry; - final double xPrimeSquare = xPrime * xPrime; - final double yPrimeSquare = yPrime * yPrime; - - double radiiScale = (xPrimeSquare / rxSquare) + (yPrimeSquare / rySquare); - if (radiiScale > 1) { - radiiScale = math.sqrt(radiiScale); - rx *= radiiScale; - ry *= radiiScale; - rxSquare = rx * rx; - rySquare = ry * ry; - } - - // Compute transformed center. eq. 5.2 - final double distanceSquare = - (rxSquare * yPrimeSquare) + rySquare * xPrimeSquare; - final double cNumerator = (rxSquare * rySquare) - distanceSquare; - double scaleFactor = math.sqrt(math.max(cNumerator / distanceSquare, 0.0)); - if (largeArc == clockwise) { - scaleFactor = -scaleFactor; - } - // Ready to compute transformed center. - final double cxPrime = scaleFactor * ((rx * yPrime) / ry); - final double cyPrime = scaleFactor * (-(ry * xPrime) / rx); - - // Rotate to find actual center. - final double cx = cosXAxisRotation * cxPrime - - sinXAxisRotation * cyPrime + - ((_currentX + arcEnd.dx) / 2.0); - final double cy = sinXAxisRotation * cxPrime + - cosXAxisRotation * cyPrime + - ((_currentY + arcEnd.dy) / 2.0); - - // Calculate start angle and sweep. - // Start vector is from midpoint of start/end points to transformed center. - final double startVectorX = (xPrime - cxPrime) / rx; - final double startVectorY = (yPrime - cyPrime) / ry; - - final double startAngle = math.atan2(startVectorY, startVectorX); - final double endVectorX = (-xPrime - cxPrime) / rx; - final double endVectorY = (-yPrime - cyPrime) / ry; - double sweepAngle = math.atan2(endVectorY, endVectorX) - startAngle; - - if (clockwise && sweepAngle < 0) { - sweepAngle += math.pi * 2.0; - } else if (!clockwise && sweepAngle > 0) { - sweepAngle -= math.pi * 2.0; - } - - _commands.add(engine.Ellipse(cx, cy, rx, ry, xAxisRotation, startAngle, - startAngle + sweepAngle, sweepAngle.isNegative)); - - _setCurrentPoint(arcEnd.dx, arcEnd.dy); - } - - /// Appends up to four conic curves weighted to describe an oval of `radius` - /// and rotated by `rotation`. - /// - /// The last path point is described by (px, py). - /// - /// The first curve begins from the last point in the path and the last ends - /// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a - /// path in a direction determined by `clockwise` and `largeArc` - /// in such a way that the sweep angle is always less than 360 degrees. - /// - /// A simple line is appended if either either radii are zero, or, both - /// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to - /// fit the last path point if both are greater than zero but too small to - /// describe an arc. - void relativeArcToPoint( - Offset arcEndDelta, { - Radius radius = Radius.zero, - double rotation = 0.0, - bool largeArc = false, - bool clockwise = true, - }) { - assert(engine.offsetIsValid(arcEndDelta)); - assert(engine.radiusIsValid(radius)); - arcToPoint(Offset(_currentX + arcEndDelta.dx, _currentY + arcEndDelta.dy), - radius: radius, - rotation: rotation, - largeArc: largeArc, - clockwise: clockwise); - } - - /// Adds a new subpath that consists of four lines that outline the - /// given rectangle. - void addRect(Rect rect) { - assert(engine.rectIsValid(rect)); - _openNewSubpath(rect.left, rect.top); - _commands - .add(engine.RectCommand(rect.left, rect.top, rect.width, rect.height)); - } - - /// Adds a new subpath that consists of a curve that forms the - /// ellipse that fills the given rectangle. - /// - /// To add a circle, pass an appropriate rectangle as `oval`. - /// [Rect.fromCircle] can be used to easily describe the circle's center - /// [Offset] and radius. - void addOval(Rect oval) { - assert(engine.rectIsValid(oval)); - final Offset center = oval.center; - final double radiusX = oval.width / 2; - final double radiusY = oval.height / 2; - - /// At startAngle = 0, the path will begin at center + cos(0) * radius. - _openNewSubpath(center.dx + radiusX, center.dy); - _commands.add(engine.Ellipse( - center.dx, center.dy, radiusX, radiusY, 0.0, 0.0, 2 * math.pi, false)); - } - - /// Adds a new subpath with one arc segment that consists of the arc - /// that follows the edge of the oval bounded by the given - /// rectangle, from startAngle radians around the oval up to - /// startAngle + sweepAngle radians around the oval, with zero - /// radians being the point on the right hand side of the oval that - /// crosses the horizontal line that intersects the center of the - /// rectangle and with positive angles going clockwise around the - /// oval. - void addArc(Rect oval, double startAngle, double sweepAngle) { - assert(engine.rectIsValid(oval)); - final Offset center = oval.center; - final double radiusX = oval.width / 2; - final double radiusY = oval.height / 2; - _openNewSubpath(radiusX * math.cos(startAngle) + center.dx, - radiusY * math.sin(startAngle) + center.dy); - _commands.add(engine.Ellipse(center.dx, center.dy, radiusX, radiusY, 0.0, - startAngle, startAngle + sweepAngle, sweepAngle.isNegative)); - - _setCurrentPoint(radiusX * math.cos(startAngle + sweepAngle) + center.dx, - radiusY * math.sin(startAngle + sweepAngle) + center.dy); - } - - /// Adds a new subpath with a sequence of line segments that connect the given - /// points. - /// - /// If `close` is true, a final line segment will be added that connects the - /// last point to the first point. - /// - /// The `points` argument is interpreted as offsets from the origin. - void addPolygon(List points, bool close) { - assert(points != null); - if (points.isEmpty) { - return; - } - - moveTo(points.first.dx, points.first.dy); - for (int i = 1; i < points.length; i++) { - final Offset point = points[i]; - lineTo(point.dx, point.dy); - } - if (close) { - this.close(); - } else { - _setCurrentPoint(points.last.dx, points.last.dy); - } - } - - /// Adds a new subpath that consists of the straight lines and - /// curves needed to form the rounded rectangle described by the - /// argument. - void addRRect(RRect rrect) { - assert(engine.rrectIsValid(rrect)); - - // Set the current point to the top left corner of the rectangle (the - // point on the top of the rectangle farthest to the left that isn't in - // the rounded corner). - // TODO(het): Is this the current point in Flutter? - _openNewSubpath(rrect.tallMiddleRect.left, rrect.top); - _commands.add(engine.RRectCommand(rrect)); - } - - /// Adds a new subpath that consists of the given `path` offset by the given - /// `offset`. - /// - /// If `matrix4` is specified, the path will be transformed by this matrix - /// after the matrix is translated by the given offset. The matrix is a 4x4 - /// matrix stored in column major order. - void addPath(Path path, Offset offset, {Float64List matrix4}) { - assert(path != null); // path is checked on the engine side - assert(engine.offsetIsValid(offset)); - if (matrix4 != null) { - assert(engine.matrix4IsValid(matrix4)); - _addPathWithMatrix(path, offset.dx, offset.dy, matrix4); - } else { - _addPath(path, offset.dx, offset.dy); - } - } - - void _addPath(Path path, double dx, double dy) { - if (dx == 0.0 && dy == 0.0) { - subpaths.addAll(path.subpaths); - } else { - subpaths.addAll(path - .transform(engine.Matrix4.translationValues(dx, dy, 0.0).storage) - .subpaths); - } - } - - void _addPathWithMatrix(Path path, double dx, double dy, Float64List matrix) { - final engine.Matrix4 transform = engine.Matrix4.fromFloat64List(matrix); - transform.translate(dx, dy); - subpaths.addAll(path.transform(transform.storage).subpaths); - } - - /// Adds the given path to this path by extending the current segment of this - /// path with the the first segment of the given path. - /// - /// If `matrix4` is specified, the path will be transformed by this matrix - /// after the matrix is translated by the given `offset`. The matrix is a 4x4 - /// matrix stored in column major order. - void extendWithPath(Path path, Offset offset, {Float64List matrix4}) { - assert(path != null); // path is checked on the engine side - assert(engine.offsetIsValid(offset)); - if (matrix4 != null) { - assert(engine.matrix4IsValid(matrix4)); - _extendWithPathAndMatrix(path, offset.dx, offset.dy, matrix4); - } else { - _extendWithPath(path, offset.dx, offset.dy); - } - } - - void _extendWithPath(Path path, double dx, double dy) { - if (dx == 0.0 && dy == 0.0) { - assert(path.subpaths.length == 1); - _ensurePathStarted(); - _commands.addAll(path.subpaths.single.commands); - _setCurrentPoint( - path.subpaths.single.currentX, path.subpaths.single.currentY); - } else { - throw UnimplementedError('Cannot extend path with non-zero offset'); - } - } - - void _extendWithPathAndMatrix( - Path path, double dx, double dy, Float64List matrix) { - throw UnimplementedError('Cannot extend path with transform matrix'); - } - - /// Closes the last subpath, as if a straight line had been drawn - /// from the current point to the first point of the subpath. - void close() { - _ensurePathStarted(); - _commands.add(const engine.CloseCommand()); - _setCurrentPoint(_currentSubpath.startX, _currentSubpath.startY); - } - - /// Clears the [Path] object of all subpaths, returning it to the - /// same state it had when it was created. The _current point_ is - /// reset to the origin. - void reset() { - subpaths.clear(); - } - - /// Tests to see if the given point is within the path. (That is, whether the - /// point would be in the visible portion of the path if the path was used - /// with [Canvas.clipPath].) - /// - /// The `point` argument is interpreted as an offset from the origin. - /// - /// Returns true if the point is in the path, and false otherwise. - /// - /// Note: Not very efficient, it creates a canvas, plays path and calls - /// Context2D isPointInPath. If performance becomes issue, retaining - /// RawRecordingCanvas can remove create/remove rootElement cost. - bool contains(Offset point) { - assert(engine.offsetIsValid(point)); - final int subPathCount = subpaths.length; - if (subPathCount == 0) { - return false; - } - final double pointX = point.dx; - final double pointY = point.dy; - if (subPathCount == 1) { - // Optimize for rect/roundrect checks. - final engine.Subpath subPath = subpaths[0]; - if (subPath.commands.length == 1) { - final engine.PathCommand cmd = subPath.commands[0]; - if (cmd is engine.RectCommand) { - if (pointY < cmd.y || pointY > (cmd.y + cmd.height)) { - return false; - } - if (pointX < cmd.x || pointX > (cmd.x + cmd.width)) { - return false; - } - return true; - } else if (cmd is engine.RRectCommand) { - final RRect rRect = cmd.rrect; - if (pointY < rRect.top || pointY > rRect.bottom) { - return false; - } - if (pointX < rRect.left || pointX > rRect.right) { - return false; - } - if (pointX < (rRect.left + rRect.tlRadiusX) && - pointY < (rRect.top + rRect.tlRadiusY)) { - // Top left corner - return _ellipseContains( - pointX, - pointY, - rRect.left + rRect.tlRadiusX, - rRect.top + rRect.tlRadiusY, - rRect.tlRadiusX, - rRect.tlRadiusY); - } else if (pointX >= (rRect.right - rRect.trRadiusX) && - pointY < (rRect.top + rRect.trRadiusY)) { - // Top right corner - return _ellipseContains( - pointX, - pointY, - rRect.right - rRect.trRadiusX, - rRect.top + rRect.trRadiusY, - rRect.trRadiusX, - rRect.trRadiusY); - } else if (pointX >= (rRect.right - rRect.brRadiusX) && - pointY >= (rRect.bottom - rRect.brRadiusY)) { - // Bottom right corner - return _ellipseContains( - pointX, - pointY, - rRect.right - rRect.brRadiusX, - rRect.bottom - rRect.brRadiusY, - rRect.trRadiusX, - rRect.trRadiusY); - } else if (pointX < (rRect.left + rRect.blRadiusX) && - pointY >= (rRect.bottom - rRect.blRadiusY)) { - // Bottom left corner - return _ellipseContains( - pointX, - pointY, - rRect.left + rRect.blRadiusX, - rRect.bottom - rRect.blRadiusY, - rRect.trRadiusX, - rRect.trRadiusY); - } - return true; - } - } - } - final Size size = window.physicalSize / window.devicePixelRatio; - _rawRecorder ??= RawRecordingCanvas(size); - // Account for the shift due to padding. - _rawRecorder.translate(-engine.BitmapCanvas.kPaddingPixels.toDouble(), - -engine.BitmapCanvas.kPaddingPixels.toDouble()); - _rawRecorder.drawPath( - this, (Paint()..color = const Color(0xFF000000)).webOnlyPaintData); - final bool result = _rawRecorder.ctx.isPointInPath(pointX, pointY); - _rawRecorder.dispose(); - return result; - } - - /// Returns a copy of the path with all the segments of every - /// subpath translated by the given offset. - Path shift(Offset offset) { - assert(engine.offsetIsValid(offset)); - final List shiftedSubPaths = []; - for (final engine.Subpath subPath in subpaths) { - shiftedSubPaths.add(subPath.shift(offset)); - } - return Path._clone(shiftedSubPaths, fillType); - } - - /// Returns a copy of the path with all the segments of every - /// sub path transformed by the given matrix. - Path transform(Float64List matrix4) { - assert(engine.matrix4IsValid(matrix4)); - final Path transformedPath = Path(); - for (final engine.Subpath subPath in subpaths) { - for (final engine.PathCommand cmd in subPath.commands) { - cmd.transform(matrix4, transformedPath); - } - } - return transformedPath; - } - - /// Computes the bounding rectangle for this path. - /// - /// A path containing only axis-aligned points on the same straight line will - /// have no area, and therefore `Rect.isEmpty` will return true for such a - /// path. Consider checking `rect.width + rect.height > 0.0` instead, or - /// using the [computeMetrics] API to check the path length. - /// - /// For many more elaborate paths, the bounds may be inaccurate. For example, - /// when a path contains a circle, the points used to compute the bounds are - /// the circle's implied control points, which form a square around the - /// circle; if the circle has a transformation applied using [transform] then - /// that square is rotated, and the (axis-aligned, non-rotated) bounding box - /// therefore ends up grossly overestimating the actual area covered by the - /// circle. - // see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds - Rect getBounds() { - // Sufficiently small number for curve eq. - const double epsilon = 0.000000001; - bool ltrbInitialized = false; - double left = 0.0, top = 0.0, right = 0.0, bottom = 0.0; - double curX = 0.0; - double curY = 0.0; - double minX = 0.0, maxX = 0.0, minY = 0.0, maxY = 0.0; - for (engine.Subpath subpath in subpaths) { - for (engine.PathCommand op in subpath.commands) { - bool skipBounds = false; - switch (op.type) { - case engine.PathCommandTypes.moveTo: - final engine.MoveTo cmd = op; - curX = minX = maxX = cmd.x; - curY = minY = maxY = cmd.y; - break; - case engine.PathCommandTypes.lineTo: - final engine.LineTo cmd = op; - curX = minX = maxX = cmd.x; - curY = minY = maxY = cmd.y; - break; - case engine.PathCommandTypes.ellipse: - final engine.Ellipse cmd = op; - // Rotate 4 corners of bounding box. - final double rx = cmd.radiusX; - final double ry = cmd.radiusY; - final double cosVal = math.cos(cmd.rotation); - final double sinVal = math.sin(cmd.rotation); - final double rxCos = rx * cosVal; - final double ryCos = ry * cosVal; - final double rxSin = rx * sinVal; - final double rySin = ry * sinVal; - - final double leftDeltaX = rxCos - rySin; - final double rightDeltaX = -rxCos - rySin; - final double topDeltaY = ryCos + rxSin; - final double bottomDeltaY = ryCos - rxSin; - - final double centerX = cmd.x; - final double centerY = cmd.y; - - double rotatedX = centerX + leftDeltaX; - double rotatedY = centerY + topDeltaY; - minX = maxX = rotatedX; - minY = maxY = rotatedY; - - rotatedX = centerX + rightDeltaX; - rotatedY = centerY + bottomDeltaY; - minX = math.min(minX, rotatedX); - maxX = math.max(maxX, rotatedX); - minY = math.min(minY, rotatedY); - maxY = math.max(maxY, rotatedY); - - rotatedX = centerX - leftDeltaX; - rotatedY = centerY - topDeltaY; - minX = math.min(minX, rotatedX); - maxX = math.max(maxX, rotatedX); - minY = math.min(minY, rotatedY); - maxY = math.max(maxY, rotatedY); - - rotatedX = centerX - rightDeltaX; - rotatedY = centerY - bottomDeltaY; - minX = math.min(minX, rotatedX); - maxX = math.max(maxX, rotatedX); - minY = math.min(minY, rotatedY); - maxY = math.max(maxY, rotatedY); - - curX = centerX + cmd.radiusX; - curY = centerY; - break; - case engine.PathCommandTypes.quadraticCurveTo: - final engine.QuadraticCurveTo cmd = op; - final double x1 = curX; - final double y1 = curY; - final double cpX = cmd.x1; - final double cpY = cmd.y1; - final double x2 = cmd.x2; - final double y2 = cmd.y2; - - minX = math.min(x1, x2); - minY = math.min(y1, y2); - maxX = math.max(x1, x2); - maxY = math.max(y1, y2); - - // Curve equation : (1-t)(1-t)P1 + 2t(1-t)CP + t*t*P2. - // At extrema's derivative = 0. - // Solve for - // -2x1+2tx1 + 2cpX + 4tcpX + 2tx2 = 0 - // -2x1 + 2cpX +2t(x1 + 2cpX + x2) = 0 - // t = (x1 - cpX) / (x1 - 2cpX + x2) - - double denom = x1 - (2 * cpX) + x2; - if (denom.abs() > epsilon) { - final num t1 = (x1 - cpX) / denom; - if ((t1 >= 0) && (t1 <= 1.0)) { - // Solve (x,y) for curve at t = tx to find extrema - final num tprime = 1.0 - t1; - final num extremaX = (tprime * tprime * x1) + - (2 * t1 * tprime * cpX) + - (t1 * t1 * x2); - final num extremaY = (tprime * tprime * y1) + - (2 * t1 * tprime * cpY) + - (t1 * t1 * y2); - // Expand bounds. - minX = math.min(minX, extremaX); - maxX = math.max(maxX, extremaX); - minY = math.min(minY, extremaY); - maxY = math.max(maxY, extremaY); - } - } - // Now calculate dy/dt = 0 - denom = y1 - (2 * cpY) + y2; - if (denom.abs() > epsilon) { - final num t2 = (y1 - cpY) / denom; - if ((t2 >= 0) && (t2 <= 1.0)) { - final num tprime2 = 1.0 - t2; - final num extrema2X = (tprime2 * tprime2 * x1) + - (2 * t2 * tprime2 * cpX) + - (t2 * t2 * x2); - final num extrema2Y = (tprime2 * tprime2 * y1) + - (2 * t2 * tprime2 * cpY) + - (t2 * t2 * y2); - // Expand bounds. - minX = math.min(minX, extrema2X); - maxX = math.max(maxX, extrema2X); - minY = math.min(minY, extrema2Y); - maxY = math.max(maxY, extrema2Y); - } - } - curX = x2; - curY = y2; - break; - case engine.PathCommandTypes.bezierCurveTo: - final engine.BezierCurveTo cmd = op; - final double startX = curX; - final double startY = curY; - final double cpX1 = cmd.x1; - final double cpY1 = cmd.y1; - final double cpX2 = cmd.x2; - final double cpY2 = cmd.y2; - final double endX = cmd.x3; - final double endY = cmd.y3; - // Bounding box is defined by all points on the curve where - // monotonicity changes. - minX = math.min(startX, endX); - minY = math.min(startY, endY); - maxX = math.max(startX, endX); - maxY = math.max(startY, endY); - - double extremaX; - double extremaY; - double a, b, c; - - // Check for simple case of strong ordering before calculating - // extrema - if (!(((startX < cpX1) && (cpX1 < cpX2) && (cpX2 < endX)) || - ((startX > cpX1) && (cpX1 > cpX2) && (cpX2 > endX)))) { - // The extrema point is dx/dt B(t) = 0 - // The derivative of B(t) for cubic bezier is a quadratic equation - // with multiple roots - // B'(t) = a*t*t + b*t + c*t - a = -startX + (3 * (cpX1 - cpX2)) + endX; - b = 2 * (startX - (2 * cpX1) + cpX2); - c = -startX + cpX1; - - // Now find roots for quadratic equation with known coefficients - // a,b,c - // The roots are (-b+-sqrt(b*b-4*a*c)) / 2a - num s = (b * b) - (4 * a * c); - // If s is negative, we have no real roots - if ((s >= 0.0) && (a.abs() > epsilon)) { - if (s == 0.0) { - // we have only 1 root - final num t = -b / (2 * a); - final num tprime = 1.0 - t; - if ((t >= 0.0) && (t <= 1.0)) { - extremaX = ((tprime * tprime * tprime) * startX) + - ((3 * tprime * tprime * t) * cpX1) + - ((3 * tprime * t * t) * cpX2) + - (t * t * t * endX); - minX = math.min(extremaX, minX); - maxX = math.max(extremaX, maxX); - } - } else { - // we have 2 roots - s = math.sqrt(s); - num t = (-b - s) / (2 * a); - num tprime = 1.0 - t; - if ((t >= 0.0) && (t <= 1.0)) { - extremaX = ((tprime * tprime * tprime) * startX) + - ((3 * tprime * tprime * t) * cpX1) + - ((3 * tprime * t * t) * cpX2) + - (t * t * t * endX); - minX = math.min(extremaX, minX); - maxX = math.max(extremaX, maxX); - } - // check 2nd root - t = (-b + s) / (2 * a); - tprime = 1.0 - t; - if ((t >= 0.0) && (t <= 1.0)) { - extremaX = ((tprime * tprime * tprime) * startX) + - ((3 * tprime * tprime * t) * cpX1) + - ((3 * tprime * t * t) * cpX2) + - (t * t * t * endX); - - minX = math.min(extremaX, minX); - maxX = math.max(extremaX, maxX); - } - } - } - } - - // Now calc extremes for dy/dt = 0 just like above - if (!(((startY < cpY1) && (cpY1 < cpY2) && (cpY2 < endY)) || - ((startY > cpY1) && (cpY1 > cpY2) && (cpY2 > endY)))) { - // The extrema point is dy/dt B(t) = 0 - // The derivative of B(t) for cubic bezier is a quadratic equation - // with multiple roots - // B'(t) = a*t*t + b*t + c*t - a = -startY + (3 * (cpY1 - cpY2)) + endY; - b = 2 * (startY - (2 * cpY1) + cpY2); - c = -startY + cpY1; - - // Now find roots for quadratic equation with known coefficients - // a,b,c - // The roots are (-b+-sqrt(b*b-4*a*c)) / 2a - num s = (b * b) - (4 * a * c); - // If s is negative, we have no real roots - if ((s >= 0.0) && (a.abs() > epsilon)) { - if (s == 0.0) { - // we have only 1 root - final num t = -b / (2 * a); - final num tprime = 1.0 - t; - if ((t >= 0.0) && (t <= 1.0)) { - extremaY = ((tprime * tprime * tprime) * startY) + - ((3 * tprime * tprime * t) * cpY1) + - ((3 * tprime * t * t) * cpY2) + - (t * t * t * endY); - minY = math.min(extremaY, minY); - maxY = math.max(extremaY, maxY); - } - } else { - // we have 2 roots - s = math.sqrt(s); - final num t = (-b - s) / (2 * a); - final num tprime = 1.0 - t; - if ((t >= 0.0) && (t <= 1.0)) { - extremaY = ((tprime * tprime * tprime) * startY) + - ((3 * tprime * tprime * t) * cpY1) + - ((3 * tprime * t * t) * cpY2) + - (t * t * t * endY); - minY = math.min(extremaY, minY); - maxY = math.max(extremaY, maxY); - } - // check 2nd root - final num t2 = (-b + s) / (2 * a); - final num tprime2 = 1.0 - t2; - if ((t2 >= 0.0) && (t2 <= 1.0)) { - extremaY = ((tprime2 * tprime2 * tprime2) * startY) + - ((3 * tprime2 * tprime2 * t2) * cpY1) + - ((3 * tprime2 * t2 * t2) * cpY2) + - (t2 * t2 * t2 * endY); - minY = math.min(extremaY, minY); - maxY = math.max(extremaY, maxY); - } - } - } - } - break; - case engine.PathCommandTypes.rect: - final engine.RectCommand cmd = op; - left = cmd.x; - double width = cmd.width; - if (cmd.width < 0) { - left -= width; - width = -width; - } - double top = cmd.y; - double height = cmd.height; - if (cmd.height < 0) { - top -= height; - height = -height; - } - curX = minX = left; - maxX = left + width; - curY = minY = top; - maxY = top + height; - break; - case engine.PathCommandTypes.rRect: - final engine.RRectCommand cmd = op; - final RRect rRect = cmd.rrect; - curX = minX = rRect.left; - maxX = rRect.left + rRect.width; - curY = minY = rRect.top; - maxY = rRect.top + rRect.height; - break; - case engine.PathCommandTypes.close: - default: - skipBounds = false; - break; - } - if (!skipBounds) { - if (!ltrbInitialized) { - left = minX; - right = maxX; - top = minY; - bottom = maxY; - ltrbInitialized = true; - } else { - left = math.min(left, minX); - right = math.max(right, maxX); - top = math.min(top, minY); - bottom = math.max(bottom, maxY); - } - } - } - } - return ltrbInitialized - ? Rect.fromLTRB(left, top, right, bottom) - : Rect.zero; - } - - /// Combines the two paths according to the manner specified by the given - /// `operation`. - /// - /// The resulting path will be constructed from non-overlapping contours. The - /// curve order is reduced where possible so that cubics may be turned into - /// quadratics, and quadratics maybe turned into lines. - static Path combine(PathOperation operation, Path path1, Path path2) { - assert(path1 != null); - assert(path2 != null); - throw UnimplementedError(); - } - - /// Creates a [PathMetrics] object for this path. - /// - /// If `forceClosed` is set to true, the contours of the path will be measured - /// as if they had been closed, even if they were not explicitly closed. - PathMetrics computeMetrics({bool forceClosed = false}) { - return PathMetrics._(this, forceClosed); - } - - /// Detects if path is rounded rectangle and returns rounded rectangle or - /// null. - /// - /// Used for web optimization of physical shape represented as - /// a persistent div. - RRect get webOnlyPathAsRoundedRect { - if (subpaths.length != 1) { - return null; - } - final engine.Subpath subPath = subpaths[0]; - if (subPath.commands.length != 1) { - return null; - } - final engine.PathCommand command = subPath.commands[0]; - return (command is engine.RRectCommand) ? command.rrect : null; - } - - /// Detects if path is simple rectangle and returns rectangle or null. - /// - /// Used for web optimization of physical shape represented as - /// a persistent div. - Rect get webOnlyPathAsRect { - if (subpaths.length != 1) { - return null; - } - final engine.Subpath subPath = subpaths[0]; - if (subPath.commands.length != 1) { - return null; - } - final engine.PathCommand command = subPath.commands[0]; - return (command is engine.RectCommand) - ? Rect.fromLTWH(command.x, command.y, command.width, command.height) - : null; - } - - /// Detects if path is simple oval and returns [engine.Ellipse] or null. - /// - /// Used for web optimization of physical shape represented as - /// a persistent div. - engine.Ellipse get webOnlyPathAsCircle { - if (subpaths.length != 1) { - return null; - } - final engine.Subpath subPath = subpaths[0]; - if (subPath.commands.length != 1) { - return null; - } - final engine.PathCommand command = subPath.commands[0]; - if (command is engine.Ellipse) { - final engine.Ellipse ellipse = command; - if ((ellipse.endAngle - ellipse.startAngle) % (2 * math.pi) == 0.0) { - return ellipse; - } - } - return null; - } - - /// Serializes this path to a value that's sent to a CSS custom painter for - /// painting. - List webOnlySerializeToCssPaint() { - final List serializedSubpaths = []; - for (int i = 0; i < subpaths.length; i++) { - serializedSubpaths.add(subpaths[i].serializeToCssPaint()); - } - return serializedSubpaths; - } - - @override - String toString() { - if (engine.assertionsEnabled) { - return 'Path(${subpaths.join(', ')})'; - } else { - return super.toString(); - } - } -} - -/// An iterable collection of [PathMetric] objects describing a [Path]. -/// -/// A [PathMetrics] object is created by using the [Path.computeMetrics] method, -/// and represents the path as it stood at the time of the call. Subsequent -/// modifications of the path do not affect the [PathMetrics] object. -/// -/// Each path metric corresponds to a segment, or contour, of a path. -/// -/// For example, a path consisting of a [Path.lineTo], a [Path.moveTo], and -/// another [Path.lineTo] will contain two contours and thus be represented by -/// two [PathMetric] objects. -/// -/// When iterating across a [PathMetrics]' contours, the [PathMetric] objects -/// are only valid until the next one is obtained. -class PathMetrics extends collection.IterableBase { - PathMetrics._(Path path, bool forceClosed) - : _iterator = PathMetricIterator._(PathMetric._(path, forceClosed)); - - final Iterator _iterator; - - @override - Iterator get iterator => _iterator; -} - -/// Tracks iteration from one segment of a path to the next for measurement. -class PathMetricIterator implements Iterator { - PathMetricIterator._(this._pathMetric); - - PathMetric _pathMetric; - bool _firstTime = true; - - @override - PathMetric get current => _firstTime ? null : _pathMetric; - - @override - bool moveNext() { - // PathMetric isn't a normal iterable - it's already initialized to its - // first Path. Should only call _moveNext when done with the first one. - if (_firstTime == true) { - _firstTime = false; - return true; - } else if (_pathMetric?._moveNext() == true) { - return true; - } - _pathMetric = null; - return false; - } -} - -/// Utilities for measuring a [Path] and extracting subpaths. -/// -/// Iterate over the object returned by [Path.computeMetrics] to obtain -/// [PathMetric] objects. -/// -/// Once created, metrics will only be valid while the iterator is at the given -/// contour. When the next contour's [PathMetric] is obtained, this object -/// becomes invalid. -class PathMetric { - final Path _path; - final bool _forceClosed; - - /// Create a new empty [Path] object. - PathMetric._(this._path, this._forceClosed); - - /// Return the total length of the current contour. - double get length => throw UnimplementedError(); - - /// Computes the position of hte current contour at the given offset, and the - /// angle of the path at that point. - /// - /// For example, calling this method with a distance of 1.41 for a line from - /// 0.0,0.0 to 2.0,2.0 would give a point 1.0,1.0 and the angle 45 degrees - /// (but in radians). - /// - /// Returns null if the contour has zero [length]. - /// - /// The distance is clamped to the [length] of the current contour. - Tangent getTangentForOffset(double distance) { - final Float32List posTan = _getPosTan(distance); - // first entry == 0 indicates that Skia returned false - if (posTan[0] == 0.0) { - return null; - } else { - return Tangent( - Offset(posTan[1], posTan[2]), Offset(posTan[3], posTan[4])); - } - } - - Float32List _getPosTan(double distance) => throw UnimplementedError(); - - /// Given a start and stop distance, return the intervening segment(s). - /// - /// `start` and `end` are pinned to legal values (0..[length]) - /// Returns null if the segment is 0 length or `start` > `stop`. - /// Begin the segment with a moveTo if `startWithMoveTo` is true. - Path extractPath(double start, double end, {bool startWithMoveTo = true}) => - throw UnimplementedError(); - - /// Whether the contour is closed. - /// - /// Returns true if the contour ends with a call to [Path.close] (which may - /// have been implied when using [Path.addRect]) or if `forceClosed` was - /// specified as true in the call to [Path.computeMetrics]. Returns false - /// otherwise. - bool get isClosed => throw UnimplementedError(); - - // Move to the next contour in the path. - // - // A path can have a next contour if [Path.moveTo] was called after drawing - // began. Return true if one exists, or false. - // - // This is not exactly congruent with a regular [Iterator.moveNext]. - // Typically, [Iterator.moveNext] should be called before accessing the - // [Iterator.current]. In this case, the [PathMetric] is valid before - // calling `_moveNext` - `_moveNext` should be called after the first - // iteration is done instead of before. - bool _moveNext() => throw UnimplementedError(); - - @override - String toString() => 'PathMetric'; -} - -/// The geometric description of a tangent: the angle at a point. -/// -/// See also: -/// * [PathMetric.getTangentForOffset], which returns the tangent of an offset -/// along a path. -class Tangent { - /// Creates a [Tangent] with the given values. - /// - /// The arguments must not be null. - const Tangent(this.position, this.vector) - : assert(position != null), - assert(vector != null); - - /// Creates a [Tangent] based on the angle rather than the vector. - /// - /// The [vector] is computed to be the unit vector at the given angle, - /// interpreted as clockwise radians from the x axis. - factory Tangent.fromAngle(Offset position, double angle) { - return Tangent(position, Offset(math.cos(angle), math.sin(angle))); - } - - /// Position of the tangent. - /// - /// When used with [PathMetric.getTangentForOffset], this represents the - /// precise position that the given offset along the path corresponds to. - final Offset position; - - /// The vector of the curve at [position]. - /// - /// When used with [PathMetric.getTangentForOffset], this is the vector of the - /// curve that is at the given offset along the path (i.e. the direction of - /// the curve at [position]). - final Offset vector; - - /// The direction of the curve at [position]. - /// - /// When used with [PathMetric.getTangentForOffset], this is the angle of the - /// curve that is the given offset along the path (i.e. the direction of the - /// curve at [position]). - /// - /// This value is in radians, with 0.0 meaning pointing along the x axis in - /// the positive x-axis direction, positive numbers pointing downward toward - /// the negative y-axis, i.e. in a clockwise direction, and negative numbers - /// pointing upward toward the positive y-axis, i.e. in a counter-clockwise - /// direction. - // flip the sign to be consistent with [Path.arcTo]'s `sweepAngle` - double get angle => -math.atan2(vector.dy, vector.dx); -} - class RawRecordingCanvas extends engine.BitmapCanvas implements PictureRecorder { RawRecordingCanvas(Size size) : super(Offset.zero & size); diff --git a/lib/web_ui/lib/src/ui/path.dart b/lib/web_ui/lib/src/ui/path.dart new file mode 100644 index 0000000000000..f22c9a679db05 --- /dev/null +++ b/lib/web_ui/lib/src/ui/path.dart @@ -0,0 +1,1107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of ui; + +/// A complex, one-dimensional subset of a plane. +/// +/// A path consists of a number of subpaths, and a _current point_. +/// +/// Subpaths consist of segments of various types, such as lines, +/// arcs, or beziers. Subpaths can be open or closed, and can +/// self-intersect. +/// +/// Closed subpaths enclose a (possibly discontiguous) region of the +/// plane based on the current [fillType]. +/// +/// The _current point_ is initially at the origin. After each +/// operation adding a segment to a subpath, the current point is +/// updated to the end of that segment. +/// +/// Paths can be drawn on canvases using [Canvas.drawPath], and can +/// used to create clip regions using [Canvas.clipPath]. +class Path { + final List subpaths; + PathFillType _fillType = PathFillType.nonZero; + + engine.Subpath get _currentSubpath => subpaths.isEmpty ? null : subpaths.last; + + List get _commands => _currentSubpath?.commands; + + /// The current x-coordinate for this path. + double get _currentX => _currentSubpath?.currentX ?? 0.0; + + /// The current y-coordinate for this path. + double get _currentY => _currentSubpath?.currentY ?? 0.0; + + /// Recorder used for hit testing paths. + static RawRecordingCanvas _rawRecorder; + + /// Create a new empty [Path] object. + factory Path() { + if (engine.experimentalUseSkia) { + return engine.SkPath(); + } else { + return Path._(); + } + } + + Path._() : subpaths = []; + + /// Creates a copy of another [Path]. + /// + /// This copy is fast and does not require additional memory unless either + /// the `source` path or the path returned by this constructor are modified. + Path.from(Path source) + : subpaths = List.from(source.subpaths); + + Path._clone(this.subpaths, this._fillType); + + /// Determines how the interior of this path is calculated. + /// + /// Defaults to the non-zero winding rule, [PathFillType.nonZero]. + PathFillType get fillType => _fillType; + set fillType(PathFillType value) { + _fillType = value; + } + + /// Opens a new subpath with starting point (x, y). + void _openNewSubpath(double x, double y) { + subpaths.add(engine.Subpath(x, y)); + _setCurrentPoint(x, y); + } + + /// Sets the current point to (x, y). + void _setCurrentPoint(double x, double y) { + _currentSubpath.currentX = x; + _currentSubpath.currentY = y; + } + + /// Starts a new subpath at the given coordinate. + void moveTo(double x, double y) { + _openNewSubpath(x, y); + _commands.add(engine.MoveTo(x, y)); + } + + /// Starts a new subpath at the given offset from the current point. + void relativeMoveTo(double dx, double dy) { + final double newX = _currentX + dx; + final double newY = _currentY + dy; + _openNewSubpath(newX, newY); + _commands.add(engine.MoveTo(newX, newY)); + } + + /// Adds a straight line segment from the current point to the given + /// point. + void lineTo(double x, double y) { + if (subpaths.isEmpty) { + moveTo(0.0, 0.0); + } + _commands.add(engine.LineTo(x, y)); + _setCurrentPoint(x, y); + } + + /// Adds a straight line segment from the current point to the point + /// at the given offset from the current point. + void relativeLineTo(double dx, double dy) { + final double newX = _currentX + dx; + final double newY = _currentY + dy; + if (subpaths.isEmpty) { + moveTo(0.0, 0.0); + } + _commands.add(engine.LineTo(newX, newY)); + _setCurrentPoint(newX, newY); + } + + void _ensurePathStarted() { + if (subpaths.isEmpty) { + subpaths.add(engine.Subpath(0.0, 0.0)); + } + } + + /// Adds a quadratic bezier segment that curves from the current + /// point to the given point (x2,y2), using the control point + /// (x1,y1). + void quadraticBezierTo(double x1, double y1, double x2, double y2) { + _ensurePathStarted(); + _commands.add(engine.QuadraticCurveTo(x1, y1, x2, y2)); + _setCurrentPoint(x2, y2); + } + + /// Adds a quadratic bezier segment that curves from the current + /// point to the point at the offset (x2,y2) from the current point, + /// using the control point at the offset (x1,y1) from the current + /// point. + void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { + _ensurePathStarted(); + _commands.add(engine.QuadraticCurveTo( + x1 + _currentX, y1 + _currentY, x2 + _currentX, y2 + _currentY)); + _setCurrentPoint(x2 + _currentX, y2 + _currentY); + } + + /// Adds a cubic bezier segment that curves from the current point + /// to the given point (x3,y3), using the control points (x1,y1) and + /// (x2,y2). + void cubicTo( + double x1, double y1, double x2, double y2, double x3, double y3) { + _ensurePathStarted(); + _commands.add(engine.BezierCurveTo(x1, y1, x2, y2, x3, y3)); + _setCurrentPoint(x3, y3); + } + + /// Adds a cubic bezier segment that curves from the current point + /// to the point at the offset (x3,y3) from the current point, using + /// the control points at the offsets (x1,y1) and (x2,y2) from the + /// current point. + void relativeCubicTo( + double x1, double y1, double x2, double y2, double x3, double y3) { + _ensurePathStarted(); + _commands.add(engine.BezierCurveTo(x1 + _currentX, y1 + _currentY, + x2 + _currentX, y2 + _currentY, x3 + _currentX, y3 + _currentY)); + _setCurrentPoint(x3 + _currentX, y3 + _currentY); + } + + /// Adds a bezier segment that curves from the current point to the + /// given point (x2,y2), using the control points (x1,y1) and the + /// weight w. If the weight is greater than 1, then the curve is a + /// hyperbola; if the weight equals 1, it's a parabola; and if it is + /// less than 1, it is an ellipse. + void conicTo(double x1, double y1, double x2, double y2, double w) { + final List quads = + engine.Conic(_currentX, _currentY, x1, y1, x2, y2, w).toQuads(); + final int len = quads.length; + for (int i = 1; i < len; i += 2) { + quadraticBezierTo( + quads[i].dx, quads[i].dy, quads[i + 1].dx, quads[i + 1].dy); + } + } + + /// Adds a bezier segment that curves from the current point to the + /// point at the offset (x2,y2) from the current point, using the + /// control point at the offset (x1,y1) from the current point and + /// the weight w. If the weight is greater than 1, then the curve is + /// a hyperbola; if the weight equals 1, it's a parabola; and if it + /// is less than 1, it is an ellipse. + void relativeConicTo(double x1, double y1, double x2, double y2, double w) { + conicTo(_currentX + x1, _currentY + y1, _currentX + x2, _currentY + y2, w); + } + + /// If the `forceMoveTo` argument is false, adds a straight line + /// segment and an arc segment. + /// + /// If the `forceMoveTo` argument is true, starts a new subpath + /// consisting of an arc segment. + /// + /// In either case, the arc segment consists of the arc that follows + /// the edge of the oval bounded by the given rectangle, from + /// startAngle radians around the oval up to startAngle + sweepAngle + /// radians around the oval, with zero radians being the point on + /// the right hand side of the oval that crosses the horizontal line + /// that intersects the center of the rectangle and with positive + /// angles going clockwise around the oval. + /// + /// The line segment added if `forceMoveTo` is false starts at the + /// current point and ends at the start of the arc. + void arcTo( + Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { + assert(engine.rectIsValid(rect)); + final Offset center = rect.center; + final double radiusX = rect.width / 2; + final double radiusY = rect.height / 2; + final double startX = radiusX * math.cos(startAngle) + center.dx; + final double startY = radiusY * math.sin(startAngle) + center.dy; + if (forceMoveTo) { + _openNewSubpath(startX, startY); + } else { + lineTo(startX, startY); + } + _commands.add(engine.Ellipse(center.dx, center.dy, radiusX, radiusY, 0.0, + startAngle, startAngle + sweepAngle, sweepAngle.isNegative)); + + _setCurrentPoint(radiusX * math.cos(startAngle + sweepAngle) + center.dx, + radiusY * math.sin(startAngle + sweepAngle) + center.dy); + } + + /// Appends up to four conic curves weighted to describe an oval of `radius` + /// and rotated by `rotation`. + /// + /// The first curve begins from the last point in the path and the last ends + /// at `arcEnd`. The curves follow a path in a direction determined by + /// `clockwise` and `largeArc` in such a way that the sweep angle + /// is always less than 360 degrees. + /// + /// A simple line is appended if either either radii are zero or the last + /// point in the path is `arcEnd`. The radii are scaled to fit the last path + /// point if both are greater than zero but too small to describe an arc. + /// + /// See Conversion from endpoint to center parametrization described in + /// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter + /// as reference for implementation. + void arcToPoint( + Offset arcEnd, { + Radius radius = Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { + assert(engine.offsetIsValid(arcEnd)); + assert(engine.radiusIsValid(radius)); + // _currentX, _currentY are the coordinates of start point on path, + // arcEnd is final point of arc. + // rx,ry are the radii of the eclipse (semi-major/semi-minor axis) + // largeArc is false if arc is spanning less than or equal to 180 degrees. + // clockwise is false if arc sweeps through decreasing angles or true + // if sweeping through increasing angles. + // rotation is the angle from the x-axis of the current coordinate + // system to the x-axis of the eclipse. + + double rx = radius.x.abs(); + double ry = radius.y.abs(); + + // If the current point and target point for the arc are identical, it + // should be treated as a zero length path. This ensures continuity in + // animations. + final bool isSamePoint = _currentX == arcEnd.dx && _currentY == arcEnd.dy; + + // If rx = 0 or ry = 0 then this arc is treated as a straight line segment + // (a "lineto") joining the endpoints. + // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters + if (isSamePoint || rx.toInt() == 0 || ry.toInt() == 0) { + _commands.add(engine.LineTo(arcEnd.dx, arcEnd.dy)); + _setCurrentPoint(arcEnd.dx, arcEnd.dy); + return; + } + + // As an intermediate point to finding center parametrization, place the + // origin on the midpoint between start/end points and rotate to align + // coordinate axis with axes of the ellipse. + final double midPointX = (_currentX - arcEnd.dx) / 2.0; + final double midPointY = (_currentY - arcEnd.dy) / 2.0; + + // Convert rotation or radians. + final double xAxisRotation = math.pi * rotation / 180.0; + + // Cache cos/sin value. + final double cosXAxisRotation = math.cos(xAxisRotation); + final double sinXAxisRotation = math.sin(xAxisRotation); + + // Calculate rotate midpoint as x/yPrime. + final double xPrime = + (cosXAxisRotation * midPointX) + (sinXAxisRotation * midPointY); + final double yPrime = + (-sinXAxisRotation * midPointX) + (cosXAxisRotation * midPointY); + + // Check if the radii are big enough to draw the arc, scale radii if not. + // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii + double rxSquare = rx * rx; + double rySquare = ry * ry; + final double xPrimeSquare = xPrime * xPrime; + final double yPrimeSquare = yPrime * yPrime; + + double radiiScale = (xPrimeSquare / rxSquare) + (yPrimeSquare / rySquare); + if (radiiScale > 1) { + radiiScale = math.sqrt(radiiScale); + rx *= radiiScale; + ry *= radiiScale; + rxSquare = rx * rx; + rySquare = ry * ry; + } + + // Compute transformed center. eq. 5.2 + final double distanceSquare = + (rxSquare * yPrimeSquare) + rySquare * xPrimeSquare; + final double cNumerator = (rxSquare * rySquare) - distanceSquare; + double scaleFactor = math.sqrt(math.max(cNumerator / distanceSquare, 0.0)); + if (largeArc == clockwise) { + scaleFactor = -scaleFactor; + } + // Ready to compute transformed center. + final double cxPrime = scaleFactor * ((rx * yPrime) / ry); + final double cyPrime = scaleFactor * (-(ry * xPrime) / rx); + + // Rotate to find actual center. + final double cx = cosXAxisRotation * cxPrime - + sinXAxisRotation * cyPrime + + ((_currentX + arcEnd.dx) / 2.0); + final double cy = sinXAxisRotation * cxPrime + + cosXAxisRotation * cyPrime + + ((_currentY + arcEnd.dy) / 2.0); + + // Calculate start angle and sweep. + // Start vector is from midpoint of start/end points to transformed center. + final double startVectorX = (xPrime - cxPrime) / rx; + final double startVectorY = (yPrime - cyPrime) / ry; + + final double startAngle = math.atan2(startVectorY, startVectorX); + final double endVectorX = (-xPrime - cxPrime) / rx; + final double endVectorY = (-yPrime - cyPrime) / ry; + double sweepAngle = math.atan2(endVectorY, endVectorX) - startAngle; + + if (clockwise && sweepAngle < 0) { + sweepAngle += math.pi * 2.0; + } else if (!clockwise && sweepAngle > 0) { + sweepAngle -= math.pi * 2.0; + } + + _commands.add(engine.Ellipse(cx, cy, rx, ry, xAxisRotation, startAngle, + startAngle + sweepAngle, sweepAngle.isNegative)); + + _setCurrentPoint(arcEnd.dx, arcEnd.dy); + } + + /// Appends up to four conic curves weighted to describe an oval of `radius` + /// and rotated by `rotation`. + /// + /// The last path point is described by (px, py). + /// + /// The first curve begins from the last point in the path and the last ends + /// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a + /// path in a direction determined by `clockwise` and `largeArc` + /// in such a way that the sweep angle is always less than 360 degrees. + /// + /// A simple line is appended if either either radii are zero, or, both + /// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to + /// fit the last path point if both are greater than zero but too small to + /// describe an arc. + void relativeArcToPoint( + Offset arcEndDelta, { + Radius radius = Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { + assert(engine.offsetIsValid(arcEndDelta)); + assert(engine.radiusIsValid(radius)); + arcToPoint(Offset(_currentX + arcEndDelta.dx, _currentY + arcEndDelta.dy), + radius: radius, + rotation: rotation, + largeArc: largeArc, + clockwise: clockwise); + } + + /// Adds a new subpath that consists of four lines that outline the + /// given rectangle. + void addRect(Rect rect) { + assert(engine.rectIsValid(rect)); + _openNewSubpath(rect.left, rect.top); + _commands + .add(engine.RectCommand(rect.left, rect.top, rect.width, rect.height)); + } + + /// Adds a new subpath that consists of a curve that forms the + /// ellipse that fills the given rectangle. + /// + /// To add a circle, pass an appropriate rectangle as `oval`. + /// [Rect.fromCircle] can be used to easily describe the circle's center + /// [Offset] and radius. + void addOval(Rect oval) { + assert(engine.rectIsValid(oval)); + final Offset center = oval.center; + final double radiusX = oval.width / 2; + final double radiusY = oval.height / 2; + + /// At startAngle = 0, the path will begin at center + cos(0) * radius. + _openNewSubpath(center.dx + radiusX, center.dy); + _commands.add(engine.Ellipse( + center.dx, center.dy, radiusX, radiusY, 0.0, 0.0, 2 * math.pi, false)); + } + + /// Adds a new subpath with one arc segment that consists of the arc + /// that follows the edge of the oval bounded by the given + /// rectangle, from startAngle radians around the oval up to + /// startAngle + sweepAngle radians around the oval, with zero + /// radians being the point on the right hand side of the oval that + /// crosses the horizontal line that intersects the center of the + /// rectangle and with positive angles going clockwise around the + /// oval. + void addArc(Rect oval, double startAngle, double sweepAngle) { + assert(engine.rectIsValid(oval)); + final Offset center = oval.center; + final double radiusX = oval.width / 2; + final double radiusY = oval.height / 2; + _openNewSubpath(radiusX * math.cos(startAngle) + center.dx, + radiusY * math.sin(startAngle) + center.dy); + _commands.add(engine.Ellipse(center.dx, center.dy, radiusX, radiusY, 0.0, + startAngle, startAngle + sweepAngle, sweepAngle.isNegative)); + + _setCurrentPoint(radiusX * math.cos(startAngle + sweepAngle) + center.dx, + radiusY * math.sin(startAngle + sweepAngle) + center.dy); + } + + /// Adds a new subpath with a sequence of line segments that connect the given + /// points. + /// + /// If `close` is true, a final line segment will be added that connects the + /// last point to the first point. + /// + /// The `points` argument is interpreted as offsets from the origin. + void addPolygon(List points, bool close) { + assert(points != null); + if (points.isEmpty) { + return; + } + + moveTo(points.first.dx, points.first.dy); + for (int i = 1; i < points.length; i++) { + final Offset point = points[i]; + lineTo(point.dx, point.dy); + } + if (close) { + this.close(); + } else { + _setCurrentPoint(points.last.dx, points.last.dy); + } + } + + /// Adds a new subpath that consists of the straight lines and + /// curves needed to form the rounded rectangle described by the + /// argument. + void addRRect(RRect rrect) { + assert(engine.rrectIsValid(rrect)); + + // Set the current point to the top left corner of the rectangle (the + // point on the top of the rectangle farthest to the left that isn't in + // the rounded corner). + // TODO(het): Is this the current point in Flutter? + _openNewSubpath(rrect.tallMiddleRect.left, rrect.top); + _commands.add(engine.RRectCommand(rrect)); + } + + /// Adds a new subpath that consists of the given `path` offset by the given + /// `offset`. + /// + /// If `matrix4` is specified, the path will be transformed by this matrix + /// after the matrix is translated by the given offset. The matrix is a 4x4 + /// matrix stored in column major order. + void addPath(Path path, Offset offset, {Float64List matrix4}) { + assert(path != null); // path is checked on the engine side + assert(engine.offsetIsValid(offset)); + if (matrix4 != null) { + assert(engine.matrix4IsValid(matrix4)); + _addPathWithMatrix(path, offset.dx, offset.dy, matrix4); + } else { + _addPath(path, offset.dx, offset.dy); + } + } + + void _addPath(Path path, double dx, double dy) { + if (dx == 0.0 && dy == 0.0) { + subpaths.addAll(path.subpaths); + } else { + subpaths.addAll(path + .transform(engine.Matrix4.translationValues(dx, dy, 0.0).storage) + .subpaths); + } + } + + void _addPathWithMatrix(Path path, double dx, double dy, Float64List matrix) { + final engine.Matrix4 transform = engine.Matrix4.fromFloat64List(matrix); + transform.translate(dx, dy); + subpaths.addAll(path.transform(transform.storage).subpaths); + } + + /// Adds the given path to this path by extending the current segment of this + /// path with the the first segment of the given path. + /// + /// If `matrix4` is specified, the path will be transformed by this matrix + /// after the matrix is translated by the given `offset`. The matrix is a 4x4 + /// matrix stored in column major order. + void extendWithPath(Path path, Offset offset, {Float64List matrix4}) { + assert(path != null); // path is checked on the engine side + assert(engine.offsetIsValid(offset)); + if (matrix4 != null) { + assert(engine.matrix4IsValid(matrix4)); + _extendWithPathAndMatrix(path, offset.dx, offset.dy, matrix4); + } else { + _extendWithPath(path, offset.dx, offset.dy); + } + } + + void _extendWithPath(Path path, double dx, double dy) { + if (dx == 0.0 && dy == 0.0) { + assert(path.subpaths.length == 1); + _ensurePathStarted(); + _commands.addAll(path.subpaths.single.commands); + _setCurrentPoint( + path.subpaths.single.currentX, path.subpaths.single.currentY); + } else { + throw UnimplementedError('Cannot extend path with non-zero offset'); + } + } + + void _extendWithPathAndMatrix( + Path path, double dx, double dy, Float64List matrix) { + throw UnimplementedError('Cannot extend path with transform matrix'); + } + + /// Closes the last subpath, as if a straight line had been drawn + /// from the current point to the first point of the subpath. + void close() { + _ensurePathStarted(); + _commands.add(const engine.CloseCommand()); + _setCurrentPoint(_currentSubpath.startX, _currentSubpath.startY); + } + + /// Clears the [Path] object of all subpaths, returning it to the + /// same state it had when it was created. The _current point_ is + /// reset to the origin. + void reset() { + subpaths.clear(); + } + + /// Tests to see if the given point is within the path. (That is, whether the + /// point would be in the visible portion of the path if the path was used + /// with [Canvas.clipPath].) + /// + /// The `point` argument is interpreted as an offset from the origin. + /// + /// Returns true if the point is in the path, and false otherwise. + /// + /// Note: Not very efficient, it creates a canvas, plays path and calls + /// Context2D isPointInPath. If performance becomes issue, retaining + /// RawRecordingCanvas can remove create/remove rootElement cost. + bool contains(Offset point) { + assert(engine.offsetIsValid(point)); + final int subPathCount = subpaths.length; + if (subPathCount == 0) { + return false; + } + final double pointX = point.dx; + final double pointY = point.dy; + if (subPathCount == 1) { + // Optimize for rect/roundrect checks. + final engine.Subpath subPath = subpaths[0]; + if (subPath.commands.length == 1) { + final engine.PathCommand cmd = subPath.commands[0]; + if (cmd is engine.RectCommand) { + if (pointY < cmd.y || pointY > (cmd.y + cmd.height)) { + return false; + } + if (pointX < cmd.x || pointX > (cmd.x + cmd.width)) { + return false; + } + return true; + } else if (cmd is engine.RRectCommand) { + final RRect rRect = cmd.rrect; + if (pointY < rRect.top || pointY > rRect.bottom) { + return false; + } + if (pointX < rRect.left || pointX > rRect.right) { + return false; + } + if (pointX < (rRect.left + rRect.tlRadiusX) && + pointY < (rRect.top + rRect.tlRadiusY)) { + // Top left corner + return _ellipseContains( + pointX, + pointY, + rRect.left + rRect.tlRadiusX, + rRect.top + rRect.tlRadiusY, + rRect.tlRadiusX, + rRect.tlRadiusY); + } else if (pointX >= (rRect.right - rRect.trRadiusX) && + pointY < (rRect.top + rRect.trRadiusY)) { + // Top right corner + return _ellipseContains( + pointX, + pointY, + rRect.right - rRect.trRadiusX, + rRect.top + rRect.trRadiusY, + rRect.trRadiusX, + rRect.trRadiusY); + } else if (pointX >= (rRect.right - rRect.brRadiusX) && + pointY >= (rRect.bottom - rRect.brRadiusY)) { + // Bottom right corner + return _ellipseContains( + pointX, + pointY, + rRect.right - rRect.brRadiusX, + rRect.bottom - rRect.brRadiusY, + rRect.trRadiusX, + rRect.trRadiusY); + } else if (pointX < (rRect.left + rRect.blRadiusX) && + pointY >= (rRect.bottom - rRect.blRadiusY)) { + // Bottom left corner + return _ellipseContains( + pointX, + pointY, + rRect.left + rRect.blRadiusX, + rRect.bottom - rRect.blRadiusY, + rRect.trRadiusX, + rRect.trRadiusY); + } + return true; + } + } + } + final Size size = window.physicalSize / window.devicePixelRatio; + _rawRecorder ??= RawRecordingCanvas(size); + // Account for the shift due to padding. + _rawRecorder.translate(-engine.BitmapCanvas.kPaddingPixels.toDouble(), + -engine.BitmapCanvas.kPaddingPixels.toDouble()); + _rawRecorder.drawPath( + this, (Paint()..color = const Color(0xFF000000)).webOnlyPaintData); + final bool result = _rawRecorder.ctx.isPointInPath(pointX, pointY); + _rawRecorder.dispose(); + return result; + } + + /// Returns a copy of the path with all the segments of every + /// subpath translated by the given offset. + Path shift(Offset offset) { + assert(engine.offsetIsValid(offset)); + final List shiftedSubPaths = []; + for (final engine.Subpath subPath in subpaths) { + shiftedSubPaths.add(subPath.shift(offset)); + } + return Path._clone(shiftedSubPaths, fillType); + } + + /// Returns a copy of the path with all the segments of every + /// sub path transformed by the given matrix. + Path transform(Float64List matrix4) { + assert(engine.matrix4IsValid(matrix4)); + final Path transformedPath = Path(); + for (final engine.Subpath subPath in subpaths) { + for (final engine.PathCommand cmd in subPath.commands) { + cmd.transform(matrix4, transformedPath); + } + } + return transformedPath; + } + + /// Computes the bounding rectangle for this path. + /// + /// A path containing only axis-aligned points on the same straight line will + /// have no area, and therefore `Rect.isEmpty` will return true for such a + /// path. Consider checking `rect.width + rect.height > 0.0` instead, or + /// using the [computeMetrics] API to check the path length. + /// + /// For many more elaborate paths, the bounds may be inaccurate. For example, + /// when a path contains a circle, the points used to compute the bounds are + /// the circle's implied control points, which form a square around the + /// circle; if the circle has a transformation applied using [transform] then + /// that square is rotated, and the (axis-aligned, non-rotated) bounding box + /// therefore ends up grossly overestimating the actual area covered by the + /// circle. + // see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds + Rect getBounds() { + // Sufficiently small number for curve eq. + const double epsilon = 0.000000001; + bool ltrbInitialized = false; + double left = 0.0, top = 0.0, right = 0.0, bottom = 0.0; + double curX = 0.0; + double curY = 0.0; + double minX = 0.0, maxX = 0.0, minY = 0.0, maxY = 0.0; + for (engine.Subpath subpath in subpaths) { + for (engine.PathCommand op in subpath.commands) { + bool skipBounds = false; + switch (op.type) { + case engine.PathCommandTypes.moveTo: + final engine.MoveTo cmd = op; + curX = minX = maxX = cmd.x; + curY = minY = maxY = cmd.y; + break; + case engine.PathCommandTypes.lineTo: + final engine.LineTo cmd = op; + curX = minX = maxX = cmd.x; + curY = minY = maxY = cmd.y; + break; + case engine.PathCommandTypes.ellipse: + final engine.Ellipse cmd = op; + // Rotate 4 corners of bounding box. + final double rx = cmd.radiusX; + final double ry = cmd.radiusY; + final double cosVal = math.cos(cmd.rotation); + final double sinVal = math.sin(cmd.rotation); + final double rxCos = rx * cosVal; + final double ryCos = ry * cosVal; + final double rxSin = rx * sinVal; + final double rySin = ry * sinVal; + + final double leftDeltaX = rxCos - rySin; + final double rightDeltaX = -rxCos - rySin; + final double topDeltaY = ryCos + rxSin; + final double bottomDeltaY = ryCos - rxSin; + + final double centerX = cmd.x; + final double centerY = cmd.y; + + double rotatedX = centerX + leftDeltaX; + double rotatedY = centerY + topDeltaY; + minX = maxX = rotatedX; + minY = maxY = rotatedY; + + rotatedX = centerX + rightDeltaX; + rotatedY = centerY + bottomDeltaY; + minX = math.min(minX, rotatedX); + maxX = math.max(maxX, rotatedX); + minY = math.min(minY, rotatedY); + maxY = math.max(maxY, rotatedY); + + rotatedX = centerX - leftDeltaX; + rotatedY = centerY - topDeltaY; + minX = math.min(minX, rotatedX); + maxX = math.max(maxX, rotatedX); + minY = math.min(minY, rotatedY); + maxY = math.max(maxY, rotatedY); + + rotatedX = centerX - rightDeltaX; + rotatedY = centerY - bottomDeltaY; + minX = math.min(minX, rotatedX); + maxX = math.max(maxX, rotatedX); + minY = math.min(minY, rotatedY); + maxY = math.max(maxY, rotatedY); + + curX = centerX + cmd.radiusX; + curY = centerY; + break; + case engine.PathCommandTypes.quadraticCurveTo: + final engine.QuadraticCurveTo cmd = op; + final double x1 = curX; + final double y1 = curY; + final double cpX = cmd.x1; + final double cpY = cmd.y1; + final double x2 = cmd.x2; + final double y2 = cmd.y2; + + minX = math.min(x1, x2); + minY = math.min(y1, y2); + maxX = math.max(x1, x2); + maxY = math.max(y1, y2); + + // Curve equation : (1-t)(1-t)P1 + 2t(1-t)CP + t*t*P2. + // At extrema's derivative = 0. + // Solve for + // -2x1+2tx1 + 2cpX + 4tcpX + 2tx2 = 0 + // -2x1 + 2cpX +2t(x1 + 2cpX + x2) = 0 + // t = (x1 - cpX) / (x1 - 2cpX + x2) + + double denom = x1 - (2 * cpX) + x2; + if (denom.abs() > epsilon) { + final num t1 = (x1 - cpX) / denom; + if ((t1 >= 0) && (t1 <= 1.0)) { + // Solve (x,y) for curve at t = tx to find extrema + final num tprime = 1.0 - t1; + final num extremaX = (tprime * tprime * x1) + + (2 * t1 * tprime * cpX) + + (t1 * t1 * x2); + final num extremaY = (tprime * tprime * y1) + + (2 * t1 * tprime * cpY) + + (t1 * t1 * y2); + // Expand bounds. + minX = math.min(minX, extremaX); + maxX = math.max(maxX, extremaX); + minY = math.min(minY, extremaY); + maxY = math.max(maxY, extremaY); + } + } + // Now calculate dy/dt = 0 + denom = y1 - (2 * cpY) + y2; + if (denom.abs() > epsilon) { + final num t2 = (y1 - cpY) / denom; + if ((t2 >= 0) && (t2 <= 1.0)) { + final num tprime2 = 1.0 - t2; + final num extrema2X = (tprime2 * tprime2 * x1) + + (2 * t2 * tprime2 * cpX) + + (t2 * t2 * x2); + final num extrema2Y = (tprime2 * tprime2 * y1) + + (2 * t2 * tprime2 * cpY) + + (t2 * t2 * y2); + // Expand bounds. + minX = math.min(minX, extrema2X); + maxX = math.max(maxX, extrema2X); + minY = math.min(minY, extrema2Y); + maxY = math.max(maxY, extrema2Y); + } + } + curX = x2; + curY = y2; + break; + case engine.PathCommandTypes.bezierCurveTo: + final engine.BezierCurveTo cmd = op; + final double startX = curX; + final double startY = curY; + final double cpX1 = cmd.x1; + final double cpY1 = cmd.y1; + final double cpX2 = cmd.x2; + final double cpY2 = cmd.y2; + final double endX = cmd.x3; + final double endY = cmd.y3; + // Bounding box is defined by all points on the curve where + // monotonicity changes. + minX = math.min(startX, endX); + minY = math.min(startY, endY); + maxX = math.max(startX, endX); + maxY = math.max(startY, endY); + + double extremaX; + double extremaY; + double a, b, c; + + // Check for simple case of strong ordering before calculating + // extrema + if (!(((startX < cpX1) && (cpX1 < cpX2) && (cpX2 < endX)) || + ((startX > cpX1) && (cpX1 > cpX2) && (cpX2 > endX)))) { + // The extrema point is dx/dt B(t) = 0 + // The derivative of B(t) for cubic bezier is a quadratic equation + // with multiple roots + // B'(t) = a*t*t + b*t + c*t + a = -startX + (3 * (cpX1 - cpX2)) + endX; + b = 2 * (startX - (2 * cpX1) + cpX2); + c = -startX + cpX1; + + // Now find roots for quadratic equation with known coefficients + // a,b,c + // The roots are (-b+-sqrt(b*b-4*a*c)) / 2a + num s = (b * b) - (4 * a * c); + // If s is negative, we have no real roots + if ((s >= 0.0) && (a.abs() > epsilon)) { + if (s == 0.0) { + // we have only 1 root + final num t = -b / (2 * a); + final num tprime = 1.0 - t; + if ((t >= 0.0) && (t <= 1.0)) { + extremaX = ((tprime * tprime * tprime) * startX) + + ((3 * tprime * tprime * t) * cpX1) + + ((3 * tprime * t * t) * cpX2) + + (t * t * t * endX); + minX = math.min(extremaX, minX); + maxX = math.max(extremaX, maxX); + } + } else { + // we have 2 roots + s = math.sqrt(s); + num t = (-b - s) / (2 * a); + num tprime = 1.0 - t; + if ((t >= 0.0) && (t <= 1.0)) { + extremaX = ((tprime * tprime * tprime) * startX) + + ((3 * tprime * tprime * t) * cpX1) + + ((3 * tprime * t * t) * cpX2) + + (t * t * t * endX); + minX = math.min(extremaX, minX); + maxX = math.max(extremaX, maxX); + } + // check 2nd root + t = (-b + s) / (2 * a); + tprime = 1.0 - t; + if ((t >= 0.0) && (t <= 1.0)) { + extremaX = ((tprime * tprime * tprime) * startX) + + ((3 * tprime * tprime * t) * cpX1) + + ((3 * tprime * t * t) * cpX2) + + (t * t * t * endX); + + minX = math.min(extremaX, minX); + maxX = math.max(extremaX, maxX); + } + } + } + } + + // Now calc extremes for dy/dt = 0 just like above + if (!(((startY < cpY1) && (cpY1 < cpY2) && (cpY2 < endY)) || + ((startY > cpY1) && (cpY1 > cpY2) && (cpY2 > endY)))) { + // The extrema point is dy/dt B(t) = 0 + // The derivative of B(t) for cubic bezier is a quadratic equation + // with multiple roots + // B'(t) = a*t*t + b*t + c*t + a = -startY + (3 * (cpY1 - cpY2)) + endY; + b = 2 * (startY - (2 * cpY1) + cpY2); + c = -startY + cpY1; + + // Now find roots for quadratic equation with known coefficients + // a,b,c + // The roots are (-b+-sqrt(b*b-4*a*c)) / 2a + num s = (b * b) - (4 * a * c); + // If s is negative, we have no real roots + if ((s >= 0.0) && (a.abs() > epsilon)) { + if (s == 0.0) { + // we have only 1 root + final num t = -b / (2 * a); + final num tprime = 1.0 - t; + if ((t >= 0.0) && (t <= 1.0)) { + extremaY = ((tprime * tprime * tprime) * startY) + + ((3 * tprime * tprime * t) * cpY1) + + ((3 * tprime * t * t) * cpY2) + + (t * t * t * endY); + minY = math.min(extremaY, minY); + maxY = math.max(extremaY, maxY); + } + } else { + // we have 2 roots + s = math.sqrt(s); + final num t = (-b - s) / (2 * a); + final num tprime = 1.0 - t; + if ((t >= 0.0) && (t <= 1.0)) { + extremaY = ((tprime * tprime * tprime) * startY) + + ((3 * tprime * tprime * t) * cpY1) + + ((3 * tprime * t * t) * cpY2) + + (t * t * t * endY); + minY = math.min(extremaY, minY); + maxY = math.max(extremaY, maxY); + } + // check 2nd root + final num t2 = (-b + s) / (2 * a); + final num tprime2 = 1.0 - t2; + if ((t2 >= 0.0) && (t2 <= 1.0)) { + extremaY = ((tprime2 * tprime2 * tprime2) * startY) + + ((3 * tprime2 * tprime2 * t2) * cpY1) + + ((3 * tprime2 * t2 * t2) * cpY2) + + (t2 * t2 * t2 * endY); + minY = math.min(extremaY, minY); + maxY = math.max(extremaY, maxY); + } + } + } + } + break; + case engine.PathCommandTypes.rect: + final engine.RectCommand cmd = op; + left = cmd.x; + double width = cmd.width; + if (cmd.width < 0) { + left -= width; + width = -width; + } + double top = cmd.y; + double height = cmd.height; + if (cmd.height < 0) { + top -= height; + height = -height; + } + curX = minX = left; + maxX = left + width; + curY = minY = top; + maxY = top + height; + break; + case engine.PathCommandTypes.rRect: + final engine.RRectCommand cmd = op; + final RRect rRect = cmd.rrect; + curX = minX = rRect.left; + maxX = rRect.left + rRect.width; + curY = minY = rRect.top; + maxY = rRect.top + rRect.height; + break; + case engine.PathCommandTypes.close: + default: + skipBounds = false; + break; + } + if (!skipBounds) { + if (!ltrbInitialized) { + left = minX; + right = maxX; + top = minY; + bottom = maxY; + ltrbInitialized = true; + } else { + left = math.min(left, minX); + right = math.max(right, maxX); + top = math.min(top, minY); + bottom = math.max(bottom, maxY); + } + } + } + } + return ltrbInitialized + ? Rect.fromLTRB(left, top, right, bottom) + : Rect.zero; + } + + /// Combines the two paths according to the manner specified by the given + /// `operation`. + /// + /// The resulting path will be constructed from non-overlapping contours. The + /// curve order is reduced where possible so that cubics may be turned into + /// quadratics, and quadratics maybe turned into lines. + static Path combine(PathOperation operation, Path path1, Path path2) { + assert(path1 != null); + assert(path2 != null); + throw UnimplementedError(); + } + + /// Creates a [PathMetrics] object for this path. + /// + /// If `forceClosed` is set to true, the contours of the path will be measured + /// as if they had been closed, even if they were not explicitly closed. + PathMetrics computeMetrics({bool forceClosed = false}) { + return PathMetrics._(this, forceClosed); + } + + /// Detects if path is rounded rectangle and returns rounded rectangle or + /// null. + /// + /// Used for web optimization of physical shape represented as + /// a persistent div. + RRect get webOnlyPathAsRoundedRect { + if (subpaths.length != 1) { + return null; + } + final engine.Subpath subPath = subpaths[0]; + if (subPath.commands.length != 1) { + return null; + } + final engine.PathCommand command = subPath.commands[0]; + return (command is engine.RRectCommand) ? command.rrect : null; + } + + /// Detects if path is simple rectangle and returns rectangle or null. + /// + /// Used for web optimization of physical shape represented as + /// a persistent div. + Rect get webOnlyPathAsRect { + if (subpaths.length != 1) { + return null; + } + final engine.Subpath subPath = subpaths[0]; + if (subPath.commands.length != 1) { + return null; + } + final engine.PathCommand command = subPath.commands[0]; + return (command is engine.RectCommand) + ? Rect.fromLTWH(command.x, command.y, command.width, command.height) + : null; + } + + /// Detects if path is simple oval and returns [engine.Ellipse] or null. + /// + /// Used for web optimization of physical shape represented as + /// a persistent div. + engine.Ellipse get webOnlyPathAsCircle { + if (subpaths.length != 1) { + return null; + } + final engine.Subpath subPath = subpaths[0]; + if (subPath.commands.length != 1) { + return null; + } + final engine.PathCommand command = subPath.commands[0]; + if (command is engine.Ellipse) { + final engine.Ellipse ellipse = command; + if ((ellipse.endAngle - ellipse.startAngle) % (2 * math.pi) == 0.0) { + return ellipse; + } + } + return null; + } + + /// Serializes this path to a value that's sent to a CSS custom painter for + /// painting. + List webOnlySerializeToCssPaint() { + final List serializedSubpaths = []; + for (int i = 0; i < subpaths.length; i++) { + serializedSubpaths.add(subpaths[i].serializeToCssPaint()); + } + return serializedSubpaths; + } + + @override + String toString() { + if (engine.assertionsEnabled) { + return 'Path(${subpaths.join(', ')})'; + } else { + return super.toString(); + } + } +} diff --git a/lib/web_ui/lib/src/ui/path_metrics.dart b/lib/web_ui/lib/src/ui/path_metrics.dart new file mode 100644 index 0000000000000..a13957e426aff --- /dev/null +++ b/lib/web_ui/lib/src/ui/path_metrics.dart @@ -0,0 +1,176 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of ui; + +/// An iterable collection of [PathMetric] objects describing a [Path]. +/// +/// A [PathMetrics] object is created by using the [Path.computeMetrics] method, +/// and represents the path as it stood at the time of the call. Subsequent +/// modifications of the path do not affect the [PathMetrics] object. +/// +/// Each path metric corresponds to a segment, or contour, of a path. +/// +/// For example, a path consisting of a [Path.lineTo], a [Path.moveTo], and +/// another [Path.lineTo] will contain two contours and thus be represented by +/// two [PathMetric] objects. +/// +/// When iterating across a [PathMetrics]' contours, the [PathMetric] objects +/// are only valid until the next one is obtained. +class PathMetrics extends collection.IterableBase { + PathMetrics._(Path path, bool forceClosed) + : _iterator = PathMetricIterator._(PathMetric._(path, forceClosed)); + + final Iterator _iterator; + + @override + Iterator get iterator => _iterator; +} + +/// Tracks iteration from one segment of a path to the next for measurement. +class PathMetricIterator implements Iterator { + PathMetricIterator._(this._pathMetric); + + PathMetric _pathMetric; + bool _firstTime = true; + + @override + PathMetric get current => _firstTime ? null : _pathMetric; + + @override + bool moveNext() { + // PathMetric isn't a normal iterable - it's already initialized to its + // first Path. Should only call _moveNext when done with the first one. + if (_firstTime == true) { + _firstTime = false; + return true; + } else if (_pathMetric?._moveNext() == true) { + return true; + } + _pathMetric = null; + return false; + } +} + +/// Utilities for measuring a [Path] and extracting subpaths. +/// +/// Iterate over the object returned by [Path.computeMetrics] to obtain +/// [PathMetric] objects. +/// +/// Once created, metrics will only be valid while the iterator is at the given +/// contour. When the next contour's [PathMetric] is obtained, this object +/// becomes invalid. +class PathMetric { + final Path _path; + final bool _forceClosed; + + /// Create a new empty [Path] object. + PathMetric._(this._path, this._forceClosed); + + /// Return the total length of the current contour. + double get length => throw UnimplementedError(); + + /// Computes the position of hte current contour at the given offset, and the + /// angle of the path at that point. + /// + /// For example, calling this method with a distance of 1.41 for a line from + /// 0.0,0.0 to 2.0,2.0 would give a point 1.0,1.0 and the angle 45 degrees + /// (but in radians). + /// + /// Returns null if the contour has zero [length]. + /// + /// The distance is clamped to the [length] of the current contour. + Tangent getTangentForOffset(double distance) { + final Float32List posTan = _getPosTan(distance); + // first entry == 0 indicates that Skia returned false + if (posTan[0] == 0.0) { + return null; + } else { + return Tangent( + Offset(posTan[1], posTan[2]), Offset(posTan[3], posTan[4])); + } + } + + Float32List _getPosTan(double distance) => throw UnimplementedError(); + + /// Given a start and stop distance, return the intervening segment(s). + /// + /// `start` and `end` are pinned to legal values (0..[length]) + /// Returns null if the segment is 0 length or `start` > `stop`. + /// Begin the segment with a moveTo if `startWithMoveTo` is true. + Path extractPath(double start, double end, {bool startWithMoveTo = true}) => + throw UnimplementedError(); + + /// Whether the contour is closed. + /// + /// Returns true if the contour ends with a call to [Path.close] (which may + /// have been implied when using [Path.addRect]) or if `forceClosed` was + /// specified as true in the call to [Path.computeMetrics]. Returns false + /// otherwise. + bool get isClosed => throw UnimplementedError(); + + // Move to the next contour in the path. + // + // A path can have a next contour if [Path.moveTo] was called after drawing + // began. Return true if one exists, or false. + // + // This is not exactly congruent with a regular [Iterator.moveNext]. + // Typically, [Iterator.moveNext] should be called before accessing the + // [Iterator.current]. In this case, the [PathMetric] is valid before + // calling `_moveNext` - `_moveNext` should be called after the first + // iteration is done instead of before. + bool _moveNext() => throw UnimplementedError(); + + @override + String toString() => 'PathMetric'; +} + +/// The geometric description of a tangent: the angle at a point. +/// +/// See also: +/// * [PathMetric.getTangentForOffset], which returns the tangent of an offset +/// along a path. +class Tangent { + /// Creates a [Tangent] with the given values. + /// + /// The arguments must not be null. + const Tangent(this.position, this.vector) + : assert(position != null), + assert(vector != null); + + /// Creates a [Tangent] based on the angle rather than the vector. + /// + /// The [vector] is computed to be the unit vector at the given angle, + /// interpreted as clockwise radians from the x axis. + factory Tangent.fromAngle(Offset position, double angle) { + return Tangent(position, Offset(math.cos(angle), math.sin(angle))); + } + + /// Position of the tangent. + /// + /// When used with [PathMetric.getTangentForOffset], this represents the + /// precise position that the given offset along the path corresponds to. + final Offset position; + + /// The vector of the curve at [position]. + /// + /// When used with [PathMetric.getTangentForOffset], this is the vector of the + /// curve that is at the given offset along the path (i.e. the direction of + /// the curve at [position]). + final Offset vector; + + /// The direction of the curve at [position]. + /// + /// When used with [PathMetric.getTangentForOffset], this is the angle of the + /// curve that is the given offset along the path (i.e. the direction of the + /// curve at [position]). + /// + /// This value is in radians, with 0.0 meaning pointing along the x axis in + /// the positive x-axis direction, positive numbers pointing downward toward + /// the negative y-axis, i.e. in a clockwise direction, and negative numbers + /// pointing upward toward the positive y-axis, i.e. in a counter-clockwise + /// direction. + // flip the sign to be consistent with [Path.arcTo]'s `sweepAngle` + double get angle => -math.atan2(vector.dy, vector.dx); +} diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index a6aa4f7d18e50..24b3ad03e35e3 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -32,6 +32,8 @@ part 'src/ui/initialization.dart'; part 'src/ui/lerp.dart'; part 'src/ui/natives.dart'; part 'src/ui/painting.dart'; +part 'src/ui/path.dart'; +part 'src/ui/path_metrics.dart'; part 'src/ui/pointer.dart'; part 'src/ui/semantics.dart'; part 'src/ui/test_embedding.dart'; From c9afadccbf2384754c962bda37f738b0add60f75 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 12 Nov 2019 01:24:36 -0500 Subject: [PATCH 094/591] Roll src/third_party/skia 3de645cbca78..ad21d47cfa8d (15 commits) (#13790) https://skia.googlesource.com/skia.git/+log/3de645cbca78..ad21d47cfa8d git log 3de645cbca78..ad21d47cfa8d --date=short --no-merges --format='%ad %ae %s' 2019-11-11 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-11 bsalomon@google.com Clamp RGB outputs of GrYUVtoRGBEffect. 2019-11-11 herb@google.com Remove belongsToCache from SkStrike 2019-11-11 jlavrova@google.com Correct cluster index 2019-11-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms e51ca8b81987..0e5f77218153 (1 commits) 2019-11-11 sgilhuly@chromium.org Roll Dawn, fix build issues in Skia Dawn 2019-11-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms b118cb0ef477..e51ca8b81987 (1 commits) 2019-11-11 robertphillips@google.com Remove GrProgramDesc's need for the GrGpu 2019-11-11 mtklein@google.com add int3, vptest, jc 2019-11-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 652dbfc63e70..012d15196023 (9 commits) 2019-11-11 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update SKP version 2019-11-11 mtklein@google.com add assert_true() 2019-11-11 bsalomon@google.com C++17 2019-11-11 mtklein@google.com don't allocate an interpreter register for stores 2019-11-11 rosasco@google.com Staging for Chromium to accept Skia-GL changes to come. Created with: gclient setdep -r src/third_party/skia@ad21d47cfa8d If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c4097a5663e0f..63bb2fd75a0bc 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3de645cbca784e8daf740fe5945befb1800ed8c6', + 'skia_revision': 'ad21d47cfa8d9adb2145216c5e514d978d649096', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 1dec20ca5d18b..bb2e2805449eb 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: b2816ba7c238ea19879731426c08b7d3 +Signature: e2fba7540b7c9a199110c06d578872b8 UNUSED LICENSES: @@ -3390,6 +3390,7 @@ FILE: ../../../third_party/skia/gm/crbug_913349.cpp FILE: ../../../third_party/skia/gm/crbug_938592.cpp FILE: ../../../third_party/skia/gm/crbug_947055.cpp FILE: ../../../third_party/skia/gm/crbug_996140.cpp +FILE: ../../../third_party/skia/gm/ducky_yuv_blend.cpp FILE: ../../../third_party/skia/gm/fiddle.cpp FILE: ../../../third_party/skia/gm/mac_aa_explorer.cpp FILE: ../../../third_party/skia/gm/mixercolorfilter.cpp From e19ee72f31da6e0e11a60bb408db0aabf335f4a5 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Tue, 12 Nov 2019 07:14:27 +0000 Subject: [PATCH 095/591] Expose asset lookup from plugin binding. (#42019) (#13743) --- shell/platform/android/BUILD.gn | 1 + .../embedding/engine/FlutterEngine.java | 3 +- .../engine/FlutterEnginePluginRegistry.java | 31 ++++- .../engine/plugins/FlutterPlugin.java | 48 ++++++- .../test/io/flutter/FlutterTestSuite.java | 2 + .../embedding/engine/PluginComponentTest.java | 123 ++++++++++++++++++ 6 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 shell/platform/android/test/io/flutter/embedding/engine/PluginComponentTest.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 741635c92edce..ecd1d7ff81020 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -422,6 +422,7 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/FlutterEngineCacheTest.java", "test/io/flutter/embedding/engine/FlutterEngineTest.java", "test/io/flutter/embedding/engine/FlutterJNITest.java", + "test/io/flutter/embedding/engine/PluginComponentTest.java", "test/io/flutter/embedding/engine/RenderingComponentTest.java", "test/io/flutter/embedding/engine/dart/DartExecutorTest.java", "test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index ca03bc9419699..e37733515c7ec 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -207,7 +207,8 @@ public FlutterEngine( this.pluginRegistry = new FlutterEnginePluginRegistry( context.getApplicationContext(), - this + this, + flutterLoader ); if (automaticallyRegisterPlugins) { diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java index 29692195cad4c..c5a4e66d07c8c 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java @@ -21,6 +21,7 @@ import java.util.Set; import io.flutter.Log; +import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.PluginRegistry; import io.flutter.embedding.engine.plugins.activity.ActivityAware; @@ -90,7 +91,8 @@ class FlutterEnginePluginRegistry implements PluginRegistry, FlutterEnginePluginRegistry( @NonNull Context appContext, - @NonNull FlutterEngine flutterEngine + @NonNull FlutterEngine flutterEngine, + @NonNull FlutterLoader flutterLoader ) { this.flutterEngine = flutterEngine; pluginBinding = new FlutterPlugin.FlutterPluginBinding( @@ -98,7 +100,8 @@ class FlutterEnginePluginRegistry implements PluginRegistry, flutterEngine, flutterEngine.getDartExecutor(), flutterEngine.getRenderer(), - flutterEngine.getPlatformViewsController().getRegistry() + flutterEngine.getPlatformViewsController().getRegistry(), + new DefaultFlutterAssets(flutterLoader) ); } @@ -532,6 +535,30 @@ public void detachFromContentProvider() { } //----- End ContentProviderControlSurface ----- + private static class DefaultFlutterAssets implements FlutterPlugin.FlutterAssets { + final FlutterLoader flutterLoader; + + private DefaultFlutterAssets(@NonNull FlutterLoader flutterLoader) { + this.flutterLoader = flutterLoader; + } + + public String getAssetFilePathByName(@NonNull String assetFileName) { + return flutterLoader.getLookupKeyForAsset(assetFileName); + } + + public String getAssetFilePathByName(@NonNull String assetFileName, @NonNull String packageName) { + return flutterLoader.getLookupKeyForAsset(assetFileName, packageName); + } + + public String getAssetFilePathBySubpath(@NonNull String assetSubpath) { + return flutterLoader.getLookupKeyForAsset(assetSubpath); + } + + public String getAssetFilePathBySubpath(@NonNull String assetSubpath, @NonNull String packageName) { + return flutterLoader.getLookupKeyForAsset(assetSubpath, packageName); + } + } + private static class FlutterEngineActivityPluginBinding implements ActivityPluginBinding { @NonNull private final Activity activity; diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java b/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java index f3ff96e3a584a..46eaf06a7bf6a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java @@ -9,6 +9,7 @@ import android.support.annotation.NonNull; import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.platform.PlatformViewRegistry; import io.flutter.view.TextureRegistry; @@ -101,19 +102,22 @@ class FlutterPluginBinding { private final BinaryMessenger binaryMessenger; private final TextureRegistry textureRegistry; private final PlatformViewRegistry platformViewRegistry; + private final FlutterAssets flutterAssets; public FlutterPluginBinding( @NonNull Context applicationContext, @NonNull FlutterEngine flutterEngine, @NonNull BinaryMessenger binaryMessenger, @NonNull TextureRegistry textureRegistry, - @NonNull PlatformViewRegistry platformViewRegistry + @NonNull PlatformViewRegistry platformViewRegistry, + @NonNull FlutterAssets flutterAssets ) { this.applicationContext = applicationContext; this.flutterEngine = flutterEngine; this.binaryMessenger = binaryMessenger; this.textureRegistry = textureRegistry; this.platformViewRegistry = platformViewRegistry; + this.flutterAssets = flutterAssets; } @NonNull @@ -146,5 +150,47 @@ public TextureRegistry getTextureRegistry() { public PlatformViewRegistry getPlatformViewRegistry() { return platformViewRegistry; } + + @NonNull + public FlutterAssets getFlutterAssets() { + return flutterAssets; + } } + + /** + * Provides Flutter plugins with access to Flutter asset information. + */ + interface FlutterAssets { + /** + * Returns the relative file path to the Flutter asset with the given name, including the file's + * extension, e.g., {@code "myImage.jpg"}. + * + *

The returned file path is relative to the Android app's standard assets directory. + * Therefore, the returned path is appropriate to pass to Android's {@code AssetManager}, + * but the path is not appropriate to load as an absolute path. + */ + String getAssetFilePathByName(@NonNull String assetFileName); + + /** + * Same as {@link #getAssetFilePathByName(String)} but with added support for an explicit + * Android {@code packageName}. + */ + String getAssetFilePathByName(@NonNull String assetFileName, @NonNull String packageName); + + /** + * Returns the relative file path to the Flutter asset with the given subpath, including the file's + * extension, e.g., {@code "/dir1/dir2/myImage.jpg"}. + * + *

The returned file path is relative to the Android app's standard assets directory. + * Therefore, the returned path is appropriate to pass to Android's {@code AssetManager}, + * but the path is not appropriate to load as an absolute path. + */ + String getAssetFilePathBySubpath(@NonNull String assetSubpath); + + /** + * Same as {@link #getAssetFilePathBySubpath(String)} but with added support for an explicit + * Android {@code packageName}. + */ + String getAssetFilePathBySubpath(@NonNull String assetSubpath, @NonNull String packageName); + } } diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index b1188950539bd..ac99a9b45ae26 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -23,6 +23,7 @@ import io.flutter.plugin.platform.SingleViewPresentationTest; import io.flutter.util.PreconditionsTest; import test.io.flutter.embedding.engine.FlutterEngineTest; +import test.io.flutter.embedding.engine.PluginComponentTest; import test.io.flutter.embedding.engine.dart.DartExecutorTest; @RunWith(Suite.class) @@ -38,6 +39,7 @@ FlutterRendererTest.class, FlutterViewTest.class, PlatformChannelTest.class, + PluginComponentTest.class, PreconditionsTest.class, RenderingComponentTest.class, StandardMessageCodecTest.class, diff --git a/shell/platform/android/test/io/flutter/embedding/engine/PluginComponentTest.java b/shell/platform/android/test/io/flutter/embedding/engine/PluginComponentTest.java new file mode 100644 index 0000000000000..50b6e5335440f --- /dev/null +++ b/shell/platform/android/test/io/flutter/embedding/engine/PluginComponentTest.java @@ -0,0 +1,123 @@ +package test.io.flutter.embedding.engine; + +import android.support.annotation.NonNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.loader.FlutterLoader; +import io.flutter.embedding.engine.plugins.FlutterPlugin; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Config(manifest=Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class PluginComponentTest { + @Test + public void pluginsCanAccessFlutterAssetPaths() { + // Setup test. + FlutterJNI flutterJNI = mock(FlutterJNI.class); + when(flutterJNI.isAttached()).thenReturn(true); + + // FlutterLoader is the object to which the PluginRegistry defers for obtaining + // the path to a Flutter asset. Ideally in this component test we would use a + // real FlutterLoader and directly verify the relationship between FlutterAssets + // and FlutterLoader. However, a real FlutterLoader cannot be used in a JVM test + // because it would attempt to load native libraries. Therefore, we create a fake + // FlutterLoader, but then we defer the corresponding asset lookup methods to the + // real FlutterLoader singleton. This test ends up verifying that when FlutterAssets + // is queried for an asset path, it returns the real expected path based on real + // FlutterLoader behavior. + FlutterLoader flutterLoader = mock(FlutterLoader.class); + when(flutterLoader.getLookupKeyForAsset(any(String.class))).thenAnswer(new Answer() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + // Defer to a real FlutterLoader to return the asset path. + String fileNameOrSubpath = (String) invocation.getArguments()[0]; + return FlutterLoader.getInstance().getLookupKeyForAsset(fileNameOrSubpath); + } + }); + when(flutterLoader.getLookupKeyForAsset(any(String.class), any(String.class))).thenAnswer(new Answer() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + // Defer to a real FlutterLoader to return the asset path. + String fileNameOrSubpath = (String) invocation.getArguments()[0]; + String packageName = (String) invocation.getArguments()[1]; + return FlutterLoader.getInstance().getLookupKeyForAsset(fileNameOrSubpath, packageName); + } + }); + + // Execute behavior under test. + FlutterEngine flutterEngine = new FlutterEngine( + RuntimeEnvironment.application, + flutterLoader, + flutterJNI + ); + + // As soon as our plugin is registered it will look up asset paths and store them + // for our verification. + PluginThatAccessesAssets plugin = new PluginThatAccessesAssets(); + flutterEngine.getPlugins().add(plugin); + + // Verify results. + assertEquals("flutter_assets/fake_asset.jpg", plugin.getAssetPathBasedOnName()); + assertEquals("flutter_assets/packages/fakepackage/fake_asset.jpg", plugin.getAssetPathBasedOnNameAndPackage()); + assertEquals("flutter_assets/some/path/fake_asset.jpg", plugin.getAssetPathBasedOnSubpath()); + assertEquals("flutter_assets/packages/fakepackage/some/path/fake_asset.jpg", plugin.getAssetPathBasedOnSubpathAndPackage()); + } + + private static class PluginThatAccessesAssets implements FlutterPlugin { + private String assetPathBasedOnName; + private String assetPathBasedOnNameAndPackage; + private String assetPathBasedOnSubpath; + private String assetPathBasedOnSubpathAndPackage; + + public String getAssetPathBasedOnName() { + return assetPathBasedOnName; + } + + public String getAssetPathBasedOnNameAndPackage() { + return assetPathBasedOnNameAndPackage; + } + + public String getAssetPathBasedOnSubpath() { + return assetPathBasedOnSubpath; + } + + public String getAssetPathBasedOnSubpathAndPackage() { + return assetPathBasedOnSubpathAndPackage; + } + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + assetPathBasedOnName = binding + .getFlutterAssets() + .getAssetFilePathByName("fake_asset.jpg"); + + assetPathBasedOnNameAndPackage = binding + .getFlutterAssets() + .getAssetFilePathByName("fake_asset.jpg", "fakepackage"); + + assetPathBasedOnSubpath = binding + .getFlutterAssets() + .getAssetFilePathByName("some/path/fake_asset.jpg"); + + assetPathBasedOnSubpathAndPackage = binding + .getFlutterAssets() + .getAssetFilePathByName("some/path/fake_asset.jpg", "fakepackage"); + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {} + } +} From 679a4369b2e0a2ffb6fb18007f7517683d9dbf1a Mon Sep 17 00:00:00 2001 From: Ferhat Date: Tue, 12 Nov 2019 00:05:00 -0800 Subject: [PATCH 096/591] [web] Implement TextStyle.shadows (#13769) * Add shadows to Engine classes * add text shadow test * update golden locks file, update ui.ParagraphStyle, fix issues * Change maxDiffRate for mac clients --- lib/web_ui/lib/src/engine/text/paragraph.dart | 58 +++++++++++++++++-- lib/web_ui/lib/src/engine/text/ruler.dart | 16 ++++- lib/web_ui/lib/src/ui/text.dart | 17 +++--- .../test/golden_tests/engine/scuba.dart | 35 ++++++----- .../engine/text_style_golden_test.dart | 56 ++++++++++++++++++ 5 files changed, 150 insertions(+), 32 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 7b6faaf3ed209..98b62127dfe47 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -18,6 +18,7 @@ class EngineParagraph implements ui.Paragraph { @required ui.TextAlign textAlign, @required ui.TextDirection textDirection, @required ui.Paint background, + @required List shadows, }) : assert((plainText == null && paint == null) || (plainText != null && paint != null)), _paragraphElement = paragraphElement, @@ -26,7 +27,8 @@ class EngineParagraph implements ui.Paragraph { _textAlign = textAlign, _textDirection = textDirection, _paint = paint, - _background = background; + _background = background, + _shadows = shadows; final html.HtmlElement _paragraphElement; final ParagraphGeometricStyle _geometricStyle; @@ -35,6 +37,7 @@ class EngineParagraph implements ui.Paragraph { final ui.TextAlign _textAlign; final ui.TextDirection _textDirection; final ui.Paint _background; + final List _shadows; @visibleForTesting String get plainText => _plainText; @@ -287,7 +290,8 @@ class EngineParagraph implements ui.Paragraph { return ui.TextRange(start: textPosition.offset, end: textPosition.offset); } - final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset); + final int start = + WordBreaker.prevBreakIndex(_plainText, textPosition.offset); final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset); return ui.TextRange(start: start, end: end); } @@ -321,6 +325,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle strutStyle, String ellipsis, ui.Locale locale, + List shadows, }) : _textAlign = textAlign, _textDirection = textDirection, _fontWeight = fontWeight, @@ -332,7 +337,8 @@ class EngineParagraphStyle implements ui.ParagraphStyle { // TODO(b/128317744): add support for strut style. _strutStyle = strutStyle, _ellipsis = ellipsis, - _locale = locale; + _locale = locale, + _shadows = shadows; final ui.TextAlign _textAlign; final ui.TextDirection _textDirection; @@ -345,6 +351,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { final EngineStrutStyle _strutStyle; final String _ellipsis; final ui.Locale _locale; + final List _shadows; String get _effectiveFontFamily { if (assertionsEnabled) { @@ -413,6 +420,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { 'height: ${_height != null ? "${_height.toStringAsFixed(1)}x" : "unspecified"}, ' 'ellipsis: ${_ellipsis != null ? "\"$_ellipsis\"" : "unspecified"}, ' 'locale: ${_locale ?? "unspecified"}' + 'shadows: ${_shadows ?? "unspecified"}' ')'; } else { return super.toString(); @@ -798,6 +806,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { ui.Locale locale = _paragraphStyle._locale; ui.Paint background; ui.Paint foreground; + List shadows; int i = 0; @@ -852,6 +861,9 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { if (style._foreground != null) { foreground = style._foreground; } + if (style._shadows != null) { + shadows = style._shadows; + } i++; } @@ -871,6 +883,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { locale: locale, background: background, foreground: foreground, + shadows: shadows, ); ui.Paint paint; @@ -900,6 +913,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { wordSpacing: wordSpacing, decoration: _textDecorationToCssString(decoration, decorationStyle), ellipsis: _paragraphStyle._ellipsis, + shadows: shadows, ), plainText: '', paint: paint, @@ -953,6 +967,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { wordSpacing: wordSpacing, decoration: _textDecorationToCssString(decoration, decorationStyle), ellipsis: _paragraphStyle._ellipsis, + shadows: shadows, ), plainText: plainText, paint: paint, @@ -996,6 +1011,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { lineHeight: _paragraphStyle._height, maxLines: _paragraphStyle._maxLines, ellipsis: _paragraphStyle._ellipsis, + shadows: _paragraphStyle._shadows, ), plainText: null, paint: null, @@ -1082,6 +1098,9 @@ void _applyParagraphStyleToElement({ if (style._effectiveFontFamily != null) { cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily); } + if (style._shadows != null) { + cssStyle.textShadow = _shadowListToCss(style._shadows); + } } else { if (style._textAlign != previousStyle._textAlign) { cssStyle.textAlign = textAlignToCssValue( @@ -1108,6 +1127,9 @@ void _applyParagraphStyleToElement({ if (style._fontFamily != previousStyle._fontFamily) { cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); } + if (style._shadows != previousStyle._shadows) { + cssStyle.textShadow = _shadowListToCss(style._shadows); + } } } @@ -1150,7 +1172,8 @@ void _applyTextStyleToElement({ } } else { if (style._effectiveFontFamily != null) { - cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily); + cssStyle.fontFamily = + canonicalizeFontFamily(style._effectiveFontFamily); } } if (style._letterSpacing != null) { @@ -1162,6 +1185,9 @@ void _applyTextStyleToElement({ if (style._decoration != null) { updateDecoration = true; } + if (style._shadows != null) { + cssStyle.textShadow = _shadowListToCss(style._shadows); + } } else { if (style._color != previousStyle._color || style._foreground != previousStyle._foreground) { @@ -1197,6 +1223,9 @@ void _applyTextStyleToElement({ style._decorationColor != previousStyle._decorationColor) { updateDecoration = true; } + if (style._shadows != previousStyle._shadows) { + cssStyle.textShadow = _shadowListToCss(style._shadows); + } } if (updateDecoration) { @@ -1214,6 +1243,27 @@ void _applyTextStyleToElement({ } } +String _shadowListToCss(List shadows) { + if (shadows.isEmpty) { + return ''; + } + // CSS text-shadow is a comma separated list of shadows. + // . + // Shadows are applied front-to-back with first shadow on top. + // Color is optional. offsetx,y are required. blur-radius is optional as well + // and defaults to 0. + StringBuffer sb = new StringBuffer(); + for (int i = 0, len = shadows.length; i < len; i++) { + if (i != 0) { + sb.write(','); + } + ui.Shadow shadow = shadows[i]; + sb.write('${shadow.offset.dx}px ${shadow.offset.dy}px ' + '${shadow.blurRadius}px ${shadow.color.toCssString()}'); + } + return sb.toString(); +} + /// Applies background color properties in text style to paragraph or span /// elements. void _applyTextBackgroundToElement({ diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index e8c1944aff59c..58d180e593848 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -17,6 +17,7 @@ class ParagraphGeometricStyle { this.wordSpacing, this.decoration, this.ellipsis, + this.shadows, }); final ui.FontWeight fontWeight; @@ -29,6 +30,7 @@ class ParagraphGeometricStyle { final double wordSpacing; final String decoration; final String ellipsis; + final List shadows; // Since all fields above are primitives, cache hashcode since ruler lookups // use this style as key. @@ -109,7 +111,8 @@ class ParagraphGeometricStyle { letterSpacing == typedOther.letterSpacing && wordSpacing == typedOther.wordSpacing && decoration == typedOther.decoration && - ellipsis == typedOther.ellipsis; + ellipsis == typedOther.ellipsis && + shadows == typedOther.shadows; } @override @@ -124,8 +127,12 @@ class ParagraphGeometricStyle { wordSpacing, decoration, ellipsis, + _hashShadows(shadows), ); + int _hashShadows(List shadows) => + (shadows == null ? '' : _shadowListToCss(shadows)).hashCode; + @override String toString() { if (assertionsEnabled) { @@ -137,6 +144,7 @@ class ParagraphGeometricStyle { ' wordSpacing: $wordSpacing,' ' decoration: $decoration,' ' ellipsis: $ellipsis,' + ' shadows: $shadows,' ')'; } else { return super.toString(); @@ -241,6 +249,10 @@ class TextDimensions { if (style.lineHeight != null) { _element.style.lineHeight = style.lineHeight.toString(); } + final List shadowList = style.shadows; + if (shadowList != null) { + _element.style.textShadow = _shadowListToCss(shadowList); + } _invalidateBoundsCache(); } @@ -765,7 +777,7 @@ class ParagraphRuler { return null; } final List constraintCache = - _measurementCache[plainText]; + _measurementCache[plainText]; if (constraintCache == null) { return null; } diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index d003c7b6eda65..f4a798ef4cbc4 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -918,7 +918,7 @@ class TextRange { const TextRange({ this.start, this.end, - }) : assert(start != null && start >= -1), + }) : assert(start != null && start >= -1), assert(end != null && end >= -1); /// A text range that starts and ends at offset. @@ -971,20 +971,17 @@ class TextRange { @override bool operator ==(dynamic other) { - if (identical(this, other)) - return true; - if (other is! TextRange) - return false; + if (identical(this, other)) return true; + if (other is! TextRange) return false; final TextRange typedOther = other; - return typedOther.start == start - && typedOther.end == end; + return typedOther.start == start && typedOther.end == end; } @override int get hashCode => hashValues( - start.hashCode, - end.hashCode, - ); + start.hashCode, + end.hashCode, + ); @override String toString() => 'TextRange(start: $start, end: $end)'; diff --git a/lib/web_ui/test/golden_tests/engine/scuba.dart b/lib/web_ui/test/golden_tests/engine/scuba.dart index 598daa11a5c3c..18dff26fd7af9 100644 --- a/lib/web_ui/test/golden_tests/engine/scuba.dart +++ b/lib/web_ui/test/golden_tests/engine/scuba.dart @@ -39,18 +39,18 @@ class EngineScubaTester { return EngineScubaTester(viewportSize); } - Future diffScreenshot(String fileName) async { - await matchGoldenFile('$fileName.png', region: ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height)); + Future diffScreenshot(String fileName, {double maxDiffRate}) async { + await matchGoldenFile('$fileName.png', + region: ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height), + maxDiffRate: maxDiffRate); } /// Prepares the DOM and inserts all the necessary nodes, then invokes scuba's /// screenshot diffing. /// /// It also cleans up the DOM after itself. - Future diffCanvasScreenshot( - EngineCanvas canvas, - String fileName, - ) async { + Future diffCanvasScreenshot(EngineCanvas canvas, String fileName, + {double maxDiffRate}) async { // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); try { @@ -60,7 +60,7 @@ class EngineScubaTester { if (TextMeasurementService.enableExperimentalCanvasImplementation) { screenshotName += '+canvas_measurement'; } - await diffScreenshot(screenshotName); + await diffScreenshot(screenshotName, maxDiffRate: maxDiffRate); } finally { // The page is reused across tests, so remove the element after taking the // Scuba screenshot. @@ -72,7 +72,8 @@ class EngineScubaTester { typedef CanvasTest = FutureOr Function(EngineCanvas canvas); /// Runs the given test [body] with each type of canvas. -void testEachCanvas(String description, CanvasTest body) { +void testEachCanvas(String description, CanvasTest body, + {double maxDiffRate, bool bSkipHoudini = false}) { const ui.Rect bounds = ui.Rect.fromLTWH(0, 0, 600, 800); test('$description (bitmap)', () { try { @@ -100,14 +101,16 @@ void testEachCanvas(String description, CanvasTest body) { TextMeasurementService.clearCache(); } }); - test('$description (houdini)', () { - try { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - return body(HoudiniCanvas(bounds)); - } finally { - TextMeasurementService.clearCache(); - } - }); + if (!bSkipHoudini) { + test('$description (houdini)', () { + try { + TextMeasurementService.initialize(rulerCacheCapacity: 2); + return body(HoudiniCanvas(bounds)); + } finally { + TextMeasurementService.clearCache(); + } + }); + } } final ui.TextStyle _defaultTextStyle = ui.TextStyle( diff --git a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart index b0c10abf83f57..04742ad3f92f2 100644 --- a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart @@ -158,8 +158,64 @@ void main() async { ); } + void drawTextWithShadow(EngineCanvas canvas) { + // Single-line text. + canvas.drawParagraph( + paragraph( + 'Hello World', + maxWidth: 600, + textStyle: TextStyle( + color: const Color.fromRGBO(0, 0, 0, 1.0), + background: Paint()..color = const Color.fromRGBO(255, 50, 50, 1.0), + fontFamily: 'Arial', + fontSize: 30, + shadows: [ + Shadow( + blurRadius: 0, + color: const Color.fromRGBO(255, 0, 255, 1.0), + offset: Offset(10, 5), + ), + ], + ), + ), + Offset.zero, + ); + + // Multi-line text. + canvas.drawParagraph( + paragraph( + 'Multi line Hello World paragraph', + maxWidth: 200, + textStyle: TextStyle( + color: const Color.fromRGBO(0, 0, 0, 1.0), + background: Paint()..color = const Color.fromRGBO(50, 50, 255, 1.0), + fontFamily: 'Arial', + fontSize: 30, + shadows: [ + Shadow( + blurRadius: 0, + color: const Color.fromRGBO(255, 0, 255, 1.0), + offset: Offset(10, 5), + ), + Shadow( + blurRadius: 0, + color: const Color.fromRGBO(0, 255, 255, 1.0), + offset: Offset(-10, -5), + ), + ], + ), + ), + const Offset(0, 40), + ); + } + testEachCanvas('draws text with a background', (EngineCanvas canvas) { drawTextWithBackground(canvas); return scuba.diffCanvasScreenshot(canvas, 'text_background'); }); + + testEachCanvas('draws text with a shadow', (EngineCanvas canvas) { + drawTextWithShadow(canvas); + return scuba.diffCanvasScreenshot(canvas, 'text_shadow', maxDiffRate: 0.2); + }, bSkipHoudini: true); } From 5643988a9d16d87bc2f2596e9fabe6c991167a42 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 12 Nov 2019 03:13:58 -0500 Subject: [PATCH 097/591] Roll fuchsia/sdk/core/mac-amd64 from TnTbF... to tZdOC... (#13791) Roll fuchsia/sdk/core/mac-amd64 from TnTbF... to tZdOC... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 63bb2fd75a0bc..08b454899cc16 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'TnTbFIhfmLxcE3pJs17ZcWXT-iDYa1Ax8ee-BTCmR6YC' + 'version': 'tZdOCFmhdSOqqWySz07rEzkXjgmqL7rdAI4B0IBJfycC' } ], 'condition': 'host_os == "mac"', From 885852bd68ae182c25a81769f8b9971bf6018f33 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 12 Nov 2019 06:41:39 -0800 Subject: [PATCH 098/591] Roll src/third_party/dart 7408ed4730..7ebc820ffc (20 commits) dart-lang/sdk@7ebc820ffc [CFE] Also guard _extensions for LazyScope dart-lang/sdk@a196d68145 Revert "[vm, front-end server] Cache generated bytecode using separate file" dart-lang/sdk@b2fa458f3d [CFE] Support experiments in the incremental compiler tests dart-lang/sdk@da561313c6 [vm/bytecode] Erase promoted bounds of type parameter types dart-lang/sdk@be6e5d8717 [vm] Try to symbolize Dart frames on crash or assertion failure when there is an exit frame. dart-lang/sdk@804706c026 Represent variance as an object rather than an integer dart-lang/sdk@782b048625 Bump the ABI version dart-lang/sdk@a26f8402e5 [dartdevc] Branch the logic for FutureOr type tests when NNBD is enabled dart-lang/sdk@f9969cca58 Add Element.declaration to unify elements and members. dart-lang/sdk@b4ab28fef1 [vm/bytecode] Do not declare receiver variable in closures dart-lang/sdk@cd6dbad074 [front-end server] Cleanup --strong in test/frontend_server_flutter.dart dart-lang/sdk@6cde7ce224 Deprecate synthetic FunctionTypeImpl constructor. dart-lang/sdk@86641d07ae [vm, front-end server] Cache generated bytecode using separate file dart-lang/sdk@b5110a59a9 Revert "[vm/async] Don't add an marker along sync-async calls." dart-lang/sdk@263ac017be Use standard Set in LegacyTypeAsserter. dart-lang/sdk@c98df62dd0 [CFE] Add comments to clarify source encoding and offsets dart-lang/sdk@1d6584720b Migrating DDC dart:developer patch files. dart-lang/sdk@830b3d5f36 [front-end server] Remove obsolete --strong option dart-lang/sdk@f4d930997b [vm/async] Don't add an marker along sync-async calls. dart-lang/sdk@4366afc6f5 [http] noFolding() should follow same check as add() --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 08b454899cc16..a38659b9529f1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '7408ed473006eeb41e23b9b21d2f5544bdd22b7b', + 'dart_revision': '7ebc820ffcc06e08c0647b0a5e0f3bd41a967cf3', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 00281011526e5..78704cc26c1bd 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: c56797567c2eb8b80b7d17fddc26d426 +Signature: 03d479e89fa6c0abff5498a9980dfd24 UNUSED LICENSES: From 270fa824e46b9e7cb545c9215c0748a9c28bd302 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 12 Nov 2019 10:29:49 -0500 Subject: [PATCH 099/591] Roll src/third_party/skia ad21d47cfa8d..d860a78fd60c (2 commits) (#13794) https://skia.googlesource.com/skia.git/+log/ad21d47cfa8d..d860a78fd60c git log ad21d47cfa8d..d860a78fd60c --date=short --no-merges --format='%ad %ae %s' 2019-11-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 012d15196023..e33c1582b4bc (6 commits) 2019-11-12 herb@google.com WS: SkStrike Created with: gclient setdep -r src/third_party/skia@d860a78fd60c If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a38659b9529f1..6d72c8d6767e7 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'ad21d47cfa8d9adb2145216c5e514d978d649096', + 'skia_revision': 'd860a78fd60c6c06d5de16863b6ccd8b008565ac', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index bb2e2805449eb..f6c3705fbf08a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: e2fba7540b7c9a199110c06d578872b8 +Signature: 9b4cfb54c01eb26dfc1c57974b36fe04 UNUSED LICENSES: From db23a6cabfebaa9024d111af9cf3a3f743513f11 Mon Sep 17 00:00:00 2001 From: Clement Skau Date: Tue, 12 Nov 2019 17:00:54 +0100 Subject: [PATCH 100/591] Adds missing comma in EngineParagraphStyle.toString() (#13795) --- lib/web_ui/lib/src/engine/text/paragraph.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 98b62127dfe47..70fd3c399a94f 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -419,7 +419,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { 'fontSize: ${_fontSize != null ? _fontSize.toStringAsFixed(1) : "unspecified"}, ' 'height: ${_height != null ? "${_height.toStringAsFixed(1)}x" : "unspecified"}, ' 'ellipsis: ${_ellipsis != null ? "\"$_ellipsis\"" : "unspecified"}, ' - 'locale: ${_locale ?? "unspecified"}' + 'locale: ${_locale ?? "unspecified"}, ' 'shadows: ${_shadows ?? "unspecified"}' ')'; } else { From dee42b4ead106cb2155b3c667ccb96afddc47980 Mon Sep 17 00:00:00 2001 From: Yegor Date: Tue, 12 Nov 2019 10:56:07 -0800 Subject: [PATCH 101/591] implement radial gradient in canvaskit backend (#13796) --- .../lib/src/engine/compositor/util.dart | 22 +++++++++ lib/web_ui/lib/src/engine/shader.dart | 47 +++++++++++-------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index 6575c73692eff..2ed77a6c62339 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -193,6 +193,28 @@ js.JsArray makeSkMatrix(Float64List matrix4) { return skMatrix; } +/// Color stops used when the framework specifies `null`. +final js.JsArray _kDefaultColorStops = () { + final js.JsArray jsColorStops = js.JsArray(); + jsColorStops.length = 2; + jsColorStops[0] = 0; + jsColorStops[1] = 1; + return jsColorStops; +}(); + +/// Converts a list of color stops into a Skia-compatible JS array or color stops. +/// +/// In Flutter `null` means two color stops `[0, 1]` that in Skia must be specified explicitly. +js.JsArray makeSkiaColorStops(List colorStops) { + if (colorStops == null) { + return _kDefaultColorStops; + } + + final js.JsArray jsColorStops = js.JsArray.from(colorStops); + jsColorStops.length = colorStops.length; + return jsColorStops; +} + // These must be kept in sync with `flow/layers/physical_shape_layer.cc`. const double kLightHeight = 600.0; const double kLightRadius = 800.0; diff --git a/lib/web_ui/lib/src/engine/shader.dart b/lib/web_ui/lib/src/engine/shader.dart index cb1f37938be7d..52799bf5e2995 100644 --- a/lib/web_ui/lib/src/engine/shader.dart +++ b/lib/web_ui/lib/src/engine/shader.dart @@ -142,21 +142,11 @@ class GradientLinear extends EngineGradient { jsColors[i] = colors[i].value; } - js.JsArray jsColorStops; - if (colorStops == null) { - jsColorStops = js.JsArray(); - jsColorStops.length = 2; - jsColorStops[0] = 0; - jsColorStops[1] = 1; - } else { - jsColorStops = js.JsArray.from(colorStops); - jsColorStops.length = colorStops.length; - } return canvasKit.callMethod('MakeLinearGradientShader', [ makeSkPoint(from), makeSkPoint(to), jsColors, - jsColorStops, + makeSkiaColorStops(colorStops), tileMode.index, ]); } @@ -178,13 +168,16 @@ class GradientRadial extends EngineGradient { @override Object createPaintStyle(html.CanvasRenderingContext2D ctx) { - if (matrix4 != null && !Matrix4.fromFloat64List(matrix4).isIdentity()) { - throw UnimplementedError( - 'matrix4 not supported in GradientRadial shader'); - } - if (tileMode != ui.TileMode.clamp) { - throw UnimplementedError( - 'TileMode not supported in GradientRadial shader'); + if (!experimentalUseSkia) { + // The DOM backend does not (yet) support all parameters. + if (matrix4 != null && !Matrix4.fromFloat64List(matrix4).isIdentity()) { + throw UnimplementedError( + 'matrix4 not supported in GradientRadial shader'); + } + if (tileMode != ui.TileMode.clamp) { + throw UnimplementedError( + 'TileMode not supported in GradientRadial shader'); + } } final html.CanvasGradient gradient = ctx.createRadialGradient( center.dx, center.dy, 0, center.dx, center.dy, radius); @@ -203,7 +196,23 @@ class GradientRadial extends EngineGradient { @override js.JsObject createSkiaShader() { - throw UnimplementedError(); + assert(experimentalUseSkia); + + final js.JsArray jsColors = js.JsArray(); + jsColors.length = colors.length; + for (int i = 0; i < colors.length; i++) { + jsColors[i] = colors[i].value; + } + + return canvasKit.callMethod('MakeRadialGradientShader', [ + makeSkPoint(center), + radius, + jsColors, + makeSkiaColorStops(colorStops), + tileMode.index, + matrix4 != null ? makeSkMatrix(matrix4) : null, + 0, + ]); } } From a59c96910dbd2449742f2191ae69c96b484f18b0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 12 Nov 2019 12:21:50 -0800 Subject: [PATCH 102/591] Document the coordinate space of points in FlutterPointerEvent. (#13782) --- shell/platform/embedder/embedder.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 1d1b3795292ee..2913e85ec4ed5 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -404,15 +404,21 @@ typedef struct { /// The size of this struct. Must be sizeof(FlutterPointerEvent). size_t struct_size; FlutterPointerPhase phase; - /// @attention The timestamp must be specified in microseconds. + /// The timestamp at which the pointer event was generated. The timestamp + /// should be specified in microseconds and the clock should be the same as + /// that used by `FlutterEngineGetCurrentTime`. size_t timestamp; + /// The x coordinate of the pointer event in physical pixels. double x; + /// The y coordinate of the pointer event in physical pixels. double y; /// An optional device identifier. If this is not specified, it is assumed /// that the embedder has no multi-touch capability. int32_t device; FlutterPointerSignalKind signal_kind; + /// The x offset of the scroll in physical pixels. double scroll_delta_x; + /// The y offset of the scroll in physical pixels. double scroll_delta_y; /// The type of the device generating this event. /// Backwards compatibility note: If this is not set, the device will be From 429e56e974279156acd0241c645847979af90a89 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 12 Nov 2019 14:45:33 -0800 Subject: [PATCH 103/591] Roll src/third_party/dart 7ebc820ffc..5cce1e4acd (1 commits) (#13798) dart-lang/sdk@5cce1e4acd Reland "[vm] Remove obsolete kernel constant expression evaluator." --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 6d72c8d6767e7..c60a4df1c9da6 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '7ebc820ffcc06e08c0647b0a5e0f3bd41a967cf3', + 'dart_revision': '5cce1e4acde9c166877a0c1a142e0cb458964478', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 78704cc26c1bd..07dfd764316ee 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 03d479e89fa6c0abff5498a9980dfd24 +Signature: 0711148b858bd9d47c7b0cc7bcf967e7 UNUSED LICENSES: @@ -8597,8 +8597,8 @@ FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_flow_graph FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.cc FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.h FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_scope_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_reader.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_reader.h FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_fingerprints.cc FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_fingerprints.h FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_translation_helper.cc From 1a8bc658a0406268c4f966799f706ffee9832bbc Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 12 Nov 2019 18:45:58 -0500 Subject: [PATCH 104/591] Roll src/third_party/skia d860a78fd60c..581108137b46 (13 commits) (#13800) https://skia.googlesource.com/skia.git/+log/d860a78fd60c..581108137b46 git log d860a78fd60c..581108137b46 --date=short --no-merges --format='%ad %ae %s' 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-12 csmartdalton@google.com Add 'numRasterSamples' and 'isMixedSampled' to GrProgramInfo 2019-11-12 ccross@android.com [SkQP/Android] Replace -Weverything with -Wextra 2019-11-12 robertphillips@google.com Use a priori knowledge about the number of stencil bits in Dawn, Metal and Vulkan backends 2019-11-12 senorblanco@chromium.org Dawn: implement dynamic primitive processor texture handling. 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 31edef751a8d..9da287fd0264 (6 commits) 2019-11-12 jvanverth@google.com Detect whether trying to blit a Metal swapchain texture, and fail if so. 2019-11-12 senorblanco@chromium.org Dawn: fix bug in stencil handling. 2019-11-12 nigeltao@google.com Free SkWuffsCodec frame-count decoder earlier 2019-11-12 bungeman@google.com Track and force opsz axis on Mac. 2019-11-12 herb@google.com Make metrics const on SkStrike 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@581108137b46 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c60a4df1c9da6..f1ae197d72f81 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd860a78fd60c6c06d5de16863b6ccd8b008565ac', + 'skia_revision': '581108137b46307b521448bf987bb5e5ecf3e5dd', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f6c3705fbf08a..9f7aa2c146b78 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9b4cfb54c01eb26dfc1c57974b36fe04 +Signature: c62c715c37e15e34702315df0805f841 UNUSED LICENSES: From 704123857640cc1b09c5d6d44bfa7ad8f4ab58f4 Mon Sep 17 00:00:00 2001 From: George Wright Date: Tue, 12 Nov 2019 15:46:07 -0800 Subject: [PATCH 105/591] Create a WeakPtrFactory for use on the UI thread in VsyncWaiter (#13781) --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/fuchsia/flutter/BUILD.gn | 9 ++ .../platform/fuchsia/flutter/vsync_waiter.cc | 31 ++++++- shell/platform/fuchsia/flutter/vsync_waiter.h | 4 + .../fuchsia/flutter/vsync_waiter_unittests.cc | 87 +++++++++++++++++++ 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9863294e890eb..a2b25ba2b017e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1002,6 +1002,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_recorder.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_recorder.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_waiter.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_waiter.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 1204b23b60b9f..4b3855bba9955 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -260,15 +260,24 @@ executable("flutter_runner_unittests") { "fuchsia_intl.h", "fuchsia_intl_unittest.cc", "logging.h", + "loop.cc", + "loop.h", "platform_view.cc", "platform_view.h", "platform_view_unittest.cc", "surface.cc", "surface.h", + "task_observers.cc", + "task_observers.h", + "task_runner_adapter.cc", + "task_runner_adapter.h", + "thread.cc", + "thread.h", "vsync_recorder.cc", "vsync_recorder.h", "vsync_waiter.cc", "vsync_waiter.h", + "vsync_waiter_unittests.cc", ] # This is needed for //third_party/googletest for linking zircon symbols. diff --git a/shell/platform/fuchsia/flutter/vsync_waiter.cc b/shell/platform/fuchsia/flutter/vsync_waiter.cc index e8b52db443465..d39a114693233 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter.cc +++ b/shell/platform/fuchsia/flutter/vsync_waiter.cc @@ -5,6 +5,8 @@ #include "vsync_waiter.h" #include +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/trace_event.h" #include "vsync_recorder.h" @@ -17,7 +19,8 @@ VsyncWaiter::VsyncWaiter(std::string debug_label, : flutter::VsyncWaiter(task_runners), debug_label_(std::move(debug_label)), session_wait_(session_present_handle, SessionPresentSignal), - weak_factory_(this) { + weak_factory_(this), + weak_factory_ui_(nullptr) { auto wait_handler = [&](async_dispatcher_t* dispatcher, // async::Wait* wait, // zx_status_t status, // @@ -33,11 +36,29 @@ VsyncWaiter::VsyncWaiter(std::string debug_label, FireCallbackNow(); }; + // Generate a WeakPtrFactory for use with the UI thread. This does not need + // to wait on a latch because we only ever use the WeakPtrFactory on the UI + // thread so we have ordering guarantees (see ::AwaitVSync()) + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), fml::MakeCopyable([this]() mutable { + this->weak_factory_ui_ = + std::make_unique>(this); + })); session_wait_.set_handler(wait_handler); } VsyncWaiter::~VsyncWaiter() { session_wait_.Cancel(); + + fml::AutoResetWaitableEvent ui_latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + fml::MakeCopyable( + [weak_factory_ui = std::move(weak_factory_ui_), &ui_latch]() mutable { + weak_factory_ui.reset(); + ui_latch.Signal(); + })); + ui_latch.Wait(); } static fml::TimePoint SnapToNextPhase(fml::TimePoint value, @@ -57,7 +78,13 @@ void VsyncWaiter::AwaitVSync() { fml::TimePoint next_vsync = SnapToNextPhase(now, vsync_info.presentation_time, vsync_info.presentation_interval); task_runners_.GetUITaskRunner()->PostDelayedTask( - [self = weak_factory_.GetWeakPtr()] { + [& weak_factory_ui = this->weak_factory_ui_] { + if (!weak_factory_ui) { + FML_LOG(WARNING) << "WeakPtrFactory for VsyncWaiter is null, likely " + "due to the VsyncWaiter being destroyed."; + return; + } + auto self = weak_factory_ui->GetWeakPtr(); if (self) { self->FireCallbackWhenSessionAvailable(); } diff --git a/shell/platform/fuchsia/flutter/vsync_waiter.h b/shell/platform/fuchsia/flutter/vsync_waiter.h index 633fbc56c0322..ba1edea09e7d7 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter.h +++ b/shell/platform/fuchsia/flutter/vsync_waiter.h @@ -29,6 +29,10 @@ class VsyncWaiter final : public flutter::VsyncWaiter { async::Wait session_wait_; fml::WeakPtrFactory weak_factory_; + // For accessing the VsyncWaiter via the UI thread, necessary for the callback + // for AwaitVSync() + std::unique_ptr> weak_factory_ui_; + // |flutter::VsyncWaiter| void AwaitVSync() override; diff --git a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc new file mode 100644 index 0000000000000..a6fdee43bb5ca --- /dev/null +++ b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include +#include +#include + +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/common/vsync_waiter.h" +#include "flutter/shell/platform/fuchsia/flutter/task_runner_adapter.h" +#include "flutter/shell/platform/fuchsia/flutter/thread.h" +#include "flutter/shell/platform/fuchsia/flutter/vsync_waiter.h" + +namespace flutter_runner_test { + +class VsyncWaiterTest : public testing::Test { + public: + VsyncWaiterTest() {} + + ~VsyncWaiterTest() = default; + + std::unique_ptr CreateVsyncWaiter( + flutter::TaskRunners task_runners) { + return std::make_unique( + "VsyncWaiterTest", vsync_event_.get(), task_runners); + } + + void SignalVsyncEvent() { + auto status = + zx_object_signal(vsync_event_.get(), 0, + flutter_runner::VsyncWaiter::SessionPresentSignal); + EXPECT_EQ(status, ZX_OK); + } + + protected: + void SetUp() override { + auto status = zx::event::create(0, &vsync_event_); + EXPECT_EQ(status, ZX_OK); + } + + private: + zx::event vsync_event_; +}; + +TEST_F(VsyncWaiterTest, AwaitVsync) { + std::array, 3> threads; + + for (auto& thread : threads) { + thread.reset(new flutter_runner::Thread()); + } + + async::Loop loop(&kAsyncLoopConfigAttachToThread); + + const flutter::TaskRunners task_runners( + "VsyncWaiterTests", // Dart thread labels + flutter_runner::CreateFMLTaskRunner( + async_get_default_dispatcher()), // platform + flutter_runner::CreateFMLTaskRunner(threads[0]->dispatcher()), // gpu + flutter_runner::CreateFMLTaskRunner(threads[1]->dispatcher()), // ui + flutter_runner::CreateFMLTaskRunner(threads[2]->dispatcher()) // io + ); + + auto vsync_waiter = CreateVsyncWaiter(std::move(task_runners)); + + fml::AutoResetWaitableEvent latch; + vsync_waiter->AsyncWaitForVsync( + [&latch](fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time) { latch.Signal(); }); + SignalVsyncEvent(); + + bool did_timeout = + latch.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(5000)); + + // False indicates we were signalled rather than timed out + EXPECT_FALSE(did_timeout); + + vsync_waiter.reset(); + for (const auto& thread : threads) { + thread->Quit(); + } +} + +} // namespace flutter_runner_test From 7b0c11e70710ecd7fa83515d2bc2648959ef98fa Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 12 Nov 2019 18:49:45 -0500 Subject: [PATCH 106/591] Roll fuchsia/sdk/core/mac-amd64 from tZdOC... to p9FbP... (#13801) Roll fuchsia/sdk/core/mac-amd64 from tZdOC... to p9FbP... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f1ae197d72f81..c30ae6dd9b601 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'tZdOCFmhdSOqqWySz07rEzkXjgmqL7rdAI4B0IBJfycC' + 'version': 'p9FbPCkviO4GFUnhU2H7BPgXh9tlSPDEdeuXnkguq30C' } ], 'condition': 'host_os == "mac"', From 2da213615310dfb1cff223b747534fa7462542e3 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 13 Nov 2019 07:22:42 -0800 Subject: [PATCH 107/591] Revert "Roll src/third_party/skia d860a78fd60c..581108137b46 (13 commits) (#13800)" (#13828) This reverts commit 1a8bc658a0406268c4f966799f706ffee9832bbc. --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c30ae6dd9b601..b3ff96296a17e 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '581108137b46307b521448bf987bb5e5ecf3e5dd', + 'skia_revision': 'd860a78fd60c6c06d5de16863b6ccd8b008565ac', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 9f7aa2c146b78..f6c3705fbf08a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c62c715c37e15e34702315df0805f841 +Signature: 9b4cfb54c01eb26dfc1c57974b36fe04 UNUSED LICENSES: From e5b4eabc4a39c631c5ec1c84425ddef5f765458d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 13 Nov 2019 12:04:03 -0500 Subject: [PATCH 108/591] Roll fuchsia/sdk/core/mac-amd64 from p9FbP... to _QV9E... (#13823) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index b3ff96296a17e..87787f8fcc203 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'p9FbPCkviO4GFUnhU2H7BPgXh9tlSPDEdeuXnkguq30C' + 'version': '_QV9Eqxy4Alyg7wqAfzaC8d3FfihSCmv6SwWNlKeIrUC' } ], 'condition': 'host_os == "mac"', From 2877aa470ba129729a4fef52148b61014da890ee Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 13 Nov 2019 09:10:56 -0800 Subject: [PATCH 109/591] Roll src/third_party/dart 5cce1e4acd..d5d889668b (17 commits) (#13806) dart-lang/sdk@d5d889668b [analyzer] Added variance support for upper/lower bounds. dart-lang/sdk@a65aff122d [analyzer] Changed subtype checking for variance modifiers. dart-lang/sdk@a520e771f6 More subtyping tests. dart-lang/sdk@942419e6ba [analysis_server] Funnel sendServerErrorNotification through logException dart-lang/sdk@26cabb72c3 [analyzer] Add [SilentException] where previously not reported to users. dart-lang/sdk@d355778418 [analyzer] rate limit the crash reports we send dart-lang/sdk@0b210e5e81 strict inference should not complain about top-level setter return types dart-lang/sdk@ab7bceb622 Ensure that Member(s) are not created around Member(s). dart-lang/sdk@b3d52df66d Remove undetected unused field in CreateAllSummariesVisitor dart-lang/sdk@158559bc03 Changelog update for extension method prefix change. dart-lang/sdk@2563a1b2ac Add an option to dartfix to specify the SDK used by analysis dart-lang/sdk@667f77ed60 Add an http server for the interactive preview mode dart-lang/sdk@2a9af7d953 [vm] Late modifier for non-final static fields. dart-lang/sdk@3771dddf43 [VM/nnbd] Force the DeclarationType of a class to have legacy nullability. dart-lang/sdk@024a4b2999 Remove unused import dart-lang/sdk@3bde76208d Fix for AstBinaryFlags.hasQuestion dart-lang/sdk@63c67a6768 Ignore zero length regions in the preview tool --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 87787f8fcc203..973652786c77b 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '5cce1e4acde9c166877a0c1a142e0cb458964478', + 'dart_revision': 'd5d889668b1761c659936df98571cd7c53a11e1e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 07dfd764316ee..07186c68d9aed 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 0711148b858bd9d47c7b0cc7bcf967e7 +Signature: 80ec515c59d3950b04c99471b0fdbfe2 UNUSED LICENSES: From 198c2a7ddc2b38d82a0c36ab3cdb445a51355cdf Mon Sep 17 00:00:00 2001 From: "G. Ari Aye" Date: Wed, 13 Nov 2019 09:56:38 -0800 Subject: [PATCH 110/591] Update version of dart/language_model distributed with flutter engine to latest (#13799) Fixes https://github.com/dart-lang/sdk/issues/39352. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 973652786c77b..dc46903b60910 100644 --- a/DEPS +++ b/DEPS @@ -508,7 +508,7 @@ deps = { 'packages': [ { 'package': 'dart/language_model', - 'version': '9fJQZ0TrnAGQKrEtuL3-AXbUfPzYxqpN_OBHr9P4hE4C', + 'version': 'lIRt14qoA1Cocb8j3yw_Fx5cfYou2ddam6ArBm4AI6QC', } ], 'dep_type': 'cipd', From a5680f9388ebcef6012c446c5226a79a5ab8000c Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 13 Nov 2019 10:02:09 -0800 Subject: [PATCH 111/591] [dart_runner] Initialize logging and tracing (#13829) Remove !defined(FUCHSIA_SDK) bits to start using the SDK apis to initialize logging and trace events for dart runners. --- shell/platform/fuchsia/dart_runner/main.cc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/shell/platform/fuchsia/dart_runner/main.cc b/shell/platform/fuchsia/dart_runner/main.cc index 98bd64eb0e5e7..357ac33324c5d 100644 --- a/shell/platform/fuchsia/dart_runner/main.cc +++ b/shell/platform/fuchsia/dart_runner/main.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -15,10 +16,6 @@ #include "runtime/dart/utils/tempfs.h" #include "third_party/dart/runtime/include/dart_api.h" -#if !defined(FUCHSIA_SDK) -#include -#endif // !defined(FUCHSIA_SDK) - #if !defined(DART_PRODUCT) // Register native symbol information for the Dart VM's profiler. static void RegisterProfilerSymbols(const char* symbols_path, @@ -34,20 +31,17 @@ static void RegisterProfilerSymbols(const char* symbols_path, #endif // !defined(DART_PRODUCT) int main(int argc, const char** argv) { + fx_log_init(); async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); -#if !defined(FUCHSIA_SDK) - syslog::InitLogger(); - std::unique_ptr provider; { - TRACE_EVENT0("dart", "CreateTraceProvider"); + TRACE_DURATION("dart", "CreateTraceProvider"); bool already_started; // Use CreateSynchronously to prevent loss of early events. trace::TraceProviderWithFdio::CreateSynchronously( loop.dispatcher(), "dart_runner", &provider, &already_started); } -#endif // !defined(FUCHSIA_SDK) #if !defined(DART_PRODUCT) #if defined(AOT_RUNTIME) From 6936b489b31c2446f59353f6f54482916cbc3d72 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 13 Nov 2019 09:57:11 -0800 Subject: [PATCH 112/591] Roll src/third_party/dart d5d889668b..ffbd2eb7ee (15 commits) dart-lang/sdk@ffbd2eb7ee Address comments from an earlier CL dart-lang/sdk@7c71115b28 Remove unused type parameter from VariableMember. dart-lang/sdk@cd22f16f0b Fix crash when building //utils/dartdevc:dartdevc_kernel_sdk. dart-lang/sdk@f443bc918e [cfe] Add tests for inheritance from opt-out libraries dart-lang/sdk@6ed0fcaa12 [analysis_server] Remove last dependency on front_end+kernel dart-lang/sdk@2b94bd678d [CFE] Fix renames from test to suite and move of parser dart-lang/sdk@d4f1512e00 [cfe] Support forEffect and readOnlyReceiver in IndexSet dart-lang/sdk@082c10aab7 Revert "[VM/nnbd] Pass nullability when creating Class::DeclarationType." dart-lang/sdk@b4779e8f72 Revert "[VM/nnbd] Make Nullability and NNBDMode class enums to avoid name conflicts." dart-lang/sdk@82aa7e79af Revert "[VM/nnbd] Force the DeclarationType of a class to have legacy nullability." dart-lang/sdk@a7472ba4c6 Cache value of "non-nullable" flag so it can be used in the VM code without having to search through the array of experimental flags array. dart-lang/sdk@8938a5ed2d Migration: make an edge origin for assignments of dynamic. dart-lang/sdk@c9ef7cc5e3 Deprecate Member.baseElement, use Element.declaration in analyzer. dart-lang/sdk@3793a40e6a Migration: stop using `always` for decorating the type of explicit `null`s. dart-lang/sdk@12265c4e42 Hacky prototype of how to fix the seventh item from #39247 --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index dc46903b60910..a7d95a5ef49f1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd5d889668b1761c659936df98571cd7c53a11e1e', + 'dart_revision': 'ffbd2eb7eebf569b8aefd970cf59851aaf2869a3', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 07186c68d9aed..b4e931d68373c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 80ec515c59d3950b04c99471b0fdbfe2 +Signature: a239f867d1db8832be8d121f7ff5903a UNUSED LICENSES: From b358dc58fbcec7b235a40f550445c8d6877342e1 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 13 Nov 2019 11:13:46 -0800 Subject: [PATCH 113/591] Remove extra shadows from Web Engine EngineParagraphStyle (#13805) --- lib/web_ui/lib/src/engine/text/paragraph.dart | 22 +++---------------- lib/web_ui/lib/src/engine/text/ruler.dart | 14 +----------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 70fd3c399a94f..6117d2a5f6aa7 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -18,7 +18,6 @@ class EngineParagraph implements ui.Paragraph { @required ui.TextAlign textAlign, @required ui.TextDirection textDirection, @required ui.Paint background, - @required List shadows, }) : assert((plainText == null && paint == null) || (plainText != null && paint != null)), _paragraphElement = paragraphElement, @@ -27,8 +26,7 @@ class EngineParagraph implements ui.Paragraph { _textAlign = textAlign, _textDirection = textDirection, _paint = paint, - _background = background, - _shadows = shadows; + _background = background; final html.HtmlElement _paragraphElement; final ParagraphGeometricStyle _geometricStyle; @@ -37,7 +35,6 @@ class EngineParagraph implements ui.Paragraph { final ui.TextAlign _textAlign; final ui.TextDirection _textDirection; final ui.Paint _background; - final List _shadows; @visibleForTesting String get plainText => _plainText; @@ -325,7 +322,6 @@ class EngineParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle strutStyle, String ellipsis, ui.Locale locale, - List shadows, }) : _textAlign = textAlign, _textDirection = textDirection, _fontWeight = fontWeight, @@ -337,8 +333,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { // TODO(b/128317744): add support for strut style. _strutStyle = strutStyle, _ellipsis = ellipsis, - _locale = locale, - _shadows = shadows; + _locale = locale; final ui.TextAlign _textAlign; final ui.TextDirection _textDirection; @@ -351,7 +346,6 @@ class EngineParagraphStyle implements ui.ParagraphStyle { final EngineStrutStyle _strutStyle; final String _ellipsis; final ui.Locale _locale; - final List _shadows; String get _effectiveFontFamily { if (assertionsEnabled) { @@ -419,8 +413,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { 'fontSize: ${_fontSize != null ? _fontSize.toStringAsFixed(1) : "unspecified"}, ' 'height: ${_height != null ? "${_height.toStringAsFixed(1)}x" : "unspecified"}, ' 'ellipsis: ${_ellipsis != null ? "\"$_ellipsis\"" : "unspecified"}, ' - 'locale: ${_locale ?? "unspecified"}, ' - 'shadows: ${_shadows ?? "unspecified"}' + 'locale: ${_locale ?? "unspecified"}' ')'; } else { return super.toString(); @@ -913,7 +906,6 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { wordSpacing: wordSpacing, decoration: _textDecorationToCssString(decoration, decorationStyle), ellipsis: _paragraphStyle._ellipsis, - shadows: shadows, ), plainText: '', paint: paint, @@ -967,7 +959,6 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { wordSpacing: wordSpacing, decoration: _textDecorationToCssString(decoration, decorationStyle), ellipsis: _paragraphStyle._ellipsis, - shadows: shadows, ), plainText: plainText, paint: paint, @@ -1011,7 +1002,6 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { lineHeight: _paragraphStyle._height, maxLines: _paragraphStyle._maxLines, ellipsis: _paragraphStyle._ellipsis, - shadows: _paragraphStyle._shadows, ), plainText: null, paint: null, @@ -1098,9 +1088,6 @@ void _applyParagraphStyleToElement({ if (style._effectiveFontFamily != null) { cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily); } - if (style._shadows != null) { - cssStyle.textShadow = _shadowListToCss(style._shadows); - } } else { if (style._textAlign != previousStyle._textAlign) { cssStyle.textAlign = textAlignToCssValue( @@ -1127,9 +1114,6 @@ void _applyParagraphStyleToElement({ if (style._fontFamily != previousStyle._fontFamily) { cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); } - if (style._shadows != previousStyle._shadows) { - cssStyle.textShadow = _shadowListToCss(style._shadows); - } } } diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index 58d180e593848..8c17b3d2c10e3 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -17,7 +17,6 @@ class ParagraphGeometricStyle { this.wordSpacing, this.decoration, this.ellipsis, - this.shadows, }); final ui.FontWeight fontWeight; @@ -30,7 +29,6 @@ class ParagraphGeometricStyle { final double wordSpacing; final String decoration; final String ellipsis; - final List shadows; // Since all fields above are primitives, cache hashcode since ruler lookups // use this style as key. @@ -111,8 +109,7 @@ class ParagraphGeometricStyle { letterSpacing == typedOther.letterSpacing && wordSpacing == typedOther.wordSpacing && decoration == typedOther.decoration && - ellipsis == typedOther.ellipsis && - shadows == typedOther.shadows; + ellipsis == typedOther.ellipsis; } @override @@ -127,12 +124,8 @@ class ParagraphGeometricStyle { wordSpacing, decoration, ellipsis, - _hashShadows(shadows), ); - int _hashShadows(List shadows) => - (shadows == null ? '' : _shadowListToCss(shadows)).hashCode; - @override String toString() { if (assertionsEnabled) { @@ -144,7 +137,6 @@ class ParagraphGeometricStyle { ' wordSpacing: $wordSpacing,' ' decoration: $decoration,' ' ellipsis: $ellipsis,' - ' shadows: $shadows,' ')'; } else { return super.toString(); @@ -249,10 +241,6 @@ class TextDimensions { if (style.lineHeight != null) { _element.style.lineHeight = style.lineHeight.toString(); } - final List shadowList = style.shadows; - if (shadowList != null) { - _element.style.textShadow = _shadowListToCss(shadowList); - } _invalidateBoundsCache(); } From 1f1e2ba58e6f62ab9c3808fada9769a3f23a3a77 Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Wed, 13 Nov 2019 11:26:25 -0800 Subject: [PATCH 114/591] reland add lifecycle enum (#13767) This reverts commit 8ebb318401344793daa10c3bec97c34891cf7cc8. --- lib/ui/window.dart | 16 ++--- lib/web_ui/lib/src/ui/window.dart | 13 ++-- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- .../FlutterActivityAndFragmentDelegate.java | 2 + .../systemchannels/LifecycleChannel.java | 4 ++ ...lutterActivityAndFragmentDelegateTest.java | 11 ++++ .../ios/framework/Source/FlutterEngine.mm | 24 ++++--- .../ScenariosTests/AppLifecycleTests.m | 66 +++++++++++++++++++ 9 files changed, 111 insertions(+), 29 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 794b9364f625e..dfad860b365bd 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -176,18 +176,16 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - /// - /// Android apps in this state should assume that they may enter the - /// [suspending] state at any time. paused, - /// The application will be suspended momentarily. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// The application is still hosted on a flutter engine but is detached from + /// any host views. /// - /// On iOS, this state is currently unused. - suspending, + /// When the application is in this state, the engine is running without + /// a view. It can either be in the progress of attaching a view when engine + /// was first initializes, or after the view being destroyed due to a Navigator + /// pop. + detached, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 58c82410de7c6..731874e6dff97 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -73,18 +73,13 @@ enum AppLifecycleState { /// /// When the application is in this state, the engine will not call the /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - /// - /// Android apps in this state should assume that they may enter the - /// [suspending] state at any time. paused, - /// The application will be suspended momentarily. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// The application is detached from view. /// - /// On iOS, this state is currently unused. - suspending, + /// When the application is in this state, the engine is running without + /// a platform UI. + detached, } /// A representation of distances for each of the four edges of a rectangle, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index c0ee45d762e81..5ade4672cbece 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -120,7 +120,7 @@ class RuntimeController final : public WindowClient { std::string variant_code; std::vector locale_data; std::string user_settings_data = "{}"; - std::string lifecycle_state; + std::string lifecycle_state = "AppLifecycleState.detached"; bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 38b034d52d0d0..77b6577271511 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -311,7 +311,7 @@ bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { const auto& data = message->data(); std::string state(reinterpret_cast(data.data()), data.size()); if (state == "AppLifecycleState.paused" || - state == "AppLifecycleState.suspending") { + state == "AppLifecycleState.detached") { activity_running_ = false; StopAnimator(); } else if (state == "AppLifecycleState.resumed" || diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index eddacf2b25531..6cebfcbef6232 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -461,6 +461,8 @@ void onDetach() { platformPlugin = null; } + flutterEngine.getLifecycleChannel().appIsDetached(); + // Destroy our FlutterEngine if we're not set to retain it. if (host.shouldDestroyEngineWithHost()) { flutterEngine.destroy(); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java index abc6323907d0b..cd244ff8251e1 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java @@ -39,4 +39,8 @@ public void appIsPaused() { channel.send("AppLifecycleState.paused"); } + public void appIsDetached() { + Log.v(TAG, "Sending AppLifecycleState.detached message."); + channel.send("AppLifecycleState.detached"); + } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 154c59f173191..d335fa5064dbc 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -85,18 +85,21 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter. delegate.onResume(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is paused, an inactive message should have been sent to Flutter. delegate.onPause(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); // When the Activity/Fragment is stopped, a paused message should have been sent to Flutter. // Notice that Flutter uses the term "paused" in a different way, and at a different time @@ -105,6 +108,14 @@ public void itSendsLifecycleEventsToFlutter() { verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached(); + + // When activity detaches, a detached message should have been sent to Flutter. + delegate.onDetach(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused(); + verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsDetached(); } @Test diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 83551d2aed042..a9f20708f15b5 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -175,18 +175,23 @@ - (void)ensureSemanticsEnabled { - (void)setViewController:(FlutterViewController*)viewController { FML_DCHECK(self.iosPlatformView); - _viewController = [viewController getWeakPtr]; + _viewController = + viewController ? [viewController getWeakPtr] : fml::WeakPtr(); self.iosPlatformView->SetOwnerViewController(_viewController); [self maybeSetupPlatformViewChannels]; - __block FlutterEngine* blockSelf = self; - self.flutterViewControllerWillDeallocObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc - object:viewController - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification* note) { - [blockSelf notifyViewControllerDeallocated]; - }]; + if (viewController) { + __block FlutterEngine* blockSelf = self; + self.flutterViewControllerWillDeallocObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc + object:viewController + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* note) { + [blockSelf notifyViewControllerDeallocated]; + }]; + } else { + self.flutterViewControllerWillDeallocObserver = nil; + } } - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { @@ -201,6 +206,7 @@ - (void)setFlutterViewControllerWillDeallocObserver:(id)observer { } - (void)notifyViewControllerDeallocated { + [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"]; if (!_allowHeadlessExecution) { [self destroyContext]; } else { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m index 3646e3fd5ee40..7f8f6902bde2a 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m @@ -225,4 +225,70 @@ - (void)testVisibleFlutterViewControllerRespondsToApplicationLifecycle { [engine setViewController:nil]; } +- (void)testFlutterViewControllerDetachingSendsApplicationLifecycle { + XCTestExpectation* engineStartedExpectation = [self expectationWithDescription:@"Engine started"]; + + // Let the engine finish booting (at the end of which the channels are properly set-up) before + // moving onto the next step of showing the next view controller. + ScreenBeforeFlutter* rootVC = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:^void() { + [engineStartedExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:nil]; + + UIApplication* application = UIApplication.sharedApplication; + application.delegate.window.rootViewController = rootVC; + FlutterEngine* engine = rootVC.engine; + + NSMutableArray* lifecycleExpectations = [NSMutableArray arrayWithCapacity:10]; + + // Expected sequence from showing the FlutterViewController is inactive and resumed. + [lifecycleExpectations addObjectsFromArray:@[ + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" + forStep:@"showing a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed" + forStep:@"showing a FlutterViewController"] + ]]; + // At the end of Flutter VC, we want to make sure it deallocs and sends detached signal. + // Using autoreleasepool will guarantee that. + FlutterViewController* flutterVC; + @autoreleasepool { + flutterVC = [rootVC showFlutter]; + [engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) { + if (lifecycleExpectations.count == 0) { + XCTFail(@"Unexpected lifecycle transition: %@", message); + return; + } + XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0]; + if (![[nextExpectation expectedLifecycle] isEqualToString:message]) { + XCTFail(@"Expected lifecycle %@ but instead received %@", + [nextExpectation expectedLifecycle], message); + return; + } + + [nextExpectation fulfill]; + [lifecycleExpectations removeObjectAtIndex:0]; + }]; + + [self waitForExpectations:lifecycleExpectations timeout:5]; + + // Starts dealloc flutter VC. + [lifecycleExpectations addObjectsFromArray:@[ + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive" + forStep:@"detaching a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.paused" + forStep:@"detaching a FlutterViewController"], + [[XCAppLifecycleTestExpectation alloc] + initForLifecycle:@"AppLifecycleState.detached" + forStep:@"detaching a FlutterViewController"] + ]]; + [flutterVC dismissViewControllerAnimated:NO completion:nil]; + flutterVC = nil; + } + [self waitForExpectations:lifecycleExpectations timeout:5]; + + [engine.lifecycleChannel setMessageHandler:nil]; + [engine setViewController:nil]; +} + @end From f4cad544a05ff97d3a330a35dc13522c59f4f10c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 13 Nov 2019 12:00:19 -0800 Subject: [PATCH 115/591] Roll buildroot to 0fec442d067a0998352ea12706fcae0a53b62884. (#13837) Pulls in Skia GN flag addition of skia_using_fuchsia_sdk. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a7d95a5ef49f1..094a910f29890 100644 --- a/DEPS +++ b/DEPS @@ -137,7 +137,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a518e359c41e00f964f7cc079cbc5ef525f82516', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '0fec442d067a0998352ea12706fcae0a53b62884', # Fuchsia compatibility # From b6a0fb77edae1d7c48b0b55ba78327c245ed7a7a Mon Sep 17 00:00:00 2001 From: Ferhat Date: Wed, 13 Nov 2019 13:09:16 -0800 Subject: [PATCH 116/591] [web] Fix blendmode for images (#13809) * Fix blendmode for images --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 6 +- .../engine/canvas_blend_golden_test.dart | 136 ++++++++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index bbd03eb46112a..3a6e72bb95980 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 75729099b6f4d4e78ceb110b9305fc9905b91519 +revision: ab1f2da642d6c5188b312965759ce7157fd073b9 diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index b9daedc91f644..c7c223b38e8eb 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -579,7 +579,9 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { void drawImage(ui.Image image, ui.Offset p, ui.PaintData paint) { _applyPaint(paint); final HtmlImage htmlImage = image; - final html.Element imgElement = htmlImage.cloneImageElement(); + final html.ImageElement imgElement = htmlImage.cloneImageElement(); + String blendMode = ctx.globalCompositeOperation; + imgElement.style.mixBlendMode = blendMode; _drawImage(imgElement, p); _childOverdraw = true; } @@ -618,6 +620,8 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { } else { _applyPaint(paint); final html.Element imgElement = htmlImage.cloneImageElement(); + final ui.BlendMode blendMode = paint.blendMode; + imgElement.style.mixBlendMode = _stringForBlendMode(blendMode); if (requiresClipping) { save(); clipRect(dst); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart new file mode 100644 index 0000000000000..b4074d08233f6 --- /dev/null +++ b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart @@ -0,0 +1,136 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:js_util' as js_util; + +import 'package:ui/ui.dart' hide TextStyle; +import 'package:ui/src/engine.dart'; +import 'package:test/test.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + final Paint testPaint = Paint()..color = const Color(0xFFFF0000); + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(RecordingCanvas rc, String fileName, + {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { + final EngineCanvas engineCanvas = BitmapCanvas(screenRect); + + rc.apply(engineCanvas); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body.append(sceneElement); + await matchGoldenFile('$fileName.png', region: region, maxDiffRate: 0.03); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Blend circles with difference and color', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.drawRect( + Rect.fromLTRB(0, 0, 400, 400), + Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(255, 255, 255, 255)); + rc.drawCircle( + Offset(100, 100), + 80.0, + Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0) + ..blendMode = BlendMode.difference); + + rc.drawCircle( + Offset(170, 100), + 80.0, + Paint() + ..style = PaintingStyle.fill + ..blendMode = BlendMode.color + ..color = const Color.fromARGB(128, 0, 255, 0)); + + rc.drawCircle( + Offset(135, 170), + 80.0, + Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0)); + await _checkScreenshot(rc, 'canvas_blend_circle_diff_color'); + }); + + test('Blend circle and text with multiply', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.drawRect( + Rect.fromLTRB(0, 0, 400, 400), + Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(255, 255, 255, 255)); + rc.drawCircle( + Offset(100, 100), + 80.0, + Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0) + ..blendMode = BlendMode.difference); + rc.drawCircle( + Offset(170, 100), + 80.0, + Paint() + ..style = PaintingStyle.fill + ..blendMode = BlendMode.color + ..color = const Color.fromARGB(128, 0, 255, 0)); + + rc.drawCircle( + Offset(135, 170), + 80.0, + Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0)); + rc.drawImage(createTestImage(), Offset(135.0, 130.0), + Paint()..blendMode = BlendMode.multiply); + await _checkScreenshot(rc, 'canvas_blend_image_multiply'); + }); +} + +HtmlImage createTestImage() { + const int width = 100; + const int height = 50; + html.CanvasElement canvas = + new html.CanvasElement(width: width, height: height); + html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(33, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(66, 0, 33, 50); + ctx.fill(); + html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); + return HtmlImage(imageElement, width, height); +} From 09c77fc962e2f9f99b2e09ded74212165ba73002 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 13 Nov 2019 12:43:54 -0800 Subject: [PATCH 117/591] Roll src/third_party/dart ffbd2eb7ee..a0bb025024 (1 commits) dart-lang/sdk@a0bb025024 [vm] Late static final fields --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 094a910f29890..f243253b269cd 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'ffbd2eb7eebf569b8aefd970cf59851aaf2869a3', + 'dart_revision': 'a0bb025024eda22e76ea5a08003bcf2832eba0cb', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b4e931d68373c..b3d410308a311 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: a239f867d1db8832be8d121f7ff5903a +Signature: ebc87b511d37dd5a3b846dc7982af47b UNUSED LICENSES: From e1b533483e3616e4a9b6df432a155c72062e8901 Mon Sep 17 00:00:00 2001 From: xster Date: Wed, 13 Nov 2019 14:10:38 -0800 Subject: [PATCH 118/591] add recent packages to javadoc list (#13789) --- tools/gen_javadoc.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/gen_javadoc.py b/tools/gen_javadoc.py index 0e5da15bc7a23..14c16203869e1 100755 --- a/tools/gen_javadoc.py +++ b/tools/gen_javadoc.py @@ -46,6 +46,14 @@ def main(): 'io.flutter.embedding.android', 'io.flutter.embedding.engine', 'io.flutter.embedding.engine.dart', + 'io.flutter.embedding.engine.loader', + 'io.flutter.embedding.engine.plugins', + 'io.flutter.embedding.engine.plugins.activity', + 'io.flutter.embedding.engine.plugins.broadcastreceiver', + 'io.flutter.embedding.engine.plugins.contentprovider', + 'io.flutter.embedding.engine.plugins.lifecycle', + 'io.flutter.embedding.engine.plugins.service', + 'io.flutter.embedding.engine.plugins.shim', 'io.flutter.embedding.engine.renderer', 'io.flutter.embedding.engine.systemchannels', 'io.flutter.plugin.common', From 9a3042cb50a7cc2a354e2ffe3c3ef26a2f79893a Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Wed, 13 Nov 2019 14:12:46 -0800 Subject: [PATCH 119/591] Remove unused import (#13832) --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index e37733515c7ec..15cc1200cf9a0 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -8,7 +8,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; From 4bc156d9dd98eff53b80e600123fd46b6586d01d Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 13 Nov 2019 14:33:21 -0800 Subject: [PATCH 120/591] Disable LTO on Fuchsia (#13842) --- tools/fuchsia/build_fuchsia_artifacts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/fuchsia/build_fuchsia_artifacts.py b/tools/fuchsia/build_fuchsia_artifacts.py index 86a4ea891ee75..bf033435085fa 100755 --- a/tools/fuchsia/build_fuchsia_artifacts.py +++ b/tools/fuchsia/build_fuchsia_artifacts.py @@ -207,8 +207,10 @@ def BuildTarget(runtime_mode, arch, product, enable_lto): runtime_mode, ] - if not enable_lto: - flags.append('--no-lto') + # Always disable lto until https://github.com/flutter/flutter/issues/44841 + # gets fixed. + # if not enable_lto: + flags.append('--no-lto') RunGN(out_dir, flags) BuildNinjaTargets(out_dir, GetTargetsToBuild(product)) From 98379d7b6118931d10ddfbce230051764ca4041b Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 13 Nov 2019 14:34:22 -0800 Subject: [PATCH 121/591] [build] Make --engine-version flag optional (#13803) --- tools/fuchsia/build_fuchsia_artifacts.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/fuchsia/build_fuchsia_artifacts.py b/tools/fuchsia/build_fuchsia_artifacts.py index bf033435085fa..ad1ae14ca48c2 100755 --- a/tools/fuchsia/build_fuchsia_artifacts.py +++ b/tools/fuchsia/build_fuchsia_artifacts.py @@ -229,7 +229,7 @@ def main(): parser.add_argument( '--engine-version', - required=True, + required=False, help='Specifies the flutter engine SHA.') parser.add_argument( @@ -272,8 +272,13 @@ def main(): BuildTarget(runtime_mode, arch, product, enable_lto) BuildBucket(runtime_mode, arch, product) - ProcessCIPDPakcage(args.upload, args.engine_version) + if args.upload: + if args.engine_version is None: + print('--upload requires --engine-version to be specified.') + return 1 + ProcessCIPDPakcage(args.upload, args.engine_version) + return 0 if __name__ == '__main__': - main() + sys.exit(main()) From bb7d76248cd845da605414aef7770ddbc4ef8d9b Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 13 Nov 2019 16:41:03 -0800 Subject: [PATCH 122/591] Avoid GL calls when compiling for Fuchsia. (#13847) * Avoid GL calls when compiling for Fuchsia. --- shell/common/shell_io_manager.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/common/shell_io_manager.cc b/shell/common/shell_io_manager.cc index 2b3a9c00582c4..905c8108cad20 100644 --- a/shell/common/shell_io_manager.cc +++ b/shell/common/shell_io_manager.cc @@ -4,6 +4,7 @@ #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/fml/build_config.h" #include "flutter/fml/message_loop.h" #include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -38,12 +39,14 @@ sk_sp ShellIOManager::CreateCompatibleResourceLoadingContext( // ES2 shading language when the ES3 external image extension is missing. options.fPreferExternalImagesOverES3 = true; +#if !OS_FUCHSIA if (auto context = GrContext::MakeGL(gl_interface, options)) { // Do not cache textures created by the image decoder. These textures // should be deleted when they are no longer referenced by an SkImage. context->setResourceCacheLimits(0, 0); return context; } +#endif return nullptr; } From f5c002254b59c22fbe651f9746f8249721361190 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 13 Nov 2019 17:44:25 -0800 Subject: [PATCH 123/591] Use Skia's matchStyleCSS3 to find bundled asset typefaces matching a font style (#13848) This will improve font matching in SkParagraph for fonts that are bundled as assets within the app. Libtxt was using Minikin's FontFamily class to select the closest matching font, but SkParagraph will rely on the matchStyle implementation in the asset font manager. --- lib/ui/text/asset_manager_font_provider.cc | 29 +++++++++++----------- lib/ui/text/asset_manager_font_provider.h | 4 ++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/ui/text/asset_manager_font_provider.cc b/lib/ui/text/asset_manager_font_provider.cc index fd019f23cc3f2..07a4a66f2f2f1 100644 --- a/lib/ui/text/asset_manager_font_provider.cc +++ b/lib/ui/text/asset_manager_font_provider.cc @@ -56,7 +56,8 @@ void AssetManagerFontProvider::RegisterAsset(std::string family_name, if (family_it == registered_families_.end()) { family_names_.push_back(family_name); auto value = std::make_pair( - canonical_name, sk_make_sp(asset_manager_)); + canonical_name, + sk_make_sp(asset_manager_, family_name)); family_it = registered_families_.emplace(value).first; } @@ -64,8 +65,9 @@ void AssetManagerFontProvider::RegisterAsset(std::string family_name, } AssetManagerFontStyleSet::AssetManagerFontStyleSet( - std::shared_ptr asset_manager) - : asset_manager_(asset_manager) {} + std::shared_ptr asset_manager, + std::string family_name) + : asset_manager_(asset_manager), family_name_(family_name) {} AssetManagerFontStyleSet::~AssetManagerFontStyleSet() = default; @@ -78,9 +80,15 @@ int AssetManagerFontStyleSet::count() { } void AssetManagerFontStyleSet::getStyle(int index, - SkFontStyle*, - SkString* style) { - FML_DCHECK(false); + SkFontStyle* style, + SkString* name) { + FML_DCHECK(index < static_cast(assets_.size())); + if (style) { + *style = assets_[index].typeface->fontStyle(); + } + if (name) { + *name = family_name_.c_str(); + } } SkTypeface* AssetManagerFontStyleSet::createTypeface(int i) { @@ -112,14 +120,7 @@ SkTypeface* AssetManagerFontStyleSet::createTypeface(int i) { } SkTypeface* AssetManagerFontStyleSet::matchStyle(const SkFontStyle& pattern) { - if (assets_.empty()) - return nullptr; - - for (const TypefaceAsset& asset : assets_) - if (asset.typeface && asset.typeface->fontStyle() == pattern) - return SkRef(asset.typeface.get()); - - return SkRef(assets_[0].typeface.get()); + return matchStyleCSS3(pattern); } AssetManagerFontStyleSet::TypefaceAsset::TypefaceAsset(std::string a) diff --git a/lib/ui/text/asset_manager_font_provider.h b/lib/ui/text/asset_manager_font_provider.h index d24e5acb078c3..ef4d272664203 100644 --- a/lib/ui/text/asset_manager_font_provider.h +++ b/lib/ui/text/asset_manager_font_provider.h @@ -20,7 +20,8 @@ namespace flutter { class AssetManagerFontStyleSet : public SkFontStyleSet { public: - AssetManagerFontStyleSet(std::shared_ptr asset_manager); + AssetManagerFontStyleSet(std::shared_ptr asset_manager, + std::string family_name); ~AssetManagerFontStyleSet() override; @@ -40,6 +41,7 @@ class AssetManagerFontStyleSet : public SkFontStyleSet { private: std::shared_ptr asset_manager_; + std::string family_name_; struct TypefaceAsset { TypefaceAsset(std::string a); From 7c2a4ec97f9f92f96df0115d2ae92d854e16d9c3 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 13 Nov 2019 18:04:23 -0800 Subject: [PATCH 124/591] Roll src/third_party/dart a0bb025024..e5655d5cfe (19 commits) dart-lang/sdk@e5655d5cfe [dartfuzz] Make generic types explicit when using API table dart-lang/sdk@06aef10d76 nnbd preview: Remove descriptionPrefix; don't link Details which will link to some bizarre Never node dart-lang/sdk@9cd6c01bfd [vm, gc] Add a release barrier to prevent header initialization during promotion from being ordered after publishing stores of that object. dart-lang/sdk@c1c4470da6 [gardening] Fix pkg/vm/test/modular_kernel_plus_aot_test on Windows. dart-lang/sdk@6f11d998d0 [dart2js] Allocate minified type$ names in more stable manner dart-lang/sdk@de498e9655 Add the generation date as a footer to the preview page dart-lang/sdk@2928b6179c Test interfaces of opt-in and opt-out mixes. dart-lang/sdk@22b2a35b9f Add failing test for issue #39376 dart-lang/sdk@5767cc1bfd Migration: do not use "always" node for dynamic dispatches in EdgeBuilder. dart-lang/sdk@8452d2d188 Migration: change behavior of `dynamic`. dart-lang/sdk@30690b1526 Issue 39357. Catch FormatError and report FLUTTER_SET_WIDGET_PROPERTY_VALUE_INVALID_EXPRESSION. dart-lang/sdk@a8e48405dd Share search methods in FindElement and ImportFindElement. dart-lang/sdk@26f160b9c4 nnbd preview: Fix positioning of #content HTML, allowing for footer dart-lang/sdk@64329c879d [vm, docs] Describe how to run size analysis for Flutter apps. dart-lang/sdk@bc4ced0738 Fix the list of migrated files to not include non-migrated files dart-lang/sdk@7a0d9455e4 [vm/aot] Ensure interface targets are re-resolved after deduping of mixin application classes dart-lang/sdk@858265d0b4 NNBD preview: Link to spans rather than anchors dart-lang/sdk@df2093362d [analyzer] Clean up casting for variance and related tests. dart-lang/sdk@39194779c1 nnbd preview tool: Add better text for dynamic values --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f243253b269cd..3710b65f3fd64 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'a0bb025024eda22e76ea5a08003bcf2832eba0cb', + 'dart_revision': 'e5655d5cfee0779c3437b88e2d1f11ceb5e879a5', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b3d410308a311..af6aa595c7c2d 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: ebc87b511d37dd5a3b846dc7982af47b +Signature: 8a91173daad136956c31ab8dc2426c23 UNUSED LICENSES: From 7d6e376d83ccd3b5d160542d9dbf6552a7cf376e Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 13 Nov 2019 19:20:41 -0800 Subject: [PATCH 125/591] Roll src/third_party/dart e5655d5cfe..dc35290111 (2 commits) dart-lang/sdk@dc35290111 [VM/nnbd] Reland of 3 reverted CLs and fix for canonicalization of legacy types. dart-lang/sdk@9223b9d844 [vm, gc] Change CollectAllGarbage to ensure there is no floating garbage caused by incremental marking. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3710b65f3fd64..528901f290ca1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'e5655d5cfee0779c3437b88e2d1f11ceb5e879a5', + 'dart_revision': 'dc35290111c03b29f309029f499f08fbd9e667e4', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index af6aa595c7c2d..f882ad6f08cc7 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 8a91173daad136956c31ab8dc2426c23 +Signature: ca2f5a478a350df271bc591b2a64d1e6 UNUSED LICENSES: From 687a1a75418e2b7ab734cb242bbf196f777e10d3 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 14 Nov 2019 04:09:08 +0000 Subject: [PATCH 126/591] Add support for --dart-flags in FlutterShellArgs. (#44855) (#13855) --- shell/platform/android/BUILD.gn | 1 + .../embedding/engine/FlutterShellArgs.java | 10 +++++++ .../test/io/flutter/FlutterTestSuite.java | 2 ++ .../engine/FlutterShellArgsTest.java | 30 +++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index ecd1d7ff81020..b33bd15d1241a 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -422,6 +422,7 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/FlutterEngineCacheTest.java", "test/io/flutter/embedding/engine/FlutterEngineTest.java", "test/io/flutter/embedding/engine/FlutterJNITest.java", + "test/io/flutter/embedding/engine/FlutterShellArgsTest.java", "test/io/flutter/embedding/engine/PluginComponentTest.java", "test/io/flutter/embedding/engine/RenderingComponentTest.java", "test/io/flutter/embedding/engine/dart/DartExecutorTest.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java index 65c25d5a9f5f9..08872115533f7 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java @@ -44,6 +44,8 @@ public class FlutterShellArgs { public static final String ARG_VERBOSE_LOGGING = "--verbose-logging"; public static final String ARG_KEY_OBSERVATORY_PORT = "observatory-port"; public static final String ARG_OBSERVATORY_PORT = "--observatory-port="; + public static final String ARG_KEY_DART_FLAGS = "dart-flags"; + public static final String ARG_DART_FLAGS = "--dart-flags"; @NonNull public static FlutterShellArgs fromIntent(@NonNull Intent intent) { @@ -91,6 +93,14 @@ public static FlutterShellArgs fromIntent(@NonNull Intent intent) { args.add(ARG_VERBOSE_LOGGING); } + // NOTE: all flags provided with this argument are subject to filtering + // based on a whitelist in shell/common/switches.cc. If any flag provided + // is not present in the whitelist, the process will immediately + // terminate. + if (intent.hasExtra(ARG_KEY_DART_FLAGS)) { + args.add(ARG_DART_FLAGS + "=" + intent.getStringExtra(ARG_KEY_DART_FLAGS)); + } + return new FlutterShellArgs(args); } diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index ac99a9b45ae26..3868bf628a15d 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -23,6 +23,7 @@ import io.flutter.plugin.platform.SingleViewPresentationTest; import io.flutter.util.PreconditionsTest; import test.io.flutter.embedding.engine.FlutterEngineTest; +import test.io.flutter.embedding.engine.FlutterShellArgsTest; import test.io.flutter.embedding.engine.PluginComponentTest; import test.io.flutter.embedding.engine.dart.DartExecutorTest; @@ -36,6 +37,7 @@ FlutterEngineTest.class, FlutterFragmentTest.class, FlutterJNITest.class, + FlutterShellArgsTest.class, FlutterRendererTest.class, FlutterViewTest.class, PlatformChannelTest.class, diff --git a/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java b/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java new file mode 100644 index 0000000000000..535980dc5e808 --- /dev/null +++ b/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java @@ -0,0 +1,30 @@ +package test.io.flutter.embedding.engine; + +import android.content.Intent; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import io.flutter.embedding.engine.FlutterShellArgs; + +import static org.junit.Assert.assertEquals; + +@Config(manifest=Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class FlutterShellArgsTest { + @Test + public void itProcessesDartFlags() { + // Setup the test. + Intent intent = new Intent(); + intent.putExtra("dart-flags", "--observe --no-hot --no-pub"); + + // Execute the behavior under test. + FlutterShellArgs args = FlutterShellArgs.fromIntent(intent); + + // Verify results. + assertEquals(1, args.toArray().length); + assertEquals("--dart-flags=--observe --no-hot --no-pub", args.toArray()[0]); + } +} From e65ea213ceb16991a93d4f9fced56284ee5c2a56 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 14 Nov 2019 00:39:35 -0500 Subject: [PATCH 127/591] Roll fuchsia/sdk/core/mac-amd64 from _QV9E... to 7XOyl... (#13856) Roll fuchsia/sdk/core/mac-amd64 from _QV9E... to 7XOyl... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 528901f290ca1..e850301131ead 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '_QV9Eqxy4Alyg7wqAfzaC8d3FfihSCmv6SwWNlKeIrUC' + 'version': '7XOyl-5S_0iSXB4hWYkZEQ6Pdcsfq_ZOky_U0UNIdacC' } ], 'condition': 'host_os == "mac"', From 3a89662ca171123772f086878f60eab116e49d10 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 14 Nov 2019 00:12:56 -0800 Subject: [PATCH 128/591] [web] Fix selectable text rendering (#13802) --- lib/web_ui/lib/src/engine/text/paragraph.dart | 6 ++-- .../test/golden_tests/engine/scuba.dart | 31 ++++++++++++++----- .../engine/text_style_golden_test.dart | 20 ++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 6117d2a5f6aa7..17b1041a5e212 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -362,7 +362,9 @@ class EngineParagraphStyle implements ui.ParagraphStyle { } double get _lineHeight { - if (_strutStyle == null || _strutStyle._height == null) { + // TODO(mdebbar): Implement proper support for strut styles. + // https://github.com/flutter/flutter/issues/32243 + if (_strutStyle == null || _strutStyle._height == null || _strutStyle._height == 0) { // When there's no strut height, always use paragraph style height. return _height; } @@ -1093,7 +1095,7 @@ void _applyParagraphStyleToElement({ cssStyle.textAlign = textAlignToCssValue( style._textAlign, style._textDirection ?? ui.TextDirection.ltr); } - if (style._lineHeight != style._lineHeight) { + if (style._lineHeight != previousStyle._lineHeight) { cssStyle.lineHeight = '${style._lineHeight}'; } if (style._textDirection != previousStyle._textDirection) { diff --git a/lib/web_ui/test/golden_tests/engine/scuba.dart b/lib/web_ui/test/golden_tests/engine/scuba.dart index 18dff26fd7af9..a43afdbb59dc5 100644 --- a/lib/web_ui/test/golden_tests/engine/scuba.dart +++ b/lib/web_ui/test/golden_tests/engine/scuba.dart @@ -39,18 +39,31 @@ class EngineScubaTester { return EngineScubaTester(viewportSize); } - Future diffScreenshot(String fileName, {double maxDiffRate}) async { - await matchGoldenFile('$fileName.png', - region: ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height), - maxDiffRate: maxDiffRate); + ui.Rect get viewportRegion => + ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height); + + Future diffScreenshot( + String fileName, { + ui.Rect region, + double maxDiffRate, + }) async { + await matchGoldenFile( + '$fileName.png', + region: region ?? viewportRegion, + maxDiffRate: maxDiffRate, + ); } /// Prepares the DOM and inserts all the necessary nodes, then invokes scuba's /// screenshot diffing. /// /// It also cleans up the DOM after itself. - Future diffCanvasScreenshot(EngineCanvas canvas, String fileName, - {double maxDiffRate}) async { + Future diffCanvasScreenshot( + EngineCanvas canvas, + String fileName, { + ui.Rect region, + double maxDiffRate, + }) async { // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); try { @@ -60,7 +73,11 @@ class EngineScubaTester { if (TextMeasurementService.enableExperimentalCanvasImplementation) { screenshotName += '+canvas_measurement'; } - await diffScreenshot(screenshotName, maxDiffRate: maxDiffRate); + await diffScreenshot( + screenshotName, + region: region, + maxDiffRate: maxDiffRate, + ); } finally { // The page is reused across tests, so remove the element after taking the // Scuba screenshot. diff --git a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart index 04742ad3f92f2..3017dade360fc 100644 --- a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart @@ -218,4 +218,24 @@ void main() async { drawTextWithShadow(canvas); return scuba.diffCanvasScreenshot(canvas, 'text_shadow', maxDiffRate: 0.2); }, bSkipHoudini: true); + + testEachCanvas('Handles disabled strut style', (EngineCanvas canvas) { + // Flutter uses [StrutStyle.disabled] for the [SelectableText] widget. This + // translates into a strut style with a [height] of 0, which wasn't being + // handled correctly by the web engine. + final StrutStyle disabled = StrutStyle(height: 0, leading: 0); + canvas.drawParagraph( + paragraph( + 'Hello\nWorld', + paragraphStyle: ParagraphStyle(strutStyle: disabled), + ), + Offset.zero, + ); + return scuba.diffCanvasScreenshot( + canvas, + 'text_strut_style_disabled', + region: Rect.fromLTRB(0, 0, 100, 100), + maxDiffRate: 0.9 / 100, // 0.9% + ); + }); } From 562dd0394b3b47346a0f27e28c5bb30cb5195749 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 14 Nov 2019 11:03:05 -0800 Subject: [PATCH 129/591] [web] Change canvas sibling transforms to 3d with z=0 to get around canvas rendering bug. (#13860) * Change off canvas element transforms to 3d with z=0 * update matrix4 call to call 3d version --- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 10 +++--- lib/web_ui/lib/src/engine/engine_canvas.dart | 2 +- lib/web_ui/lib/src/engine/util.dart | 33 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index c7c223b38e8eb..9a5b268ebffc7 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -596,7 +596,7 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { } } else { final String cssTransform = - matrix4ToCssTransform(transformWithOffset(currentTransform, p)); + matrix4ToCssTransform3d(transformWithOffset(currentTransform, p)); imgElement.style ..transformOrigin = '0 0 0' ..transform = cssTransform; @@ -732,7 +732,7 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { } } else { final String cssTransform = - matrix4ToCssTransform(transformWithOffset(currentTransform, offset)); + matrix4ToCssTransform3d(transformWithOffset(currentTransform, offset)); paragraphElement.style ..transformOrigin = '0 0 0' ..transform = cssTransform; @@ -966,7 +966,7 @@ List _clipContent(List<_SaveClipEntry> clipStack, ..translate(clipOffsetX, clipOffsetY); curElement.style ..overflow = 'hidden' - ..transform = matrix4ToCssTransform(newClipTransform) + ..transform = matrix4ToCssTransform3d(newClipTransform) ..transformOrigin = '0 0 0' ..width = '${rect.right - clipOffsetX}px' ..height = '${rect.bottom - clipOffsetY}px'; @@ -982,7 +982,7 @@ List _clipContent(List<_SaveClipEntry> clipStack, curElement.style ..borderRadius = borderRadius ..overflow = 'hidden' - ..transform = matrix4ToCssTransform(newClipTransform) + ..transform = matrix4ToCssTransform3d(newClipTransform) ..transformOrigin = '0 0 0' ..width = '${roundRect.right - clipOffsetX}px' ..height = '${roundRect.bottom - clipOffsetY}px'; @@ -1020,6 +1020,6 @@ List _clipContent(List<_SaveClipEntry> clipStack, String _cssTransformAtOffset( Matrix4 transform, double offsetX, double offsetY) { - return matrix4ToCssTransform( + return matrix4ToCssTransform3d( transformWithOffset(transform, ui.Offset(offsetX, offsetY))); } diff --git a/lib/web_ui/lib/src/engine/engine_canvas.dart b/lib/web_ui/lib/src/engine/engine_canvas.dart index e7c37ca0a79a6..b1e29ff2f7f50 100644 --- a/lib/web_ui/lib/src/engine/engine_canvas.dart +++ b/lib/web_ui/lib/src/engine/engine_canvas.dart @@ -261,7 +261,7 @@ html.Element _drawParagraphElement( paragraphStyle ..transformOrigin = '0 0 0' ..transform = - matrix4ToCssTransform(transformWithOffset(transform, offset)); + matrix4ToCssTransform3d(transformWithOffset(transform, offset)); } final ParagraphGeometricStyle style = paragraph._geometricStyle; diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 0871364084b0b..046c4b8e3c58c 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -54,6 +54,11 @@ String matrix4ToCssTransform(Matrix4 matrix) { return float64ListToCssTransform(matrix.storage); } +/// Converts [matrix] to CSS transform value. +String matrix4ToCssTransform3d(Matrix4 matrix) { + return float64ListToCssTransform3d(matrix.storage); +} + /// Returns `true` is the [matrix] describes an identity transformation. bool isIdentityFloat64ListTransform(Float64List matrix) { assert(matrix.length == 16); @@ -104,6 +109,34 @@ String float64ListToCssTransform(Float64List matrix) { } } +/// Converts [matrix] to CSS transform value. +String float64ListToCssTransform3d(Float64List matrix) { + assert(matrix.length == 16); + final Float64List m = matrix; + if (m[0] == 1.0 && + m[1] == 0.0 && + m[2] == 0.0 && + m[3] == 0.0 && + m[4] == 0.0 && + m[5] == 1.0 && + m[6] == 0.0 && + m[7] == 0.0 && + m[8] == 0.0 && + m[9] == 0.0 && + m[10] == 1.0 && + m[11] == 0.0 && + // 12 can be anything + // 13 can be anything + m[14] == 0.0 && + m[15] == 1.0) { + final double tx = m[12]; + final double ty = m[13]; + return 'translate3d(${tx}px, ${ty}px, 0px)'; + } else { + return 'matrix3d(${m[0]},${m[1]},${m[2]},${m[3]},${m[4]},${m[5]},${m[6]},${m[7]},${m[8]},${m[9]},${m[10]},${m[11]},${m[12]},${m[13]},${m[14]},${m[15]})'; + } +} + bool get assertionsEnabled { bool k = false; assert(k = true); From 77c3512ec85e28d906200b6891038dd5b0cf1838 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Thu, 14 Nov 2019 11:08:34 -0800 Subject: [PATCH 130/591] Implement the rest of ui.Path methods for CanvasKit (#13851) - the relative path methods - Path.combine --- .../lib/src/engine/compositor/path.dart | 58 ++++++++++++++++--- lib/web_ui/lib/src/ui/path.dart | 3 + 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/lib/web_ui/lib/src/engine/compositor/path.dart b/lib/web_ui/lib/src/engine/compositor/path.dart index 44f07bb671e50..e501fb2cbb370 100644 --- a/lib/web_ui/lib/src/engine/compositor/path.dart +++ b/lib/web_ui/lib/src/engine/compositor/path.dart @@ -230,33 +230,41 @@ class SkPath implements ui.Path { double rotation = 0.0, bool largeArc = false, bool clockwise = true}) { - throw 'relativeArcToPoint'; + _skPath.callMethod('rArcTo', [ + radius.x, + radius.y, + rotation, + !largeArc, + !clockwise, + arcEndDelta.dx, + arcEndDelta.dy, + ]); } @override void relativeConicTo(double x1, double y1, double x2, double y2, double w) { - throw 'relativeConicTo'; + _skPath.callMethod('rConicTo', [x1, y1, x2, y2, w]); } @override void relativeCubicTo( double x1, double y1, double x2, double y2, double x3, double y3) { - throw 'relativeCubicTo'; + _skPath.callMethod('rCubicTo', [x1, y1, x2, y2, x3, y3]); } @override void relativeLineTo(double dx, double dy) { - throw 'relativeLineTo'; + _skPath.callMethod('rLineTo', [dx, dy]); } @override void relativeMoveTo(double dx, double dy) { - throw 'relativeMoveTo'; + _skPath.callMethod('rMoveTo', [dx, dy]); } @override void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { - throw 'relativeQuadraticBezierTo'; + _skPath.callMethod('rQuadTo', [x1, y1, x2, y2]); } @override @@ -274,10 +282,46 @@ class SkPath implements ui.Path { return SkPath._fromSkPath(newPath); } + static SkPath combine( + ui.PathOperation operation, + ui.Path uiPath1, + ui.Path uiPath2, + ) { + final SkPath path1 = uiPath1; + final SkPath path2 = uiPath2; + js.JsObject pathOp; + switch (operation) { + case ui.PathOperation.difference: + pathOp = canvasKit['PathOp']['Difference']; + break; + case ui.PathOperation.intersect: + pathOp = canvasKit['PathOp']['Intersect']; + break; + case ui.PathOperation.union: + pathOp = canvasKit['PathOp']['Union']; + break; + case ui.PathOperation.xor: + pathOp = canvasKit['PathOp']['XOR']; + break; + case ui.PathOperation.reverseDifference: + pathOp = canvasKit['PathOp']['ReverseDifference']; + break; + } + final js.JsObject newPath = canvasKit.callMethod( + 'MakePathFromOp', + [ + path1._skPath, + path2._skPath, + pathOp, + ], + ); + return SkPath._fromSkPath(newPath); + } + @override List get subpaths { throw UnimplementedError( - 'Path.subpaths is not supported in the CanvasKit backend.'); + 'Path.subpaths is not used in the CanvasKit backend.'); } @override diff --git a/lib/web_ui/lib/src/ui/path.dart b/lib/web_ui/lib/src/ui/path.dart index f22c9a679db05..8774ffc984883 100644 --- a/lib/web_ui/lib/src/ui/path.dart +++ b/lib/web_ui/lib/src/ui/path.dart @@ -1018,6 +1018,9 @@ class Path { static Path combine(PathOperation operation, Path path1, Path path2) { assert(path1 != null); assert(path2 != null); + if (engine.experimentalUseSkia) { + return engine.SkPath.combine(operation, path1, path2); + } throw UnimplementedError(); } From 174e0e9150e274df6617913220dd9c0943f20d6f Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 14 Nov 2019 11:32:51 -0800 Subject: [PATCH 131/591] Roll src/third_party/dart dc35290111..dc808f3fcb (5 commits) (#13859) dart-lang/sdk@dc808f3fcb [vm/compiler] Canonicalize CompressedStackMaps payloads when possible. dart-lang/sdk@6e85f3337a [cfe] Support null-aware cascade dart-lang/sdk@3855eeaae7 [cfe] Move shared id-tests into id_tests folder dart-lang/sdk@45033c6ad9 [cfe] Use StaticTypeContext for getStaticType dart-lang/sdk@efbfda2e6d [gardening] Fix frontend-server-test uri vs file path use. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e850301131ead..1af892521f5d7 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'dc35290111c03b29f309029f499f08fbd9e667e4', + 'dart_revision': 'dc808f3fcbf7e6de7e2b25441ff7ed891362e70a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index f882ad6f08cc7..536280fd89ab0 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: ca2f5a478a350df271bc591b2a64d1e6 +Signature: 20f0c52cba7e0b76e6053ed0c2838e45 UNUSED LICENSES: From 33d997ce12cea6d016d619c30f2c8eac504f916c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 14 Nov 2019 14:44:26 -0500 Subject: [PATCH 132/591] Roll fuchsia/sdk/core/mac-amd64 from 7XOyl... to VMTIz... (#13861) Roll fuchsia/sdk/core/mac-amd64 from 7XOyl... to VMTIz... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1af892521f5d7..267852fef892e 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '7XOyl-5S_0iSXB4hWYkZEQ6Pdcsfq_ZOky_U0UNIdacC' + 'version': 'VMTIzrEWwZ-aY_TNqL6fkCX7mTKnVqenKC5jzL1cuKIC' } ], 'condition': 'host_os == "mac"', From b4899d911ce6423778fe626d9eb2aae3d00beb82 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 14 Nov 2019 14:47:52 -0500 Subject: [PATCH 133/591] Roll src/third_party/skia d860a78fd60c..e57ca4931952 (44 commits) (#13862) https://skia.googlesource.com/skia.git/+log/d860a78fd60c..e57ca4931952 git log d860a78fd60c..e57ca4931952 --date=short --no-merges --format='%ad %ae %s' 2019-11-14 rosasco@google.com Use original fuchsia sdk token 2019-11-14 egdaniel@google.com Miscellaneous updates to handle more vulkan creation failures. 2019-11-14 flar@google.com Only define SK_GL for Flutter on non-Fuchsia platforms. 2019-11-14 robertphillips@google.com Revert "Reland "Implement sample mask and sample locations support in Vulkan"" 2019-11-14 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 4f023f565a61..1b52f05868c9 (8 commits) 2019-11-14 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 88632cac30e1..b2407dd746de (3 commits) 2019-11-14 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-14 csmartdalton@google.com Reland "Implement sample mask and sample locations support in Vulkan" 2019-11-14 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-13 mtklein@google.com implement assert_true on ARM 2019-11-13 brianosman@google.com Move the persistent GrMemoryPool to the heap 2019-11-13 csmartdalton@google.com Implement mixed samples support in vulkan 2019-11-13 csmartdalton@google.com Enforce extra constraints for mixed samples at GrCaps level 2019-11-13 emircan@google.com Disable VkProtectedContext_DDLMakeRenderTargetTest 2019-11-13 nigeltao@google.com Move SkWuffsCodec's reset_and_decode_image_config 2019-11-13 robertphillips@google.com Revert "Respect the max indexBuffer limits in the bulk texture draw API" 2019-11-13 jvanverth@google.com Put check for MTLTextureUsageShaderRead in available block. 2019-11-13 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms 0e5f77218153..8d45badce994 (1 commits) 2019-11-13 robertphillips@google.com Respect the max indexBuffer limits in the bulk texture draw API 2019-11-13 robertphillips@google.com Remove GL 4-bit stencil option 2019-11-13 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-13 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 e33c1582b4bc..4f023f565a61 (4 commits) 2019-11-13 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 9da287fd0264..88632cac30e1 (6 commits) 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-12 herb@google.com Force single glyph calls through bulk interface 2019-11-12 rosasco@google.com Reland fully delineate GL usage w/ skia_use_gl. 2019-11-12 csmartdalton@google.com Fix detection of when we will have mixed sampled coverage 2019-11-12 mtklein@google.com sketch out structure for ops with immediates 2019-11-12 mtklein@google.com implement assert_true on x86 2019-11-12 mtklein@google.com add Release SkVMBlitter bot 2019-11-12 robertphillips@google.com Remove final usage of GrProgramDesc's header 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-12 csmartdalton@google.com Add 'numRasterSamples' and 'isMixedSampled' to GrProgramInfo 2019-11-12 ccross@android.com [SkQP/Android] Replace -Weverything with -Wextra 2019-11-12 robertphillips@google.com Use a priori knowledge about the number of stencil bits in Dawn, Metal and Vulkan backends 2019-11-12 senorblanco@chromium.org Dawn: implement dynamic primitive processor texture handling. 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 31edef751a8d..9da287fd0264 (6 commits) 2019-11-12 jvanverth@google.com Detect whether trying to blit a Metal swapchain texture, and fail if so. 2019-11-12 senorblanco@chromium.org Dawn: fix bug in stencil handling. 2019-11-12 nigeltao@google.com Free SkWuffsCodec frame-count decoder earlier 2019-11-12 bungeman@google.com Track and force opsz axis on Mac. 2019-11-12 herb@google.com Make metrics const on SkStrike 2019-11-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@e57ca4931952 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 267852fef892e..d3fbca4204bf3 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd860a78fd60c6c06d5de16863b6ccd8b008565ac', + 'skia_revision': 'e57ca49319522930e1342be90362ccf7cb4e9214', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f6c3705fbf08a..a84041ed3b8ae 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9b4cfb54c01eb26dfc1c57974b36fe04 +Signature: 374b740d431aaf794c94eb97ecf5327c UNUSED LICENSES: From f456423cfb820d07bb36e9a8979e3d75cc9d8d76 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 14 Nov 2019 11:50:45 -0800 Subject: [PATCH 134/591] RendererContextSwitch guard flutter's gl context rework. (#13812) --- ci/licenses_golden/licenses_flutter | 4 + shell/common/BUILD.gn | 2 + shell/common/rasterizer.cc | 56 +++++++-- shell/common/rasterizer.h | 9 ++ .../common/renderer_context_switch_manager.cc | 30 +++++ .../common/renderer_context_switch_manager.h | 116 ++++++++++++++++++ shell/common/shell_test.cc | 12 +- shell/common/shell_test.h | 6 +- shell/common/shell_unittests.cc | 63 ++++++++++ shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 +++-- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/gpu/gpu_surface_metal.h | 3 +- shell/gpu/gpu_surface_metal.mm | 5 +- shell/platform/android/android_surface_gl.cc | 13 +- shell/platform/android/android_surface_gl.h | 7 +- shell/platform/darwin/ios/BUILD.gn | 2 + .../ios/framework/Source/FlutterEngine.mm | 1 + .../framework/Source/FlutterPlatformViews.mm | 16 ++- .../Source/FlutterPlatformViews_Internal.h | 6 + shell/platform/darwin/ios/ios_gl_context.h | 17 ++- shell/platform/darwin/ios/ios_gl_context.mm | 22 ++-- .../ios/ios_gl_context_switch_manager.h | 65 ++++++++++ .../ios/ios_gl_context_switch_manager.mm | 79 ++++++++++++ .../darwin/ios/ios_gl_render_target.h | 16 +-- .../darwin/ios/ios_gl_render_target.mm | 43 ++++--- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 10 +- shell/platform/darwin/ios/ios_surface_gl.mm | 18 ++- shell/platform/darwin/ios/ios_surface_metal.h | 3 +- .../platform/darwin/ios/ios_surface_metal.mm | 5 +- .../darwin/ios/ios_surface_software.h | 5 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 +-- .../platform/embedder/embedder_surface_gl.cc | 13 +- shell/platform/embedder/embedder_surface_gl.h | 7 +- .../Scenarios.xcodeproj/project.pbxproj | 16 ++- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ++++ .../Scenarios/Scenarios/GLTestPlatformView.h | 30 +++++ .../Scenarios/Scenarios/GLTestPlatformView.m | 90 ++++++++++++++ .../ScenariosUITests/PlatformViewGLTests.m | 39 ++++++ testing/scenario_app/lib/main.dart | 1 + .../scenario_app/lib/src/platform_view.dart | 36 ++++-- testing/scenario_app/lib/src/texture.dart | 0 47 files changed, 864 insertions(+), 129 deletions(-) create mode 100644 shell/common/renderer_context_switch_manager.cc create mode 100644 shell/common/renderer_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m create mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a2b25ba2b017e..1cf469f50c62d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -514,6 +514,8 @@ FILE: ../../../flutter/shell/common/pointer_data_dispatcher.cc FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h FILE: ../../../flutter/shell/common/rasterizer.cc FILE: ../../../flutter/shell/common/rasterizer.h +FILE: ../../../flutter/shell/common/renderer_context_switch_manager.cc +FILE: ../../../flutter/shell/common/renderer_context_switch_manager.h FILE: ../../../flutter/shell/common/run_configuration.cc FILE: ../../../flutter/shell/common/run_configuration.h FILE: ../../../flutter/shell/common/shell.cc @@ -803,6 +805,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f93bca63478f0..d87b6abede0e5 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -78,6 +78,8 @@ source_set("common") { "pointer_data_dispatcher.h", "rasterizer.cc", "rasterizer.h", + "renderer_context_switch_manager.cc", + "renderer_context_switch_manager.h", "run_configuration.cc", "run_configuration.h", "shell.cc", diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d61e1ff86ddc3..61cd119af7a22 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -163,7 +163,9 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, // happen in case of software rendering. surface = SkSurface::MakeRaster(image_info); } else { - if (!surface_->MakeRenderContextCurrent()) { + std::unique_ptr + context_switch = surface_->MakeRenderContextCurrent(); + if (!context_switch->GetSwitchResult()) { return nullptr; } @@ -377,7 +379,7 @@ static sk_sp CreateSnapshotSurface(GrContext* surface_context, return SkSurface::MakeRaster(image_info); } -static sk_sp ScreenshotLayerTreeAsImage( +sk_sp Rasterizer::ScreenshotLayerTreeAsImage( flutter::LayerTree* tree, flutter::CompositorContext& compositor_context, GrContext* surface_context, @@ -402,21 +404,20 @@ static sk_sp ScreenshotLayerTreeAsImage( auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr, root_surface_transformation, false, nullptr); + canvas->clear(SK_ColorTRANSPARENT); frame->Raster(*tree, true); - canvas->flush(); + ScreenshotFlushCanvas(*canvas); // Prepare an image from the surface, this image may potentially be on th GPU. - auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot(); + auto potentially_gpu_snapshot = MakeImageSnapshot(snapshot_surface); if (!potentially_gpu_snapshot) { - FML_LOG(ERROR) << "Screenshot: unable to make image screenshot"; return nullptr; } // Copy the GPU image snapshot into CPU memory. - auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage(); + auto cpu_snapshot = MakeRasterImage(potentially_gpu_snapshot); if (!cpu_snapshot) { - FML_LOG(ERROR) << "Screenshot: unable to make raster image"; return nullptr; } @@ -436,6 +437,47 @@ static sk_sp ScreenshotLayerTreeAsImage( return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize()); } +sk_sp Rasterizer::MakeImageSnapshot( + sk_sp snapshot_surface) { + std::unique_ptr + context_switch = surface_->MakeRenderContextCurrent(); + if (!context_switch->GetSwitchResult()) { + return nullptr; + } + auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot(); + if (!potentially_gpu_snapshot) { + FML_LOG(ERROR) << "Screenshot: unable to make image screenshot"; + return nullptr; + } + return potentially_gpu_snapshot; +} + +sk_sp Rasterizer::MakeRasterImage( + sk_sp potentially_gpu_snapshot) { + std::unique_ptr + context_switch = surface_->MakeRenderContextCurrent(); + if (!context_switch->GetSwitchResult()) { + return nullptr; + } + auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage(); + if (!cpu_snapshot) { + FML_LOG(ERROR) << "Screenshot: unable to make raster image"; + return nullptr; + } + return cpu_snapshot; +} + +void Rasterizer::ScreenshotFlushCanvas(SkCanvas& canvas) { + std::unique_ptr + context_switch = surface_->MakeRenderContextCurrent(); + if (!context_switch->GetSwitchResult()) { + FML_LOG(ERROR) + << "Screenshot: unable to switch gl context to flutter's context"; + return; + } + canvas.flush(); +} + Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 097d93fd4c92a..9117f3674b734 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -432,6 +432,15 @@ class Rasterizer final : public SnapshotDelegate { void FireNextFrameCallbackIfPresent(); + sk_sp ScreenshotLayerTreeAsImage( + flutter::LayerTree* tree, + flutter::CompositorContext& compositor_context, + GrContext* surface_context, + bool compressed); + sk_sp MakeImageSnapshot(sk_sp snapshot_surface); + sk_sp MakeRasterImage(sk_sp potentially_gpu_snapshot); + void ScreenshotFlushCanvas(SkCanvas& canvas); + FML_DISALLOW_COPY_AND_ASSIGN(Rasterizer); }; diff --git a/shell/common/renderer_context_switch_manager.cc b/shell/common/renderer_context_switch_manager.cc new file mode 100644 index 0000000000000..6bd5dd12832c7 --- /dev/null +++ b/shell/common/renderer_context_switch_manager.cc @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "renderer_context_switch_manager.h" + +namespace flutter { + +RendererContextSwitchManager::RendererContextSwitchManager() = default; + +RendererContextSwitchManager::~RendererContextSwitchManager() = default; + +RendererContextSwitchManager::RendererContextSwitch::RendererContextSwitch() = + default; + +RendererContextSwitchManager::RendererContextSwitch::~RendererContextSwitch(){}; + +RendererContextSwitchManager::RendererContextSwitchPureResult:: + RendererContextSwitchPureResult(bool switch_result) + : switch_result_(switch_result){}; + +RendererContextSwitchManager::RendererContextSwitchPureResult:: + ~RendererContextSwitchPureResult() = default; + +bool RendererContextSwitchManager::RendererContextSwitchPureResult:: + GetSwitchResult() { + return switch_result_; +} + +} // namespace flutter diff --git a/shell/common/renderer_context_switch_manager.h b/shell/common/renderer_context_switch_manager.h new file mode 100644 index 0000000000000..323a3a5e5af32 --- /dev/null +++ b/shell/common/renderer_context_switch_manager.h @@ -0,0 +1,116 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ + +#include +#include "flutter/fml/macros.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// Manages `RendererContextSwitch`. +/// +/// Should be subclassed for platforms that uses GL and requires context +/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the +/// `RendererContextSwitchManager` to set gl contexts. +/// +class RendererContextSwitchManager { + public: + //------------------------------------------------------------------------------ + /// Switches the gl context to the flutter's contexts. + /// + /// Should be subclassed for each platform embedder that uses GL. + /// In construction, it should set the current context to a flutter's context + /// In destruction, it should rest the current context. + /// + class RendererContextSwitch { + public: + RendererContextSwitch(); + + virtual ~RendererContextSwitch(); + + virtual bool GetSwitchResult() = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitch); + }; + + RendererContextSwitchManager(); + ~RendererContextSwitchManager(); + + //---------------------------------------------------------------------------- + /// @brief Creates a shell instance using the provided settings. The + /// callbacks to create the various shell subcomponents will be + /// called on the appropriate threads before this method returns. + /// If this is the first instance of a shell in the process, this + /// call also bootstraps the Dart VM. + /// + /// @param[in] task_runners The task runners + /// @param[in] settings The settings + /// @param[in] on_create_platform_view The callback that must return a + /// platform view. This will be called on + /// the platform task runner before this + /// method returns. + /// @param[in] on_create_rasterizer That callback that must provide a + /// valid rasterizer. This will be called + /// on the render task runner before this + /// method returns. + /// + /// @return A full initialized shell if the settings and callbacks are + /// valid. The root isolate has been created but not yet launched. + /// It may be launched by obtaining the engine weak pointer and + /// posting a task onto the UI task runner with a valid run + /// configuration to run the isolate. The embedder must always + /// check the validity of the shell (using the IsSetup call) + /// immediately after getting a pointer to it. + /// + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's context as current context. + /// + /// @return A `RendererContextSwitch` with `GetSwitchResult` returning + /// true if the setting process is succesful. + virtual std::unique_ptr MakeCurrent() = 0; + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's resources context as current context. + /// + /// @return A `RendererContextSwitch` with `GetSwitchResult` returning + /// true if the setting process is succesful. + virtual std::unique_ptr ResourceMakeCurrent() = 0; + + //------------------------------------------------------------------------------ + /// A representation of a `RendererContextSwitch` that doesn't require actual + /// context switching. + /// + class RendererContextSwitchPureResult final : public RendererContextSwitch { + public: + // Constructor that creates an `RendererContextSwitchPureResult`. + // The `GetSwitchResult` will return the same value as `switch_result`. + + //---------------------------------------------------------------------------- + /// @brief Constructs a `RendererContextSwitchPureResult`. + /// + /// @param[in] switch_result the switch result that will be returned + /// in `GetSwitchResult()` + /// + RendererContextSwitchPureResult(bool switch_result); + + ~RendererContextSwitchPureResult(); + + bool GetSwitchResult() override; + + private: + bool switch_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchPureResult); + }; + + FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchManager); +}; + +} // namespace flutter + +#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 51370d082862b..3fe5a2b9e0f87 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,8 +355,11 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -bool ShellTestPlatformView::GLContextMakeCurrent() { - return gl_surface_.MakeCurrent(); +std::unique_ptr +ShellTestPlatformView::GLContextMakeCurrent() { + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>( + gl_surface_.MakeCurrent()); } // |GPUSurfaceGLDelegate| @@ -387,5 +390,10 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } +std::shared_ptr +ShellTestPlatformView::GetRendererContextSwitchManager() { + return nullptr; +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index fdee9653b71ce..27d32f058ae0e 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,7 +144,8 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -161,6 +162,9 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + std::shared_ptr + GetRendererContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 0146619cce4cf..0600853532e80 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -952,5 +952,68 @@ TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) { DestroyShell(std::move(shell), std::move(task_runners)); } +TEST_F(ShellTest, RasterizerScreenshot) { + Settings settings = CreateSettingsForFixture(); + auto configuration = RunConfiguration::InferFromSettings(settings); + auto task_runner = CreateNewThread(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(ValidateShell(shell.get())); + PlatformViewNotifyCreated(shell.get()); + + RunEngine(shell.get(), std::move(configuration)); + + auto latch = std::make_shared(); + + PumpOneFrame(shell.get()); + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetGPUTaskRunner(), [&shell, &latch]() { + Rasterizer::Screenshot screenshot = + shell->GetRasterizer()->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::CompressedImage, true); + EXPECT_NE(screenshot.data, nullptr); + + latch->Signal(); + }); + latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + +TEST_F(ShellTest, RasterizerMakeRasterSnapshot) { + Settings settings = CreateSettingsForFixture(); + auto configuration = RunConfiguration::InferFromSettings(settings); + auto task_runner = CreateNewThread(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(ValidateShell(shell.get())); + PlatformViewNotifyCreated(shell.get()); + + RunEngine(shell.get(), std::move(configuration)); + + auto latch = std::make_shared(); + + PumpOneFrame(shell.get()); + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetGPUTaskRunner(), [&shell, &latch]() { + SnapshotDelegate* delegate = + reinterpret_cast(shell->GetRasterizer().get()); + sk_sp image = delegate->MakeRasterSnapshot( + SkPicture::MakePlaceholder({0, 0, 50, 50}), SkISize::Make(50, 50)); + EXPECT_NE(image, nullptr); + + latch->Signal(); + }); + latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/surface.cc b/shell/common/surface.cc index b8a77ca1811d4..2b876e0c0f558 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,8 +60,10 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -bool Surface::MakeRenderContextCurrent() { - return true; +std::unique_ptr +Surface::MakeRenderContextCurrent() { + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>(true); } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index 7bbc16e24c690..0b19725da3b74 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,6 +10,7 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -58,7 +59,8 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual bool MakeRenderContextCurrent(); + virtual std::unique_ptr + MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5f30a48375d33..a7648b7f71d39 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -9,6 +9,7 @@ #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" #include "flutter/shell/common/persistent_cache.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -39,7 +40,10 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -87,8 +91,6 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; - - delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -98,7 +100,9 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -114,8 +118,9 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -126,8 +131,6 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; - - delegate_->GLContextClearCurrent(); } // |Surface| @@ -253,7 +256,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -285,7 +290,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - return std::make_unique(surface, submit_callback); + std::unique_ptr result = + std::make_unique(surface, submit_callback); + return result; } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -293,6 +300,8 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } + std::unique_ptr + context_switch = delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -329,7 +338,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } - return true; } @@ -360,7 +368,8 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -bool GPUSurfaceGL::MakeRenderContextCurrent() { +std::unique_ptr +GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 97325569bfd16..eb67440eee07c 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,7 +44,8 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - bool MakeRenderContextCurrent() override; + std::unique_ptr + MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dfe0ce7f468db..444e7301c1939 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,6 +7,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -16,7 +17,8 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual bool GLContextMakeCurrent() = 0; + virtual std::unique_ptr + GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -59,6 +61,9 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; + + virtual std::shared_ptr + GetRendererContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index fc6b0964766ce..acb5ca02a7162 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -49,7 +49,8 @@ class GPUSurfaceMetal : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - bool MakeRenderContextCurrent() override; + std::unique_ptr MakeRenderContextCurrent() + override; void ReleaseUnusedDrawableIfNecessary(); diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 81abf740d48f6..2aba2cd41f376 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -175,9 +175,10 @@ } // |Surface| -bool GPUSurfaceMetal::MakeRenderContextCurrent() { +std::unique_ptr +GPUSurfaceMetal::MakeRenderContextCurrent() { // This backend has no such concept. - return true; + return std::make_unique(true); } void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() { diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 737d9f293a518..a06b13e68f6bb 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,9 +104,12 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -bool AndroidSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr +AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return onscreen_context_->MakeCurrent(); + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>( + onscreen_context_->MakeCurrent()); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -130,4 +133,10 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +AndroidSurfaceGL::GetRendererContextSwitchManager() { + return nullptr; +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index d59302ad66509..bfe7b2ce06fb2 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,7 +47,8 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -61,6 +62,10 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr + GetRendererContextSwitchManager() override; + private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index a66c3bf48c5ae..3e67beb97e717 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,6 +86,8 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", + "ios_gl_context_switch_manager.h", + "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index a9f20708f15b5..2d76a30227713 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -553,6 +553,7 @@ - (void)performAction:(FlutterTextInputAction)action withClient:(int)client { - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type asBase64Encoded:(BOOL)base64Encode { FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell"; + NSLog(@"NSLog take screenshoit"); return _shell->Screenshot(type, base64Encode); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33ca14d9fabea..487fd42f5ea0d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,6 +160,11 @@ frame_size_ = frame_size; } +void FlutterPlatformViewsController::SetRendererContextSwitchManager( + std::shared_ptr gl_context_guard_manager) { + renderer_context_switch_manager_ = gl_context_guard_manager; +} + void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -368,7 +373,12 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, gl_context, gr_context); + if (renderer_context_switch_manager_ != nullptr) { + std::unique_ptr contextSwitch = + renderer_context_switch_manager_->MakeCurrent(); + } + + EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -455,6 +465,10 @@ std::shared_ptr gl_context, GrContext* gr_context) { FML_DCHECK(flutter_view_); + if (renderer_context_switch_manager_ != nullptr) { + std::unique_ptr contextSwitch = + renderer_context_switch_manager_->MakeCurrent(); + } auto overlay_it = overlays_.find(overlay_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c8daeaa605946..77b8ece44dc5d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,6 +11,7 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -80,6 +81,9 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); + void SetRendererContextSwitchManager( + std::shared_ptr gl_context_guard_manager); + void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -204,6 +208,8 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + std::shared_ptr renderer_context_switch_manager_; + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 232645d9c8592..8c73ce75dc028 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,16 +26,25 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - bool MakeCurrent(); + std::unique_ptr + MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); + + std::shared_ptr GetIOSGLContextSwitchManager() { + return renderer_context_switch_manager_; + } sk_sp ColorSpace() const { return color_space_; } + fml::scoped_nsobject GetContext() const { + return renderer_context_switch_manager_->GetContext(); + } + private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; sk_sp color_space_; + std::shared_ptr renderer_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 52fb85f8f19a9..a54387731e8d9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,15 +13,7 @@ namespace flutter { IOSGLContext::IOSGLContext() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } + renderer_context_switch_manager_ = std::make_shared(); // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -48,16 +40,16 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), context_.get(), - resource_context_.get()); + return std::make_unique(std::move(layer), renderer_context_switch_manager_); } -bool IOSGLContext::MakeCurrent() { - return [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr IOSGLContext::MakeCurrent() { + return renderer_context_switch_manager_->MakeCurrent(); } -bool IOSGLContext::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr +IOSGLContext::ResourceMakeCurrent() { + return renderer_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h new file mode 100644 index 0000000000000..56ee4f2eabb3e --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ + +#define GLES_SILENCE_DEPRECATION + +#import +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// The iOS implementation of `RendererContextSwitchManager`. +/// +/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and +/// sets the flutter's gl context as current. +/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to +/// current. +/// +class IOSGLContextSwitchManager final : public RendererContextSwitchManager { + public: + class IOSGLContextSwitch final : public RendererContextSwitch { + public: + IOSGLContextSwitch(IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context); + + ~IOSGLContextSwitch(); + + bool GetSwitchResult() override; + + private: + IOSGLContextSwitchManager& manager_; + bool switch_result_; + bool has_pushed_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); + }; + + IOSGLContextSwitchManager(); + + ~IOSGLContextSwitchManager(); + + std::unique_ptr MakeCurrent() override; + std::unique_ptr ResourceMakeCurrent() override; + + fml::scoped_nsobject GetContext(); + + private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; + fml::scoped_nsobject stored_; + + bool PushContext(fml::scoped_nsobject context); + void PopContext(); + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); +}; + +} + +#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm new file mode 100644 index 0000000000000..0be0a3bcb4df7 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios_gl_context_switch_manager.h" + +namespace flutter { + +IOSGLContextSwitchManager::IOSGLContextSwitchManager() { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } +}; + +IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; + +std::unique_ptr +IOSGLContextSwitchManager::MakeCurrent() { + return std::make_unique(*this, context_); +} + +std::unique_ptr +IOSGLContextSwitchManager::ResourceMakeCurrent() { + return std::make_unique(*this, resource_context_); +} + +fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { + return context_; +} + +bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { + EAGLContext* current = [EAGLContext currentContext]; + if (current == nil) { + [stored_.get() addObject:[NSNull null]]; + } else { + [stored_.get() addObject:current]; + } + bool result = [EAGLContext setCurrentContext:context.get()]; + return result; +} + +void IOSGLContextSwitchManager::PopContext() { + EAGLContext* last = [stored_.get() lastObject]; + [stored_.get() removeLastObject]; + if ([last isEqual:[NSNull null]]) { + [EAGLContext setCurrentContext:nil]; + return; + } + [EAGLContext setCurrentContext:last]; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( + IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context) + : manager_(manager) { + bool result = manager_.PushContext(context); + has_pushed_context_ = true; + switch_result_ = result; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { + if (!has_pushed_context_) { + return; + } + manager_.PopContext(); +} + +bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { + return switch_result_; +} +} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index b2eafe16e0950..f52c562fd96d0 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,14 +13,15 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context); + IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager); ~IOSGLRenderTarget(); @@ -32,16 +33,17 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - bool MakeCurrent(); + std::unique_ptr + MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; + std::shared_ptr renderer_context_switch_manager_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a57ba9c46b414..a7037746369db 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,22 +12,20 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context) +IOSGLRenderTarget::IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager) : layer_(std::move(layer)), - context_([context retain]), - resource_context_([resource_context retain]), + renderer_context_switch_manager_(gl_context_guard_manager), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - FML_DCHECK(context_ != nullptr); - FML_DCHECK(resource_context_ != nullptr); - - bool context_current = [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + renderer_context_switch_manager_->MakeCurrent(); + bool context_current = context_switch->GetSwitchResult(); FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -62,8 +60,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - EAGLContext* context = EAGLContext.currentContext; - [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + renderer_context_switch_manager_->MakeCurrent(); FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -71,7 +69,6 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -104,8 +101,9 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - - if (![EAGLContext setCurrentContext:context_]) { + std::unique_ptr context_switch = + renderer_context_switch_manager_->MakeCurrent(); + if (!context_switch->GetSwitchResult()) { return false; } @@ -116,7 +114,8 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { + if (![renderer_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER + fromDrawable:layer_.get()]) { return false; } @@ -132,12 +131,18 @@ return true; } -bool IOSGLRenderTarget::MakeCurrent() { - return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr +IOSGLRenderTarget::MakeCurrent() { + bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); + if (!isUpdateSuccessful) { + return std::make_unique(false); + } + return renderer_context_switch_manager_->MakeCurrent(); } -bool IOSGLRenderTarget::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr +IOSGLRenderTarget::ResourceMakeCurrent() { + return renderer_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 49f40f9eec76a..b852a63ee4e9a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -27,7 +28,8 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual bool ResourceContextMakeCurrent() = 0; + virtual std::unique_ptr + ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index c1019bb442bb0..708413c239400 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,6 +9,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -32,7 +33,8 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() + override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -40,7 +42,8 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - bool GLContextMakeCurrent() override; + std::unique_ptr GLContextMakeCurrent() + override; bool GLContextClearCurrent() override; @@ -53,6 +56,9 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetRendererContextSwitchManager() override; + // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 48e70e00a4e7a..6417e7d899b42 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,7 +28,8 @@ return render_target_->IsValid(); } -bool IOSSurfaceGL::ResourceContextMakeCurrent() { +std::unique_ptr +IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -56,11 +57,12 @@ return true; } -bool IOSSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr +IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return false; + return std::make_unique(false); } - return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); + return render_target_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -73,6 +75,11 @@ return IsValid() && render_target_->PresentRenderBuffer(); } +// |GPUSurfaceGLDelegate| +std::shared_ptr IOSSurfaceGL::GetRendererContextSwitchManager() { + return context_->GetIOSGLContextSwitchManager(); +} + // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -144,7 +151,8 @@ if (platform_views_controller == nullptr) { return true; } - + platform_views_controller->SetRendererContextSwitchManager( + context_->GetIOSGLContextSwitchManager()); bool submitted = platform_views_controller->SubmitFrame(std::move(context), context_); [CATransaction commit]; return submitted; diff --git a/shell/platform/darwin/ios/ios_surface_metal.h b/shell/platform/darwin/ios/ios_surface_metal.h index 2b001ea2b692d..1263571870bd4 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.h +++ b/shell/platform/darwin/ios/ios_surface_metal.h @@ -30,7 +30,8 @@ class IOSSurfaceMetal final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() + override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_metal.mm b/shell/platform/darwin/ios/ios_surface_metal.mm index 58a5fc328647b..827db668afa1f 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.mm +++ b/shell/platform/darwin/ios/ios_surface_metal.mm @@ -22,8 +22,9 @@ } // |IOSSurface| -bool IOSSurfaceMetal::ResourceContextMakeCurrent() { - return false; +std::unique_ptr +IOSSurfaceMetal::ResourceContextMakeCurrent() { + return std::make_unique(false); } // |IOSSurface| diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index daac2ffc77231..c0b6298e47bf7 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" - @class CALayer; namespace flutter { @@ -28,7 +28,8 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() + override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index ab5490cf25140..8c38ab5861e82 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,8 +27,9 @@ return layer_; } -bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return false; +std::unique_ptr +IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return std::make_unique(false); } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index bb37fa9610b2b..d867fc68fc6c8 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,15 +107,18 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + if (gl_context_ != nullptr) { + std::unique_ptr context_switch = + gl_context_->ResourceMakeCurrent(); + if (context_switch->GetSwitchResult()) { + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + } } - - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index d37b03aae8d9e..2efa1c01baf46 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,8 +34,11 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextMakeCurrent() { - return gl_dispatch_table_.gl_make_current_callback(); +std::unique_ptr +EmbedderSurfaceGL::GLContextMakeCurrent() { + return std::make_unique< + RendererContextSwitchManager::RendererContextSwitchPureResult>( + gl_dispatch_table_.gl_make_current_callback()); } // |GPUSurfaceGLDelegate| @@ -79,6 +82,12 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +EmbedderSurfaceGL::GetRendererContextSwitchManager() { + return nullptr; +} + // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index a01fa05d4e62c..12f264f383c37 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,7 +50,8 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -73,6 +74,10 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; + // |GPUSurfaceGLDelegate| + std::shared_ptr + GetRendererContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index e667c4c88678d..dd2238aba3181 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -136,6 +138,9 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -202,6 +207,8 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -239,6 +246,7 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -398,6 +406,7 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -418,6 +427,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -492,7 +502,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -545,7 +555,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -561,6 +571,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -584,6 +595,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index 87799ad5e6434..cd1c5b7366a89 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - - - - - - - - * registrar = + [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; + [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; + self.window.rootViewController = flutterViewController; +} + @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h new file mode 100644 index 0000000000000..19cefe7025986 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GLTestPlatformView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject*)messenger; + +- (UIView*)view; + +@end + +@interface GLTestPlatformViewFactory : NSObject + +- (instancetype)initWithMessenger:(NSObject*)messenger; + +@end + +@interface GLTestView : UIView + +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m new file mode 100644 index 0000000000000..8142550e52863 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m @@ -0,0 +1,90 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GLTestPlatformView.h" + +#define GLES_SILENCE_DEPRECATION + +@implementation GLTestPlatformView { + int64_t _viewId; + GLTestView* _view; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id)args + binaryMessenger:(NSObject*)messenger { + if ([super init]) { + _viewId = viewId; + _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; + } + return self; +} + +- (UIView*)view { + return _view; +} + +@end + +@implementation GLTestPlatformViewFactory { + NSObject* _messenger; +} + +- (instancetype)initWithMessenger:(NSObject*)messenger { + self = [super init]; + if (self) { + _messenger = messenger; + } + return self; +} + +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger]; + return platformView; +} + +- (NSObject*)createArgsCodec { + return [FlutterStringCodec sharedInstance]; +} + +@end + +@interface GLTestView () + +@property(strong, nonatomic) EAGLContext* context; + +@end + +@implementation GLTestView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + _context.debugLabel = @"platform view context"; + [EAGLContext setCurrentContext:_context]; + self.backgroundColor = [UIColor redColor]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [self checkEAGLContext]; + }); + } + return self; +} + +- (void)checkEAGLContext { + if ([EAGLContext currentContext] != _context) { + self.accessibilityIdentifier = @"gl_platformview_wrong_context"; + } else { + self.accessibilityIdentifier = @"gl_platformview_correct_context"; + } +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m new file mode 100644 index 0000000000000..d581926ae5a6a --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface PlatformViewGLTests : XCTestCase + +@property(nonatomic, strong) XCUIApplication* application; + +@end + +@implementation PlatformViewGLTests + +- (void)setUp { + self.continueAfterFailure = NO; + + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--platform-view-gl" ]; + [self.application launch]; +} + +- (void)testExample { + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || + [element.identifier isEqualToString:@"gl_platformview_correct_context"]; + }]; + XCUIElement* firstElement = + [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; + if (![firstElement waitForExistenceWithTimeout:30]) { + XCTFail(@"Failed due to not able to find platform view with 30 seconds"); + } + XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); +} + +@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 7a264b70008f0..82b589a6681fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,6 +25,7 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), + 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 05efe52fa8b25..86fb4df1df449 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -281,6 +281,24 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } +/// Platform view scenario for testing EAGLContext on iOS. +class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Constructs a platform view to test EAGLContext on iOS. + PlatformViewGLScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + finishBuilderByAddingPlatformViewAndPicture(builder, 6); + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -289,7 +307,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id) { + void createPlatformView(Window window, String text, int id, String viewType) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -313,8 +331,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - 'scenarios/textPlatformView'.length, - ...utf8.encode('scenarios/textPlatformView'), + viewType.length, + ...utf8.encode(viewType), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart new file mode 100644 index 0000000000000..e69de29bb2d1d From 6bab64e6dc232d206eee48f216aa1e3e72484837 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Nov 2019 12:15:33 -0800 Subject: [PATCH 135/591] Fix test to account for pixel ratio transformations being framework responsibility. (#13850) This incorrect assumption led to the introduction of a failure on an external embedder. Also dries up the section that copies the picture to the embedder managed render targets. Fixes https://github.com/flutter/flutter/issues/43906 Fixes https://b.corp.google.com/issues/143529469 --- .../embedder_external_view_embedder.cc | 62 ++++++++++++------- .../embedder_external_view_embedder.h | 4 ++ .../embedder/embedder_render_target.cc | 2 +- .../embedder/embedder_render_target.h | 2 +- shell/platform/embedder/fixtures/main.dart | 7 ++- .../tests/embedder_test_compositor.cc | 13 +++- .../embedder/tests/embedder_unittests.cc | 4 +- 7 files changed, 63 insertions(+), 31 deletions(-) diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 615359cf5e97e..dcb6f977bcb72 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -211,6 +211,33 @@ static FlutterLayer MakePlatformViewLayer( return layer; } +bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget( + sk_sp picture, + const EmbedderRenderTarget* render_target) const { + if (!picture || render_target == nullptr) { + return false; + } + + auto render_surface = render_target->GetRenderSurface(); + + if (!render_surface) { + return false; + } + + auto render_canvas = render_surface->getCanvas(); + + if (render_canvas == nullptr) { + return false; + } + + render_canvas->setMatrix(pending_surface_transformation_); + render_canvas->clear(SK_ColorTRANSPARENT); + render_canvas->drawPicture(picture); + render_canvas->flush(); + + return true; +} + // |ExternalViewEmbedder| bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { std::map @@ -225,18 +252,15 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { return false; } - // Copy the contents of the root picture recorder onto the root surface - // while making sure to take into account any surface transformations. - if (auto root_canvas = root_render_target_->GetRenderSurface()->getCanvas()) { - root_canvas->setMatrix(pending_surface_transformation_); - root_canvas->scale(pending_device_pixel_ratio_, - pending_device_pixel_ratio_); - root_canvas->clear(SK_ColorTRANSPARENT); - root_canvas->drawPicture( - root_picture_recorder_->finishRecordingAsPicture()); - root_canvas->flush(); - root_picture_recorder_.reset(); + // Copy the contents of the root picture recorder onto the root surface. + if (!RenderPictureToRenderTarget( + root_picture_recorder_->finishRecordingAsPicture(), + root_render_target_.get())) { + FML_LOG(ERROR) << "Could not render into the the root render target."; + return false; } + // The root picture recorder will be reset when a new frame begins. + root_picture_recorder_.reset(); { // The root surface is expressed as a layer. @@ -307,21 +331,13 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { render_targets_used[registry_key] = render_target; - auto render_surface = render_target->GetRenderSurface(); - auto render_canvas = render_surface ? render_surface->getCanvas() : nullptr; - - if (!render_canvas) { - FML_LOG(ERROR) - << "Could not acquire render canvas for on-screen rendering."; + if (!RenderPictureToRenderTarget(picture, render_target.get())) { + FML_LOG(ERROR) << "Could not render into the render target for platform " + "view of identifier " + << view_id; return false; } - render_canvas->setMatrix(pending_surface_transformation_); - render_canvas->scale(pending_device_pixel_ratio_, - pending_device_pixel_ratio_); - render_canvas->clear(SK_ColorTRANSPARENT); - render_canvas->drawPicture(picture); - render_canvas->flush(); // Indicate a layer for the backing store containing contents rendered by // Flutter. presented_layers.push_back(MakeBackingStoreLayer( diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index ca82f50902ef9..1ab13670a28cd 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -144,6 +144,10 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkMatrix GetSurfaceTransformation() const; + bool RenderPictureToRenderTarget( + sk_sp picture, + const EmbedderRenderTarget* render_target) const; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalViewEmbedder); }; diff --git a/shell/platform/embedder/embedder_render_target.cc b/shell/platform/embedder/embedder_render_target.cc index 1485cdd388fcd..8f6e447ba144e 100644 --- a/shell/platform/embedder/embedder_render_target.cc +++ b/shell/platform/embedder/embedder_render_target.cc @@ -30,7 +30,7 @@ const FlutterBackingStore* EmbedderRenderTarget::GetBackingStore() const { return &backing_store_; } -sk_sp EmbedderRenderTarget::GetRenderSurface() { +sk_sp EmbedderRenderTarget::GetRenderSurface() const { return render_surface_; } diff --git a/shell/platform/embedder/embedder_render_target.h b/shell/platform/embedder/embedder_render_target.h index a63a068334b9e..8afbed31e30f0 100644 --- a/shell/platform/embedder/embedder_render_target.h +++ b/shell/platform/embedder/embedder_render_target.h @@ -51,7 +51,7 @@ class EmbedderRenderTarget { /// /// @return The render surface. /// - sk_sp GetRenderSurface(); + sk_sp GetRenderSurface() const; //---------------------------------------------------------------------------- /// @brief The embedder backing store descriptor. This is the descriptor diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index e18a7fa3064c4..aa388c12829ac 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -481,7 +481,12 @@ void verify_b141980393() { void can_display_platform_view_with_pixel_ratio() { window.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); - builder.pushOffset(0.0, 0.0); // base + builder.pushTransform(Float64List.fromList([ + 2.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ])); // base builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); builder.pushOffset(0.0, 20.0); // offset builder.addPlatformView(42, width: 400.0, height: 280.0); diff --git a/shell/platform/embedder/tests/embedder_test_compositor.cc b/shell/platform/embedder/tests/embedder_test_compositor.cc index a30dfcc9ce551..9f68fd3cc8289 100644 --- a/shell/platform/embedder/tests/embedder_test_compositor.cc +++ b/shell/platform/embedder/tests/embedder_test_compositor.cc @@ -112,15 +112,22 @@ bool EmbedderTestCompositor::UpdateOffscrenComposition( break; }; + // If the layer is not a platform view but the engine did not specify an + // image for the backing store, it is an error. if (!layer_image && layer->type != kFlutterLayerContentTypePlatformView) { FML_LOG(ERROR) << "Could not snapshot layer in test compositor: " << *layer; return false; } - // The image rendered by Flutter already has the correct offset and - // transformation applied. The layers offset is meant for the platform. - canvas->drawImage(layer_image.get(), canvas_offset.x(), canvas_offset.y()); + // The test could have just specified no contents to be rendered in place of + // a platform view. This is not an error. + if (layer_image) { + // The image rendered by Flutter already has the correct offset and + // transformation applied. The layers offset is meant for the platform. + canvas->drawImage(layer_image.get(), canvas_offset.x(), + canvas_offset.y()); + } } last_composition_ = surface->makeImageSnapshot(); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 643034888aaa9..4fe669e663d6b 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -2803,7 +2803,7 @@ TEST_F(EmbedderTest, layer.type = kFlutterLayerContentTypePlatformView; layer.platform_view = &platform_view; layer.size = FlutterSizeMake(800.0, 560.0); - layer.offset = FlutterPointMake(0.0, 40.0); + layer.offset = FlutterPointMake(0.0, 80.0); ASSERT_EQ(*layers[1], layer); } @@ -2902,7 +2902,7 @@ TEST_F( layer.type = kFlutterLayerContentTypePlatformView; layer.platform_view = &platform_view; layer.size = FlutterSizeMake(560.0, 800.0); - layer.offset = FlutterPointMake(40.0, 0.0); + layer.offset = FlutterPointMake(80.0, 0.0); ASSERT_EQ(*layers[1], layer); } From 5b10fa35b169f6c62e5745f00ba62379f0398da5 Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Thu, 14 Nov 2019 12:16:02 -0800 Subject: [PATCH 136/591] Guard against orphaned semantic objects from referencing dead accessibility bridge on iOS (#13857) * Guard against orphaned semantic objects trying to reference a dead bridge on iOS. * Switched back to a function instead of a macro for checking the bridge. * Fixed some formatting issues. --- .../framework/Source/accessibility_bridge.h | 18 +++++++++-- .../framework/Source/accessibility_bridge.mm | 32 +++++++++++++++++++ .../Source/accessibility_text_entry.mm | 17 ++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index aaba31842c9e5..d6e0bb6446e0c 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -47,11 +47,25 @@ class AccessibilityBridge; /** * The accessibility bridge that this semantics object is attached to. This - * object may use the bridge to access contextual application information. A weak pointer is used - * because the platform view owns the accessibility bridge. + * object may use the bridge to access contextual application information. A weak + * pointer is used because the platform view owns the accessibility bridge. + * If you are referencing this property from an iOS callback, be sure to + * use `isAccessibilityBridgeActive` to protect against the case where this + * node may be orphaned. */ @property(nonatomic, readonly) fml::WeakPtr bridge; +/** + * Due to the fact that VoiceOver may hold onto SemanticObjects even after it shuts down, + * there can be situations where the AccessibilityBridge is shutdown, but the SemanticObject + * will still be alive. If VoiceOver is turned on again, it may try to access this orphaned + * SemanticObject. Methods that are called from the accessiblity framework should use + * this to guard against this case by just returning early if its bridge has been shutdown. + * + * See https://github.com/flutter/flutter/issues/43795 for more information. + */ +- (BOOL)isAccessibilityBridgeAlive; + /** * The semantics node used to produce this semantics object. */ diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 557e517729066..86cec67044dd0 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -140,6 +140,10 @@ - (void)dealloc { #pragma mark - Semantic object methods +- (BOOL)isAccessibilityBridgeAlive { + return [self bridge].get() != nil; +} + - (void)setSemanticsNode:(const flutter::SemanticsNode*)node { _node = *node; } @@ -169,6 +173,9 @@ - (BOOL)hasChildren { #pragma mark - UIAccessibility overrides - (BOOL)isAccessibilityElement { + if (![self isAccessibilityBridgeAlive]) + return false; + // Note: hit detection will only apply to elements that report // -isAccessibilityElement of YES. The framework will continue scanning the // entire element tree looking for such a hit. @@ -238,24 +245,35 @@ - (NSString*)routeName { } - (NSString*)accessibilityLabel { + if (![self isAccessibilityBridgeAlive]) + return nil; + if ([self node].label.empty()) return nil; return @([self node].label.data()); } - (NSString*)accessibilityHint { + if (![self isAccessibilityBridgeAlive]) + return nil; + if ([self node].hint.empty()) return nil; return @([self node].hint.data()); } - (NSString*)accessibilityValue { + if (![self isAccessibilityBridgeAlive]) + return nil; if ([self node].value.empty()) return nil; return @([self node].value.data()); } - (CGRect)accessibilityFrame { + if (![self isAccessibilityBridgeAlive]) + return CGRectMake(0, 0, 0, 0); + if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { return [super accessibilityFrame]; } @@ -308,6 +326,8 @@ - (id)accessibilityContainer { #pragma mark - UIAccessibilityAction overrides - (BOOL)accessibilityActivate { + if (![self isAccessibilityBridgeAlive]) + return NO; if (![self node].HasAction(flutter::SemanticsAction::kTap)) return NO; [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kTap); @@ -315,6 +335,8 @@ - (BOOL)accessibilityActivate { } - (void)accessibilityIncrement { + if (![self isAccessibilityBridgeAlive]) + return; if ([self node].HasAction(flutter::SemanticsAction::kIncrease)) { [self node].value = [self node].increasedValue; [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kIncrease); @@ -322,6 +344,8 @@ - (void)accessibilityIncrement { } - (void)accessibilityDecrement { + if (![self isAccessibilityBridgeAlive]) + return; if ([self node].HasAction(flutter::SemanticsAction::kDecrease)) { [self node].value = [self node].decreasedValue; [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDecrease); @@ -329,6 +353,8 @@ - (void)accessibilityDecrement { } - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { + if (![self isAccessibilityBridgeAlive]) + return NO; flutter::SemanticsAction action = GetSemanticsActionForScrollDirection(direction); if (![self node].HasAction(action)) return NO; @@ -337,6 +363,8 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } - (BOOL)accessibilityPerformEscape { + if (![self isAccessibilityBridgeAlive]) + return NO; if (![self node].HasAction(flutter::SemanticsAction::kDismiss)) return NO; [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDismiss); @@ -346,6 +374,8 @@ - (BOOL)accessibilityPerformEscape { #pragma mark UIAccessibilityFocus overrides - (void)accessibilityElementDidBecomeFocused { + if (![self isAccessibilityBridgeAlive]) + return; if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen); } @@ -356,6 +386,8 @@ - (void)accessibilityElementDidBecomeFocused { } - (void)accessibilityElementDidLoseFocus { + if (![self isAccessibilityBridgeAlive]) + return; if ([self node].HasAction(flutter::SemanticsAction::kDidLoseAccessibilityFocus)) { [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDidLoseAccessibilityFocus); diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm index f6a620080b543..3217ca7321dd1 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm @@ -219,24 +219,35 @@ - (UIView*)textInputView { } - (void)accessibilityElementDidBecomeFocused { + if (![self isAccessibilityBridgeAlive]) + return; [[self textInputSurrogate] accessibilityElementDidBecomeFocused]; [super accessibilityElementDidBecomeFocused]; } - (void)accessibilityElementDidLoseFocus { + if (![self isAccessibilityBridgeAlive]) + return; [[self textInputSurrogate] accessibilityElementDidLoseFocus]; [super accessibilityElementDidLoseFocus]; } - (BOOL)accessibilityElementIsFocused { + if (![self isAccessibilityBridgeAlive]) + return false; return [self node].HasFlag(flutter::SemanticsFlags::kIsFocused); } - (BOOL)accessibilityActivate { + if (![self isAccessibilityBridgeAlive]) + return false; return [[self textInputSurrogate] accessibilityActivate]; } - (NSString*)accessibilityLabel { + if (![self isAccessibilityBridgeAlive]) + return nil; + NSString* label = [super accessibilityLabel]; if (label != nil) return label; @@ -244,6 +255,8 @@ - (NSString*)accessibilityLabel { } - (NSString*)accessibilityHint { + if (![self isAccessibilityBridgeAlive]) + return nil; NSString* hint = [super accessibilityHint]; if (hint != nil) return hint; @@ -251,6 +264,8 @@ - (NSString*)accessibilityHint { } - (NSString*)accessibilityValue { + if (![self isAccessibilityBridgeAlive]) + return nil; NSString* value = [super accessibilityValue]; if (value != nil) return value; @@ -258,6 +273,8 @@ - (NSString*)accessibilityValue { } - (UIAccessibilityTraits)accessibilityTraits { + if (![self isAccessibilityBridgeAlive]) + return 0; // Adding UIAccessibilityTraitKeyboardKey to the trait list so that iOS treats it like // a keyboard entry control, thus adding support for text editing features, such as // pinch to select text, and up/down fling to move cursor. From 97df087ee67828345361fcd0aff83ac1d33ab1fa Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Thu, 14 Nov 2019 14:18:19 -0800 Subject: [PATCH 137/591] [fuchsia] Package flutter_frontend_server snapshot for fuchsia (#13865) --- flutter_frontend_server/BUILD.gn | 108 ++++++++++++----------- shell/platform/fuchsia/BUILD.gn | 8 ++ tools/fuchsia/build_fuchsia_artifacts.py | 2 + 3 files changed, 65 insertions(+), 53 deletions(-) diff --git a/flutter_frontend_server/BUILD.gn b/flutter_frontend_server/BUILD.gn index 75887f22cec64..dc9291521ecb1 100644 --- a/flutter_frontend_server/BUILD.gn +++ b/flutter_frontend_server/BUILD.gn @@ -2,6 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//third_party/dart/utils/application_snapshot.gni") + +# TODO(kaushikiska@): Leaving this here to make it a soft-transition +# Remove this once we no longer need engine sources in fuchsia/topaz. if (is_fuchsia_host || is_fuchsia) { import("//build/dart/dart_library.gni") import("//build/dart/dart_tool.gni") @@ -42,62 +46,60 @@ if (is_fuchsia_host || is_fuchsia) { ":flutter_frontend_server", ] } -} else { - import("//third_party/dart/utils/application_snapshot.gni") - - frontend_server_files = - exec_script("//third_party/dart/tools/list_dart_files.py", - [ - "absolute", - rebase_path("."), - ], - "list lines") - - frontend_server_files += - exec_script("//third_party/dart/tools/list_dart_files.py", - [ - "absolute", - rebase_path("../../third_party/dart/pkg"), - ], - "list lines") - - application_snapshot("frontend_server") { - main_dart = "bin/starter.dart" - deps = [ - ":package_incremental_compiler", - "$flutter_root/lib/snapshot:kernel_platform_files", - ] - dot_packages = rebase_path(".packages") - flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") - training_args = [ - "--train", - "--sdk-root=$flutter_patched_sdk", - rebase_path(main_dart), - ] +} - inputs = frontend_server_files - } +frontend_server_files = + exec_script("//third_party/dart/tools/list_dart_files.py", + [ + "absolute", + rebase_path("."), + ], + "list lines") - # For flutter/flutter#36738 we make the source files available so that - # we can generate a local frontend_server snapshot in the tools cache. - action("package_incremental_compiler") { - script = "$flutter_root/flutter_frontend_server/package_incremental.py" +frontend_server_files += + exec_script("//third_party/dart/tools/list_dart_files.py", + [ + "absolute", + rebase_path("../../third_party/dart/pkg"), + ], + "list lines") - inputs = frontend_server_files +application_snapshot("frontend_server") { + main_dart = "bin/starter.dart" + deps = [ + ":package_incremental_compiler", + "$flutter_root/lib/snapshot:kernel_platform_files", + ] + dot_packages = rebase_path(".packages") + flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") + training_args = [ + "--train", + "--sdk-root=$flutter_patched_sdk", + rebase_path(main_dart), + ] - outputs = [ - "$root_gen_dir/dart-pkg/flutter_frontend_server/pubspec.yaml", - "$root_gen_dir/dart-pkg/vm/pubspec.yaml", - "$root_gen_dir/dart-pkg/build_integration/pubspec.yaml", - "$root_gen_dir/dart-pkg/front_end/pubspec.yaml", - "$root_gen_dir/dart-pkg/kernel/pubspec.yaml", - "$root_gen_dir/dart-pkg/dev_compiler/pubspec.yaml", - ] + inputs = frontend_server_files +} - args = [ - "--input-root=" + rebase_path("//third_party/dart/pkg"), - "--output-root=" + rebase_path("$root_gen_dir/dart-pkg"), - "--frontend-server=" + rebase_path("$flutter_root"), - ] - } +# For flutter/flutter#36738 we make the source files available so that +# we can generate a local frontend_server snapshot in the tools cache. +action("package_incremental_compiler") { + script = "$flutter_root/flutter_frontend_server/package_incremental.py" + + inputs = frontend_server_files + + outputs = [ + "$root_gen_dir/dart-pkg/flutter_frontend_server/pubspec.yaml", + "$root_gen_dir/dart-pkg/vm/pubspec.yaml", + "$root_gen_dir/dart-pkg/build_integration/pubspec.yaml", + "$root_gen_dir/dart-pkg/front_end/pubspec.yaml", + "$root_gen_dir/dart-pkg/kernel/pubspec.yaml", + "$root_gen_dir/dart-pkg/dev_compiler/pubspec.yaml", + ] + + args = [ + "--input-root=" + rebase_path("//third_party/dart/pkg"), + "--output-root=" + rebase_path("$root_gen_dir/dart-pkg"), + "--frontend-server=" + rebase_path("$flutter_root"), + ] } diff --git a/shell/platform/fuchsia/BUILD.gn b/shell/platform/fuchsia/BUILD.gn index 5fc76f2f250bd..6838a2bedb0ef 100644 --- a/shell/platform/fuchsia/BUILD.gn +++ b/shell/platform/fuchsia/BUILD.gn @@ -45,8 +45,11 @@ if (using_fuchsia_sdk) { } _kernel_compiler_label = "dart:kernel_compiler($host_toolchain)" + _frontend_server_label = + "$flutter_root/flutter_frontend_server:frontend_server($host_toolchain)" deps = [ + _frontend_server_label, _gen_snapshot_to_use, _kernel_compiler_label, ] @@ -59,7 +62,12 @@ if (using_fuchsia_sdk) { rebase_path(get_label_info(_kernel_compiler_label, "root_gen_dir") + "/kernel_compiler.dart.snapshot") + _frontend_server_path = + rebase_path(get_label_info(_frontend_server_label, "root_gen_dir") + + "/frontend_server.dart.snapshot") + sources = [ + _frontend_server_path, _gen_snapshot_bin_path, _kernel_compiler_path, ] diff --git a/tools/fuchsia/build_fuchsia_artifacts.py b/tools/fuchsia/build_fuchsia_artifacts.py index ad1ae14ca48c2..181352160560e 100755 --- a/tools/fuchsia/build_fuchsia_artifacts.py +++ b/tools/fuchsia/build_fuchsia_artifacts.py @@ -110,6 +110,8 @@ def CopyGenSnapshotIfExists(source, destination): FindFileAndCopyTo('gen_snapshot_product', source_root, destination_base) FindFileAndCopyTo('kernel_compiler.dart.snapshot', source_root, destination_base, 'kernel_compiler.snapshot') + FindFileAndCopyTo('frontend_server.dart.snapshot', source_root, + destination_base, 'flutter_frontend_server.snapshot') def CopyFlutterTesterBinIfExists(source, destination): From 0832dfde5a50db2e27f9e5ff606e7ac5aad2c349 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 14 Nov 2019 15:31:37 -0800 Subject: [PATCH 138/591] [flow][fuchsia] Add more tracing to layers and Fuchsia surface pool (#13864) This adds more trace events to more layer operations and enhances the trace counters for the Fuchsia vulkan surface pool to include retained surface counts, emit stats on recycle events that might change the surface count, and by separating counters which measure bytes from counters which measure counts to make analysis simpler. --- flow/layers/child_scene_layer.cc | 1 + flow/layers/opacity_layer.cc | 1 + flow/layers/physical_shape_layer.cc | 4 ++ .../fuchsia/flutter/vulkan_surface_pool.cc | 38 +++++++++++++++---- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index f4100be2fee69..e5652a2c87889 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -18,6 +18,7 @@ ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, hit_testable_(hit_testable) {} void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll"); set_needs_system_composite(true); } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index a27981650d2bf..6257700ffbddf 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -34,6 +34,7 @@ void OpacityLayer::EnsureSingleChild() { } void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "OpacityLayer::Preroll"); EnsureSingleChild(); SkMatrix child_matrix = matrix; child_matrix.postTranslate(offset_.fX, offset_.fY); diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 21c9265ce00dd..0a607a88c23b0 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -52,6 +52,7 @@ PhysicalShapeLayer::~PhysicalShapeLayer() = default; void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); context->total_elevation += elevation_; total_elevation_ = context->total_elevation; SkRect child_paint_bounds; @@ -115,12 +116,14 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, void PhysicalShapeLayer::UpdateScene(SceneUpdateContext& context) { FML_DCHECK(needs_system_composite()); + TRACE_EVENT0("flutter", "PhysicalShapeLayer::UpdateScene"); // Retained rendering: speedup by reusing a retained entity node if possible. // When an entity node is reused, no paint layer is added to the frame so we // won't call PhysicalShapeLayer::Paint. LayerRasterCacheKey key(unique_id(), context.Matrix()); if (context.HasRetainedNode(key)) { + TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); const scenic::EntityNode& retained_node = context.GetRetainedNode(key); FML_DCHECK(context.top_entity()); FML_DCHECK(retained_node.session() == context.session()); @@ -128,6 +131,7 @@ void PhysicalShapeLayer::UpdateScene(SceneUpdateContext& context) { return; } + TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); // If we can't find an existing retained surface, create one. SceneUpdateContext::Frame frame(context, frameRRect_, color_, elevation_, total_elevation_, viewport_depth_, this); diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc b/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc index efba90be02888..38ea0a0b51b14 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc @@ -49,6 +49,7 @@ std::unique_ptr VulkanSurfacePool::AcquireSurface( std::unique_ptr VulkanSurfacePool::GetCachedOrCreateSurface( const SkISize& size) { + TRACE_EVENT0("flutter", "VulkanSurfacePool::GetCachedOrCreateSurface"); // First try to find a surface that exactly matches |size|. { auto exact_match_it = @@ -59,6 +60,7 @@ std::unique_ptr VulkanSurfacePool::GetCachedOrCreateSurface( if (exact_match_it != available_surfaces_.end()) { auto acquired_surface = std::move(*exact_match_it); available_surfaces_.erase(exact_match_it); + TRACE_EVENT_INSTANT0("flutter", "Exact match found"); return acquired_surface; } } @@ -88,6 +90,7 @@ std::unique_ptr VulkanSurfacePool::GetCachedOrCreateSurface( // If no such surface exists, then create a new one. if (best_it == available_surfaces_.end()) { + TRACE_EVENT_INSTANT0("flutter", "No available surfaces"); return CreateSurface(size); } @@ -98,8 +101,10 @@ std::unique_ptr VulkanSurfacePool::GetCachedOrCreateSurface( if (!swap_succeeded) { FML_DLOG(ERROR) << "Failed to swap VulkanSurface to new VkImage of size: " << ToString(size); + TRACE_EVENT_INSTANT0("flutter", "failed to swap, making new"); return CreateSurface(size); } + TRACE_EVENT_INSTANT0("flutter", "Using differently sized image"); FML_DCHECK(acquired_surface->IsValid()); trace_surfaces_reused_++; return acquired_surface; @@ -122,6 +127,7 @@ void VulkanSurfacePool::SubmitSurface( const flutter::LayerRasterCacheKey& retained_key = vulkan_surface->GetRetainedKey(); + if (retained_key.id() != 0) { // Add the surface to |retained_surfaces_| if its retained key has a valid // layer id (|retained_key.id()|). @@ -190,11 +196,15 @@ void VulkanSurfacePool::RecycleSurface(std::unique_ptr surface) { return; } + TRACE_EVENT0("flutter", "VulkanSurfacePool::RecycleSurface"); // Recycle the buffer by putting it in the list of available surfaces if we // have not reached the maximum amount of cached surfaces. if (available_surfaces_.size() < kMaxSurfaces) { available_surfaces_.push_back(std::move(surface)); + } else { + TRACE_EVENT_INSTANT0("flutter", "Too many surfaces in pool, dropping"); } + TraceStats(); } void VulkanSurfacePool::RecycleRetainedSurface( @@ -220,6 +230,7 @@ void VulkanSurfacePool::AgeAndCollectOldBuffers() { TRACE_EVENT0("flutter", "VulkanSurfacePool::AgeAndCollectOldBuffers"); // Remove all surfaces that are no longer valid or are too old. + size_t size_before = available_surfaces_.size(); available_surfaces_.erase( std::remove_if(available_surfaces_.begin(), available_surfaces_.end(), [&](auto& surface) { @@ -227,6 +238,8 @@ void VulkanSurfacePool::AgeAndCollectOldBuffers() { surface->AdvanceAndGetAge() >= kMaxSurfaceAge; }), available_surfaces_.end()); + TRACE_EVENT1("flutter", "AgeAndCollect", "aged surfaces", + (size_before - available_surfaces_.size())); // Look for a surface that has both a larger |VkDeviceMemory| allocation // than is necessary for its |VkImage|, and has a stable size history. @@ -238,6 +251,7 @@ void VulkanSurfacePool::AgeAndCollectOldBuffers() { // If we found such a surface, then destroy it and cache a new one that only // uses a necessary amount of memory. if (surface_to_remove_it != available_surfaces_.end()) { + TRACE_EVENT_INSTANT0("flutter", "replacing surface with smaller one"); auto size = (*surface_to_remove_it)->GetSize(); available_surfaces_.erase(surface_to_remove_it); auto new_surface = CreateSurface(size); @@ -271,6 +285,7 @@ void VulkanSurfacePool::AgeAndCollectOldBuffers() { } void VulkanSurfacePool::ShrinkToFit() { + TRACE_EVENT0("flutter", "VulkanSurfacePool::ShrinkToFit"); // Reset all oversized surfaces in |available_surfaces_| so that the old // surfaces and new surfaces don't exist at the same time at any point, // reducing our peak memory footprint. @@ -298,13 +313,16 @@ void VulkanSurfacePool::ShrinkToFit() { void VulkanSurfacePool::TraceStats() { // Resources held in cached buffers. - size_t cached_surfaces = 0; size_t cached_surfaces_bytes = 0; + size_t retained_surfaces_bytes = 0; for (const auto& surface : available_surfaces_) { - cached_surfaces++; cached_surfaces_bytes += surface->GetAllocationSize(); } + for (const auto& retained_entry : retained_surfaces_) { + retained_surfaces_bytes += + retained_entry.second.vk_surface->GetAllocationSize(); + } // Resources held by Skia. int skia_resources = 0; @@ -313,16 +331,20 @@ void VulkanSurfacePool::TraceStats() { const size_t skia_cache_purgeable = context_->getResourceCachePurgeableBytes(); - TRACE_COUNTER("flutter", "SurfacePool", 0u, // - "CachedCount", cached_surfaces, // - "CachedBytes", cached_surfaces_bytes, // + TRACE_COUNTER("flutter", "SurfacePoolCounts", 0u, "CachedCount", + available_surfaces_.size(), // "Created", trace_surfaces_created_, // "Reused", trace_surfaces_reused_, // "PendingInCompositor", pending_surfaces_.size(), // "Retained", retained_surfaces_.size(), // - "SkiaCacheResources", skia_resources, // - "SkiaCacheBytes", skia_bytes, // - "SkiaCachePurgeable", skia_cache_purgeable // + "SkiaCacheResources", skia_resources // + ); + + TRACE_COUNTER("flutter", "SurfacePoolBytes", 0u, // + "CachedBytes", cached_surfaces_bytes, // + "RetainedBytes", retained_surfaces_bytes, // + "SkiaCacheBytes", skia_bytes, // + "SkiaCachePurgeable", skia_cache_purgeable // ); // Reset per present/frame stats. From 141dc785dec91eb22fb52122fbbfa47a7d18e650 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 14 Nov 2019 18:39:52 -0500 Subject: [PATCH 139/591] Roll src/third_party/skia e57ca4931952..c1c4634dcb07 (15 commits) (#13866) https://skia.googlesource.com/skia.git/+log/e57ca4931952..c1c4634dcb07 git log e57ca4931952..c1c4634dcb07 --date=short --no-merges --format='%ad %ae %s' 2019-11-14 reed@google.com add some api helpers (extracted from different CL) 2019-11-14 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-14 benjaminwagner@google.com Use Clang for CMake build 2019-11-14 herb@google.com Hoist regenGlyphs out of per glyph loop 2019-11-14 rmistry@google.com Specify only luci buckets instead of hardcoding trybot names 2019-11-14 ahujabharat93@gmail.com Issue- >Skia tiled bitmap rendering is too slow when tildmodeX is not same as tileModeY. Reason-> Skia uses general-purpose pipeline in this case instead of more optimized faster pipeline. Faster pipeline code was available in older versions of Skia and used to work fine but we removed faster pipeline code to consolidate the code. As discussed offline for us in the office we have significant scenarios running on software, so maintaining software performance is very important for us thus I am bringing this fast path back. To make this work I didn't have to do much, everything was already available, I just had to modify MatirxProcs to accept tilemodex and tilemodeY as parameters and define and configure SkBitmapProcState::MatrixProc GeneralProcs. I have also limited this change to ARM devices 2019-11-14 jvanverth@google.com Reduce scaling of large SDF glyphs on Mac. 2019-11-14 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-14 csmartdalton@google.com Revert "Reland "Reland "Implement sample mask and sample locations support in Vulkan""" 2019-11-14 kjlubick@google.com [canvaskit] Expose a few more methods 2019-11-14 fmalita@chromium.org [skottie] One-node camera support 2019-11-14 robertphillips@google.com Respect max index buffer sizes in GrFillRectOp::MakeSet 2019-11-14 csmartdalton@google.com Reland "Reland "Implement sample mask and sample locations support in Vulkan"" 2019-11-14 nigeltao@google.com Reset SkWuffsCodec frame-count decoder less often 2019-11-14 bsalomon@google.com Fixed sampling non-SkSL FPs with coordinates Created with: gclient setdep -r src/third_party/skia@c1c4634dcb07 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d3fbca4204bf3..42274f6712319 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e57ca49319522930e1342be90362ccf7cb4e9214', + 'skia_revision': 'c1c4634dcb0713b11610feb52d2fa6ff84b6496f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a84041ed3b8ae..66002d1128733 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 374b740d431aaf794c94eb97ecf5327c +Signature: e60b80d6d57808efd5949efa6cdf57e2 UNUSED LICENSES: From 90a6054a878d1bc3b5a263bf07211fa9d5e06590 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 14 Nov 2019 17:05:24 -0800 Subject: [PATCH 140/591] Revert "Roll src/third_party/dart dc35290111..dc808f3fcb (5 commits) (#13859)" (#13867) This reverts commit 174e0e9150e274df6617913220dd9c0943f20d6f. Dart commit 45033c6ad9725be939f2baa019301f702efc694c was causing problems for the engine->flutter roll and it will be fixed when dart commit ef5fb7d3f456123262ddee3f26a7a20276096fc6 lands. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 42274f6712319..fd071cdfda91c 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'dc808f3fcbf7e6de7e2b25441ff7ed891362e70a', + 'dart_revision': 'dc35290111c03b29f309029f499f08fbd9e667e4', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 536280fd89ab0..f882ad6f08cc7 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 20f0c52cba7e0b76e6053ed0c2838e45 +Signature: ca2f5a478a350df271bc591b2a64d1e6 UNUSED LICENSES: From cb8d7bc569b513c45c9e4adf2509883b37ad5f14 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Thu, 14 Nov 2019 19:12:27 -0800 Subject: [PATCH 141/591] Allow passing hot reload debugging flags through dart-flags (#13780) --- shell/common/switches.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/common/switches.cc b/shell/common/switches.cc index df2e51d280ca2..6351d4c5a663a 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -56,6 +56,8 @@ static const std::string gDartFlagsWhitelist[] = { "--write-service-info", "--sample-buffer-duration", "--no-causal_async_stacks", + "--trace-reload", + "--trace-reload-verbose", }; // clang-format on From cd1b25a09e69e9ffb758fbc8d8d0467de019c838 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 14 Nov 2019 22:34:09 -0500 Subject: [PATCH 142/591] Roll src/third_party/skia c1c4634dcb07..3fafc831bc15 (1 commits) (#13870) https://skia.googlesource.com/skia.git/+log/c1c4634dcb07..3fafc831bc15 git log c1c4634dcb07..3fafc831bc15 --date=short --no-merges --format='%ad %ae %s' 2019-11-14 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@3fafc831bc15 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index fd071cdfda91c..a3dfd2c417e55 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c1c4634dcb0713b11610feb52d2fa6ff84b6496f', + 'skia_revision': '3fafc831bc15c7a456a9726ca129610017e24cde', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 66002d1128733..f372d91b3758f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: e60b80d6d57808efd5949efa6cdf57e2 +Signature: a9f96838c2a6327bc6e3734408e25ca5 UNUSED LICENSES: From be139a5b94b38fa2e0f9c6efd326fce9c2380dbc Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 15 Nov 2019 02:27:01 -0500 Subject: [PATCH 143/591] Roll src/third_party/skia 3fafc831bc15..f7281db422b7 (4 commits) (#13871) https://skia.googlesource.com/skia.git/+log/3fafc831bc15..f7281db422b7 git log 3fafc831bc15..f7281db422b7 --date=short --no-merges --format='%ad %ae %s' 2019-11-15 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 1b52f05868c9..5f4db6a76640 (9 commits) 2019-11-15 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 7ca6de3adb8e..6961018759e7 (8341 commits) 2019-11-15 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader b2407dd746de..102fd19c65f4 (4 commits) 2019-11-15 csmartdalton@google.com Reland "Reland "Reland "Implement sample mask and sample locations support in Vulkan""" Created with: gclient setdep -r src/third_party/skia@f7281db422b7 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a3dfd2c417e55..30726d73438c4 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3fafc831bc15c7a456a9726ca129610017e24cde', + 'skia_revision': 'f7281db422b7472d128510cd264b98e6891a2961', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f372d91b3758f..9a69f53d017ee 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: a9f96838c2a6327bc6e3734408e25ca5 +Signature: 3af9f51fb5ac2636536d9789a47aa7d1 UNUSED LICENSES: From a8218a901ec81def13c37c49b3982559ad236531 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 15 Nov 2019 06:12:51 -0500 Subject: [PATCH 144/591] Roll fuchsia/sdk/core/mac-amd64 from VMTIz... to RI85D... (#13873) Roll fuchsia/sdk/core/mac-amd64 from VMTIz... to RI85D... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 30726d73438c4..b6b51d8acb8e9 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'VMTIzrEWwZ-aY_TNqL6fkCX7mTKnVqenKC5jzL1cuKIC' + 'version': 'RI85DcYL8JEoFsjAyJH62YB8CvhYOu-3tto-nj8ZHlIC' } ], 'condition': 'host_os == "mac"', From cc2fe6be9e42fe329535c7f8ec2cab42d0315e55 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 15 Nov 2019 09:26:17 -0500 Subject: [PATCH 145/591] Roll src/third_party/skia f7281db422b7..b3d3551577a3 (1 commits) (#13874) https://skia.googlesource.com/skia.git/+log/f7281db422b7..b3d3551577a3 git log f7281db422b7..b3d3551577a3 --date=short --no-merges --format='%ad %ae %s' 2019-11-15 robertphillips@google.com Revert "Reland "Reland "Reland "Implement sample mask and sample locations support in Vulkan"""" Created with: gclient setdep -r src/third_party/skia@b3d3551577a3 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b6b51d8acb8e9..300c3aefef6d0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f7281db422b7472d128510cd264b98e6891a2961', + 'skia_revision': 'b3d3551577a3cf4171162448d687b7322feb4bb7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 9a69f53d017ee..ce057b3b2d5c0 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 3af9f51fb5ac2636536d9789a47aa7d1 +Signature: 6ef34ac530e1481fd260f864c33f1fa5 UNUSED LICENSES: From 096ba6892a435a9149b0406592eab7ba866bceb8 Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Fri, 15 Nov 2019 09:22:26 -0800 Subject: [PATCH 146/591] Reland children isolates sharing isolate group change. (#13758) * Revert "Revert "Provide dart vm initalize isolate callback so that children isolates belong to parent's isolate group. (#9888)" (#12327)" * Ensure that when isolate shuts down it calls isolate_data, rather than isolage_group_data callback. --- runtime/dart_isolate.cc | 179 +++++++++++++++++++++++------- runtime/dart_isolate.h | 35 ++++-- runtime/dart_isolate_unittests.cc | 5 + runtime/dart_vm.cc | 5 + 4 files changed, 178 insertions(+), 46 deletions(-) diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 50f753216e8c5..f6bc1f695ed5f 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -68,7 +68,9 @@ std::weak_ptr DartIsolate::CreateRootIsolate( advisory_script_entrypoint, // advisory entrypoint nullptr, // child isolate preparer isolate_create_callback, // isolate create callback - isolate_shutdown_callback // isolate shutdown callback + isolate_shutdown_callback, // isolate shutdown callback + true, // is_root_isolate + true // is_group_root_isolate ))); std::tie(vm_isolate, embedder_isolate) = CreateDartVMAndEmbedderObjectPair( @@ -113,7 +115,9 @@ DartIsolate::DartIsolate(const Settings& settings, std::string advisory_script_entrypoint, ChildIsolatePreparer child_isolate_preparer, fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback) + fml::closure isolate_shutdown_callback, + bool is_root_isolate, + bool is_group_root_isolate) : UIDartState(std::move(task_runners), settings.task_observer_add, settings.task_observer_remove, @@ -130,7 +134,9 @@ DartIsolate::DartIsolate(const Settings& settings, isolate_snapshot_(std::move(isolate_snapshot)), child_isolate_preparer_(std::move(child_isolate_preparer)), isolate_create_callback_(isolate_create_callback), - isolate_shutdown_callback_(isolate_shutdown_callback) { + isolate_shutdown_callback_(isolate_shutdown_callback), + is_root_isolate_(is_root_isolate), + is_group_root_isolate_(is_group_root_isolate) { FML_DCHECK(isolate_snapshot_) << "Must contain a valid isolate snapshot."; phase_ = Phase::Uninitialized; } @@ -152,7 +158,7 @@ std::string DartIsolate::GetServiceId() { return service_id; } -bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { +bool DartIsolate::Initialize(Dart_Isolate dart_isolate) { TRACE_EVENT0("flutter", "DartIsolate::Initialize"); if (phase_ != Phase::Uninitialized) { return false; @@ -166,12 +172,6 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { return false; } - auto* isolate_data = static_cast*>( - Dart_IsolateGroupData(dart_isolate)); - if (isolate_data->get() != this) { - return false; - } - // After this point, isolate scopes can be safely used. SetIsolate(dart_isolate); @@ -183,8 +183,7 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { tonic::DartIsolateScope scope(isolate()); - SetMessageHandlingTaskRunner(GetTaskRunners().GetUITaskRunner(), - is_root_isolate); + SetMessageHandlingTaskRunner(GetTaskRunners().GetUITaskRunner()); if (tonic::LogIfError( Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))) { @@ -204,9 +203,8 @@ fml::RefPtr DartIsolate::GetMessageHandlingTaskRunner() const { } void DartIsolate::SetMessageHandlingTaskRunner( - fml::RefPtr runner, - bool is_root_isolate) { - if (!is_root_isolate || !runner) { + fml::RefPtr runner) { + if (!IsRootIsolate() || !runner) { return; } @@ -255,7 +253,7 @@ bool DartIsolate::UpdateThreadPoolNames() const { return true; } -bool DartIsolate::LoadLibraries(bool is_root_isolate) { +bool DartIsolate::LoadLibraries() { TRACE_EVENT0("flutter", "DartIsolate::LoadLibraries"); if (phase_ != Phase::Initialized) { return false; @@ -265,11 +263,11 @@ bool DartIsolate::LoadLibraries(bool is_root_isolate) { DartIO::InitForIsolate(); - DartUI::InitForIsolate(is_root_isolate); + DartUI::InitForIsolate(IsRootIsolate()); const bool is_service_isolate = Dart_IsServiceIsolate(isolate()); - DartRuntimeHooks::Install(is_root_isolate && !is_service_isolate, + DartRuntimeHooks::Install(IsRootIsolate() && !is_service_isolate, GetAdvisoryScriptURI()); if (!is_service_isolate) { @@ -650,6 +648,7 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( Dart_IsolateFlags* flags, std::shared_ptr* parent_embedder_isolate, char** error) { + TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCreateCallback"); if (parent_embedder_isolate == nullptr && strcmp(advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { // The VM attempts to start the VM service for us on |Dart_Initialize|. In @@ -677,6 +676,59 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( .first; } +// |Dart_IsolateInitializeCallback| +bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, + char** error) { + TRACE_EVENT0("flutter", "DartIsolate::DartIsolateInitializeCallback"); + Dart_Isolate isolate = Dart_CurrentIsolate(); + if (isolate == nullptr) { + *error = strdup("Isolate should be available in initialize callback."); + FML_DLOG(ERROR) << *error; + return false; + } + + auto* root_embedder_isolate = static_cast*>( + Dart_CurrentIsolateGroupData()); + + TaskRunners null_task_runners( + (*root_embedder_isolate)->GetAdvisoryScriptURI(), + /* platform= */ nullptr, /* gpu= */ nullptr, + /* ui= */ nullptr, + /* io= */ nullptr); + + auto embedder_isolate = std::make_unique>( + std::shared_ptr(new DartIsolate( + (*root_embedder_isolate)->GetSettings(), // settings + (*root_embedder_isolate)->GetIsolateSnapshot(), // isolate_snapshot + null_task_runners, // task_runners + fml::WeakPtr{}, // snapshot_delegate + fml::WeakPtr{}, // io_manager + fml::RefPtr{}, // unref_queue + fml::WeakPtr{}, // image_decoder + (*root_embedder_isolate) + ->GetAdvisoryScriptURI(), // advisory_script_uri + (*root_embedder_isolate) + ->GetAdvisoryScriptEntrypoint(), // advisory_script_entrypoint + (*root_embedder_isolate)->child_isolate_preparer_, // preparer + (*root_embedder_isolate)->isolate_create_callback_, // on create + (*root_embedder_isolate)->isolate_shutdown_callback_, // on shutdown + false, // is_root_isolate + false))); // is_group_root_isolate + + // root isolate should have been created via CreateRootIsolate and + // CreateDartVMAndEmbedderObjectPair + if (!InitializeIsolate(*embedder_isolate, isolate, error)) { + return false; + } + + // The ownership of the embedder object is controlled by the Dart VM. So the + // only reference returned to the caller is weak. + *child_callback_data = embedder_isolate.release(); + + Dart_EnterIsolate(isolate); + return true; +} + std::pair> DartIsolate::CreateDartVMAndEmbedderObjectPair( const char* advisory_script_uri, @@ -717,11 +769,11 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( fml::WeakPtr{}, // image_decoder advisory_script_uri, // advisory_script_uri advisory_script_entrypoint, // advisory_script_entrypoint - (*raw_embedder_isolate)->child_isolate_preparer_, // preparer - (*raw_embedder_isolate)->isolate_create_callback_, // on create - (*raw_embedder_isolate)->isolate_shutdown_callback_ // on shutdown - )) - + (*raw_embedder_isolate)->child_isolate_preparer_, // preparer + (*raw_embedder_isolate)->isolate_create_callback_, // on create + (*raw_embedder_isolate)->isolate_shutdown_callback_, // on shutdown + is_root_isolate, + true)) // is_root_group_isolate ); } @@ -741,38 +793,53 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( return {nullptr, {}}; } - if (!(*embedder_isolate)->Initialize(isolate, is_root_isolate)) { + if (!InitializeIsolate(*embedder_isolate, isolate, error)) { + return {nullptr, {}}; + } + + auto* isolate_data = static_cast*>( + Dart_IsolateGroupData(isolate)); + FML_DCHECK(isolate_data->get() == embedder_isolate->get()); + + auto weak_embedder_isolate = (*embedder_isolate)->GetWeakIsolatePtr(); + + // The ownership of the embedder object is controlled by the Dart VM. So the + // only reference returned to the caller is weak. + embedder_isolate.release(); + return {isolate, weak_embedder_isolate}; +} + +bool DartIsolate::InitializeIsolate( + std::shared_ptr embedder_isolate, + Dart_Isolate isolate, + char** error) { + TRACE_EVENT0("flutter", "DartIsolate::InitializeIsolate"); + if (!embedder_isolate->Initialize(isolate)) { *error = strdup("Embedder could not initialize the Dart isolate."); FML_DLOG(ERROR) << *error; - return {nullptr, {}}; + return false; } - if (!(*embedder_isolate)->LoadLibraries(is_root_isolate)) { + if (!embedder_isolate->LoadLibraries()) { *error = strdup("Embedder could not load libraries in the new Dart isolate."); FML_DLOG(ERROR) << *error; - return {nullptr, {}}; + return false; } - auto weak_embedder_isolate = (*embedder_isolate)->GetWeakIsolatePtr(); - // Root isolates will be setup by the engine and the service isolate (which is // also a root isolate) by the utility routines in the VM. However, secondary // isolates will be run by the VM if they are marked as runnable. - if (!is_root_isolate) { - FML_DCHECK((*embedder_isolate)->child_isolate_preparer_); - if (!(*embedder_isolate) - ->child_isolate_preparer_((*embedder_isolate).get())) { + if (!embedder_isolate->IsRootIsolate()) { + FML_DCHECK(embedder_isolate->child_isolate_preparer_); + if (!embedder_isolate->child_isolate_preparer_(embedder_isolate.get())) { *error = strdup("Could not prepare the child isolate to run."); FML_DLOG(ERROR) << *error; - return {nullptr, {}}; + return false; } } - // The ownership of the embedder object is controlled by the Dart VM. So the - // only reference returned to the caller is weak. - embedder_isolate.release(); - return {isolate, weak_embedder_isolate}; + return true; } // |Dart_IsolateShutdownCallback| @@ -783,12 +850,46 @@ void DartIsolate::DartIsolateShutdownCallback( FML_DLOG(INFO) << "DartIsolateShutdownCallback" << " isolate_group_data " << isolate_group_data << " isolate_data " << isolate_data; - isolate_group_data->get()->OnShutdownCallback(); + isolate_data->get()->OnShutdownCallback(); } // |Dart_IsolateGroupCleanupCallback| void DartIsolate::DartIsolateGroupCleanupCallback( std::shared_ptr* isolate_data) { + TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCleanupCallback"); + FML_DLOG(INFO) << "DartIsolateGroupCleanupCallback isolate_data " + << isolate_data; + + delete isolate_data; +} + +// |Dart_IsolateCleanupCallback| +void DartIsolate::DartIsolateCleanupCallback( + std::shared_ptr* isolate_group_data, + std::shared_ptr* isolate_data) { + TRACE_EVENT0("flutter", "DartIsolate::DartIsolateCleanupCallback"); + + if ((*isolate_data)->IsRootIsolate()) { + // isolate_data will be cleaned up as part of IsolateGroup cleanup + FML_DLOG(INFO) + << "DartIsolateCleanupCallback no-op for root isolate isolate_data " + << isolate_data; + return; + } + if ((*isolate_data)->IsGroupRootIsolate()) { + // Even if isolate was not a root isolate(i.e. was spawned), + // it might have IsolateGroup created for it (when + // --no-enable-isolate-groups dart vm flag is used). + // Then its isolate_data will be cleaned up as part of IsolateGroup + // cleanup as well. + FML_DLOG(INFO) << "DartIsolateCleanupCallback no-op for group root isolate " + "isolate_data " + << isolate_data; + return; + } + + FML_DLOG(INFO) << "DartIsolateCleanupCallback cleaned up isolate_data " + << isolate_data; delete isolate_data; } diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index 7def5adc85668..fa26c071fa764 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -401,6 +401,13 @@ class DartIsolate : public UIDartState { /// fml::RefPtr GetMessageHandlingTaskRunner() const; + // Root isolate of the VM application + bool IsRootIsolate() const { return is_root_isolate_; } + // Isolate that owns IsolateGroup it lives in. + // When --no-enable-isolate-groups dart vm flag is set, + // all child isolates will have their own IsolateGroups. + bool IsGroupRootIsolate() const { return is_group_root_isolate_; } + private: using ChildIsolatePreparer = std::function; @@ -425,6 +432,8 @@ class DartIsolate : public UIDartState { fml::RefPtr message_handling_task_runner_; const fml::closure isolate_create_callback_; const fml::closure isolate_shutdown_callback_; + const bool is_root_isolate_; + const bool is_group_root_isolate_; DartIsolate(const Settings& settings, fml::RefPtr isolate_snapshot, @@ -437,18 +446,17 @@ class DartIsolate : public UIDartState { std::string advisory_script_entrypoint, ChildIsolatePreparer child_isolate_preparer, fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback); - - FML_WARN_UNUSED_RESULT bool Initialize(Dart_Isolate isolate, - bool is_root_isolate); + fml::closure isolate_shutdown_callback, + bool is_root_isolate, + bool is_group_root_isolate); + FML_WARN_UNUSED_RESULT bool Initialize(Dart_Isolate isolate); - void SetMessageHandlingTaskRunner(fml::RefPtr runner, - bool is_root_isolate); + void SetMessageHandlingTaskRunner(fml::RefPtr runner); bool LoadKernel(std::shared_ptr mapping, bool last_piece); FML_WARN_UNUSED_RESULT - bool LoadLibraries(bool is_root_isolate); + bool LoadLibraries(); bool UpdateThreadPoolNames() const; @@ -467,6 +475,10 @@ class DartIsolate : public UIDartState { std::shared_ptr* embedder_isolate, char** error); + // |Dart_IsolateInitializeCallback| + static bool DartIsolateInitializeCallback(void** child_callback_data, + char** error); + static Dart_Isolate DartCreateAndStartServiceIsolate( const char* package_root, const char* package_config, @@ -485,11 +497,20 @@ class DartIsolate : public UIDartState { bool is_root_isolate, char** error); + static bool InitializeIsolate(std::shared_ptr embedder_isolate, + Dart_Isolate isolate, + char** error); + // |Dart_IsolateShutdownCallback| static void DartIsolateShutdownCallback( std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data); + // |Dart_IsolateCleanupCallback| + static void DartIsolateCleanupCallback( + std::shared_ptr* isolate_group_data, + std::shared_ptr* isolate_data); + // |Dart_IsolateGroupCleanupCallback| static void DartIsolateGroupCleanupCallback( std::shared_ptr* isolate_group_data); diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc index 5dd39c5ebcc87..33b60ae26eefc 100644 --- a/runtime/dart_isolate_unittests.cc +++ b/runtime/dart_isolate_unittests.cc @@ -364,6 +364,7 @@ TEST_F(DartIsolateTest, CanSaveCompilationTrace) { TEST_F(DartIsolateTest, CanLaunchSecondaryIsolates) { fml::CountDownLatch latch(3); fml::AutoResetWaitableEvent child_shutdown_latch; + fml::AutoResetWaitableEvent root_isolate_shutdown_latch; AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) { latch.CountDown(); @@ -376,6 +377,9 @@ TEST_F(DartIsolateTest, CanLaunchSecondaryIsolates) { latch.CountDown(); }))); auto settings = CreateSettingsForFixture(); + settings.root_isolate_shutdown_callback = [&root_isolate_shutdown_latch]() { + root_isolate_shutdown_latch.Signal(); + }; settings.isolate_shutdown_callback = [&child_shutdown_latch]() { child_shutdown_latch.Signal(); }; @@ -385,6 +389,7 @@ TEST_F(DartIsolateTest, CanLaunchSecondaryIsolates) { ASSERT_TRUE(isolate); ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); child_shutdown_latch.Wait(); // wait for child isolate to shutdown first + ASSERT_FALSE(root_isolate_shutdown_latch.IsSignaledForTest()); latch.Wait(); // wait for last NotifyNative called by main isolate // root isolate will be auto-shutdown } diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index 623242bdff601..652bf2804aca5 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -391,9 +391,14 @@ DartVM::DartVM(std::shared_ptr vm_data, vm_data_->GetVMSnapshot().GetInstructionsMapping(); params.create_group = reinterpret_cast( DartIsolate::DartIsolateGroupCreateCallback); + params.initialize_isolate = + reinterpret_cast( + DartIsolate::DartIsolateInitializeCallback); params.shutdown_isolate = reinterpret_cast( DartIsolate::DartIsolateShutdownCallback); + params.cleanup_isolate = reinterpret_cast( + DartIsolate::DartIsolateCleanupCallback); params.cleanup_group = reinterpret_cast( DartIsolate::DartIsolateGroupCleanupCallback); params.thread_exit = ThreadExitCallback; From b2640d97e7e8034f28b4e7b92c15b0824e433897 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 15 Nov 2019 10:21:59 -0800 Subject: [PATCH 147/591] Roll src/third_party/dart ebd9af9043..8e176998b4 (10 commits) (#13875) dart-lang/sdk@8e176998b4 [cfe] Support late lowering of instance fields dart-lang/sdk@09d75d94e7 [cfe] Avoid use of #isSet# field on non-nullabel late fields dart-lang/sdk@45996ee170 [cfe] Add late field lowering for static/top-level fields dart-lang/sdk@49aaed295f [cfe] Refactor field type inference dart-lang/sdk@70892fff0c nnbd preview tool: text for uninitialized field dart-lang/sdk@fea5e1b741 Fix UNUSED_MEMBER check for cross-class private class members dart-lang/sdk@bd43ef8144 Migration: stop using "never" for the result type of an "is" check dart-lang/sdk@9cb0b678ce Migration: remove more explicit uses of never from EdgeBuilder. dart-lang/sdk@f9175f2099 Remove undetected unused field in ClosedEntityWriter dart-lang/sdk@a75ffc8956 Add non-NNBD language version marker to all sdk (non-nnbd) files --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 300c3aefef6d0..eecee1129d08a 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'dc35290111c03b29f309029f499f08fbd9e667e4', + 'dart_revision': '8e176998b45fb24f7f18f6e3d23d6fc24a0b0478', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -63,7 +63,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.15.7', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.103', + 'dart_linter_tag': '0.1.104', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.3', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index f882ad6f08cc7..df469109d328a 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: ca2f5a478a350df271bc591b2a64d1e6 +Signature: b65afd8a78fefe437cffac6ec13437b3 UNUSED LICENSES: From 38a440cc0fadddb0a0713a6b94ecb0ac2d0441fd Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 15 Nov 2019 13:27:23 -0500 Subject: [PATCH 148/591] Roll src/third_party/skia b3d3551577a3..1496758da42e (5 commits) (#13876) https://skia.googlesource.com/skia.git/+log/b3d3551577a3..1496758da42e git log b3d3551577a3..1496758da42e --date=short --no-merges --format='%ad %ae %s' 2019-11-15 kjlubick@google.com [canvaskit] Request an 8 bit stencil buffer from the WebGL 1/2 context 2019-11-15 robertphillips@google.com Respect the max indexBuffer limits in the bulk texture draw API (take 2) 2019-11-15 kjlubick@google.com [canvaskit] Swap font resolution order for emoji test 2019-11-15 herb@google.com Remove indexes from Geometry in the text op 2019-11-15 jvanverth@google.com Add flag to Viewer to trigger continuous redraw. Created with: gclient setdep -r src/third_party/skia@1496758da42e If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index eecee1129d08a..1678fdc0bdc2b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'b3d3551577a3cf4171162448d687b7322feb4bb7', + 'skia_revision': '1496758da42e1f186be61ac3c340018ac338d65c', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index ce057b3b2d5c0..f1dbc6dbdb15a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6ef34ac530e1481fd260f864c33f1fa5 +Signature: 76f98515c74adaad2c0ac623b724e2ed UNUSED LICENSES: From b3463e3580bcc17c4bf76e63f7fe89ae05bd04f5 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 15 Nov 2019 15:14:03 -0800 Subject: [PATCH 149/591] getLineBoundary edge condition use <= instead of < (#13881) --- lib/ui/text/paragraph.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 5680735b6019b..2618c3f08406a 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -140,7 +140,7 @@ Dart_Handle Paragraph::getLineBoundary(unsigned offset) { int line_start = -1; int line_end = -1; for (txt::LineMetrics& line : metrics) { - if (offset >= line.start_index && offset < line.end_index) { + if (offset >= line.start_index && offset <= line.end_index) { line_start = line.start_index; line_end = line.end_index; break; From 9ab2b58a672e0124c50c975549aab9ecb31c6543 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 15 Nov 2019 13:00:11 -0800 Subject: [PATCH 150/591] Roll src/third_party/dart 8e176998b4..fdb9d19826 (5 commits) dart-lang/sdk@fdb9d19826 Added aliases and nonfunction to kernel spell check lists dart-lang/sdk@03c3eff5dd [vm] Support JIT exception handling in DART_PRECOMPILER mode dart-lang/sdk@5f18b77d39 [vm/reload] Disable vm/dart/isolates/spawn_function_test/0 until full IG reloading is implemented dart-lang/sdk@20962e656b [vm/concurrency] Split up IsolateReloadContext into IsolateReloadContext/IsolateGroupReloadContext dart-lang/sdk@c37ca05357 [CFE] Plug memory leak in incremental compiler --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1678fdc0bdc2b..a7426ca177409 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8e176998b45fb24f7f18f6e3d23d6fc24a0b0478', + 'dart_revision': 'fdb9d19826b9fd328d168e6ff9f1c582abdd1594', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index df469109d328a..8129e4045711a 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: b65afd8a78fefe437cffac6ec13437b3 +Signature: a0f67487ad70add100b4af41c6bfbb76 UNUSED LICENSES: From 486e62d02a2a9e350b40ac3cf3fe8fe15f084a15 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 15 Nov 2019 19:02:22 -0500 Subject: [PATCH 151/591] Roll src/third_party/skia 1496758da42e..541f310b3482 (12 commits) (#13880) https://skia.googlesource.com/skia.git/+log/1496758da42e..541f310b3482 git log 1496758da42e..541f310b3482 --date=short --no-merges --format='%ad %ae %s' 2019-11-15 kjlubick@google.com [canvaskit] Fix primitive_shaper build 2019-11-15 reed@google.com trim out unneeded references to SkString 2019-11-15 reed@google.com Ummm, why are we including SkUTF.h? 2019-11-15 robertphillips@google.com Add ClockwiseTestOp::onPrePrepare 2019-11-15 kjlubick@google.com [canvaskit] Expose more SkParagraph methods 2019-11-15 kjlubick@google.com [canvaskit] Add MatrixTransform ImageFilter 2019-11-15 borenet@google.com [infra] Fix bin/try 2019-11-15 bsalomon@google.com fix issue introduced in f29caf1433e3185df01b4a286d0fc9715ad32ae2 where kClamp domain mode is always used. 2019-11-15 csmartdalton@google.com Reland "Reland "Reland "Reland "Implement sample mask and sample locations support in Vulkan"""" 2019-11-15 robertphillips@google.com Reduce max # of AA Quads that can be merged or chained into a single GrTextureOp (take 2) 2019-11-15 reed@google.com remove unused (by clients) SkMultiPictureDraw 2019-11-15 bsalomon@google.com Replace GrTextureDomainEffect with GrDomainEffect. Created with: gclient setdep -r src/third_party/skia@541f310b3482 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index a7426ca177409..937c0f87c25cb 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '1496758da42e1f186be61ac3c340018ac338d65c', + 'skia_revision': '541f310b348296c1e2cdef182a3098d9ca5e1795', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f1dbc6dbdb15a..3c54d796bff7a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 76f98515c74adaad2c0ac623b724e2ed +Signature: 4d7c8c6647cb2443e468da2d73f41706 UNUSED LICENSES: @@ -2181,7 +2181,6 @@ FILE: ../../../third_party/skia/gm/imagefilterscropexpand.cpp FILE: ../../../third_party/skia/gm/imagefiltersscaled.cpp FILE: ../../../third_party/skia/gm/imageresizetiled.cpp FILE: ../../../third_party/skia/gm/matriximagefilter.cpp -FILE: ../../../third_party/skia/gm/multipicturedraw.cpp FILE: ../../../third_party/skia/gm/patch.cpp FILE: ../../../third_party/skia/gm/picture.cpp FILE: ../../../third_party/skia/gm/pictureshader.cpp @@ -2212,7 +2211,6 @@ FILE: ../../../third_party/skia/include/core/SkBBHFactory.h FILE: ../../../third_party/skia/include/core/SkBlurTypes.h FILE: ../../../third_party/skia/include/core/SkDrawable.h FILE: ../../../third_party/skia/include/core/SkFont.h -FILE: ../../../third_party/skia/include/core/SkMultiPictureDraw.h FILE: ../../../third_party/skia/include/core/SkPictureRecorder.h FILE: ../../../third_party/skia/include/core/SkSurfaceProps.h FILE: ../../../third_party/skia/include/core/SkTextBlob.h @@ -2244,7 +2242,6 @@ FILE: ../../../third_party/skia/src/core/SkHalf.cpp FILE: ../../../third_party/skia/src/core/SkImageGenerator.cpp FILE: ../../../third_party/skia/src/core/SkMaskCache.cpp FILE: ../../../third_party/skia/src/core/SkMaskCache.h -FILE: ../../../third_party/skia/src/core/SkMultiPictureDraw.cpp FILE: ../../../third_party/skia/src/core/SkPicturePlayback.cpp FILE: ../../../third_party/skia/src/core/SkPicturePlayback.h FILE: ../../../third_party/skia/src/core/SkPictureRecorder.cpp From 7892c69445d482ee0e73108de638a6bdd4fc67d7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 15 Nov 2019 19:07:36 -0500 Subject: [PATCH 152/591] Roll fuchsia/sdk/core/mac-amd64 from RI85D... to s9Vl7... (#13882) Roll fuchsia/sdk/core/mac-amd64 from RI85D... to s9Vl7... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 937c0f87c25cb..c57c7aabaa855 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'RI85DcYL8JEoFsjAyJH62YB8CvhYOu-3tto-nj8ZHlIC' + 'version': 's9Vl7isxaSJoa0RSsBHJIMLMM5S3OIenvSpB3jhCdmkC' } ], 'condition': 'host_os == "mac"', From 59b5a0cf76765767e3ead8e7157b5f8e682c644f Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 15 Nov 2019 18:09:32 -0800 Subject: [PATCH 153/591] Roll src/third_party/dart fdb9d19826..d6b0a27665 (19 commits) dart-lang/sdk@d6b0a27665 [test] Explicitly mark streamed_conversion_json_utf8_decode_test as slow in reload test modes. dart-lang/sdk@1c12878d05 [ dart:io ] Added timeline events for HttpClient connections and requests dart-lang/sdk@545b10389d [vm, service] Compute allocation stats on demand, instead of during allocation and GC. dart-lang/sdk@6239777983 [dart2js] Fix a few missing and one unneeded `covariant`s dart-lang/sdk@a2fe9d450e [analyzer] Report invalid variance positions in methods of a class. dart-lang/sdk@329d0913ca [vm] Late modifier for non-final local variables. dart-lang/sdk@a8b3773054 [vm, reload] Guard against field loads that no longer conform to the field's static type. dart-lang/sdk@33c95a8508 Migration: account for definite assignment. dart-lang/sdk@f3ee4d0fe5 Add a failing test case for http://dartbug.com/39401 -- quick fix improvement around angular generated files dart-lang/sdk@15e5c23167 nnbd preview tool: Better text when inserting 'required' dart-lang/sdk@6ec813e07a [analyzer] Modified type inference constraints wrt variance. dart-lang/sdk@249a9172f7 Migration: don't mark fields as nullable due to factory and redirecting constructors. dart-lang/sdk@ea6c7f29fa [fuzzer] Added void functions to the API table dart-lang/sdk@a6635d006b [nnbd_migration] Show "exact nullability" in output directory. dart-lang/sdk@557ed605de Temporarily do not enable nnbd when building packages for ddc tests dart-lang/sdk@89dc1a791e Updated analyzer error messages in variance_multi_subclass_error_test. dart-lang/sdk@d290f1cec2 Add nonfunction-type-aliases experimental flag dart-lang/sdk@faa4eb7808 Partial support for an incremental workflow dart-lang/sdk@c5b87f2a01 [vm] Disable entire test (apparently status files can no longer disable individual multitests) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c57c7aabaa855..26c082ea02d71 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'fdb9d19826b9fd328d168e6ff9f1c582abdd1594', + 'dart_revision': 'd6b0a27665c333a76e63fbf68893489cd8aa817e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 8129e4045711a..3c4e8accfc32c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: a0f67487ad70add100b4af41c6bfbb76 +Signature: e526a48212137152ee7c23901e2605c1 UNUSED LICENSES: From f0371e2f8f976a675de52e2313c56056b7944b89 Mon Sep 17 00:00:00 2001 From: Ian McKellar Date: Fri, 15 Nov 2019 21:16:44 -0800 Subject: [PATCH 154/591] Work around Fuchsia a11y / ICU name conflict (#13885) ICU #defines TRUE and FALSE but these are used as enum member name by the Fuchsia i18n FIDL library. This #undefs TRUE and FALSE before including the generated FIDL header. Fixes https://github.com/flutter/flutter/issues/44817 --- shell/platform/fuchsia/flutter/accessibility_bridge.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shell/platform/fuchsia/flutter/accessibility_bridge.h b/shell/platform/fuchsia/flutter/accessibility_bridge.h index f81f995526323..58d53933b47f9 100644 --- a/shell/platform/fuchsia/flutter/accessibility_bridge.h +++ b/shell/platform/fuchsia/flutter/accessibility_bridge.h @@ -5,6 +5,10 @@ #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_ACCESSIBILITY_BRIDGE_H_ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_ACCESSIBILITY_BRIDGE_H_ +// Work around symbol conflicts with ICU. +#undef TRUE +#undef FALSE + #include #include #include From 0d97f7c9d1cd5ef9c67e169884589c8b5e9b2f22 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 15 Nov 2019 21:45:12 -0800 Subject: [PATCH 155/591] Roll src/third_party/dart d6b0a27665..8617da2bf0 (1 commits) dart-lang/sdk@8617da2bf0 [VM/nnbd] Pass NNBD mode to runtime functions whose semantics depend on it. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 26c082ea02d71..53c2441f65ac9 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd6b0a27665c333a76e63fbf68893489cd8aa817e', + 'dart_revision': '8617da2bf0b840608850fd66e8f3a859c493770c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 3c4e8accfc32c..621cf816655b0 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: e526a48212137152ee7c23901e2605c1 +Signature: d1c9ac0a0ead435d19ff948071c097c3 UNUSED LICENSES: From 7ef587220aca9e8d66b456bd89b08c871a0122b0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 16 Nov 2019 07:43:08 -0500 Subject: [PATCH 156/591] Roll fuchsia/sdk/core/mac-amd64 from s9Vl7... to rX-Bh... (#13887) Roll fuchsia/sdk/core/mac-amd64 from s9Vl7... to rX-Bh... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 53c2441f65ac9..e4fa9fd158240 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 's9Vl7isxaSJoa0RSsBHJIMLMM5S3OIenvSpB3jhCdmkC' + 'version': 'rX-Bhp2bDEsnuAvgmiUTp_iUV4oASTN-krnlTSKZDP0C' } ], 'condition': 'host_os == "mac"', From fb9b91428b4401fee584934f4c7ffc90ff84581b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 16 Nov 2019 20:19:49 -0500 Subject: [PATCH 157/591] Roll fuchsia/sdk/core/mac-amd64 from rX-Bh... to Ass60... (#13888) Roll fuchsia/sdk/core/mac-amd64 from rX-Bh... to Ass60... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index e4fa9fd158240..52da81d86a470 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'rX-Bhp2bDEsnuAvgmiUTp_iUV4oASTN-krnlTSKZDP0C' + 'version': 'Ass60FmknQVVtKXVCtvkdSid7B__EwY_bCnWFbdVKLUC' } ], 'condition': 'host_os == "mac"', From 54be2d5fd8ea5a402ee1dc37c03e2ffeb831ac19 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 17 Nov 2019 07:01:56 -0500 Subject: [PATCH 158/591] Roll src/third_party/skia 541f310b3482..78fa08b279c5 (9 commits) (#13884) https://skia.googlesource.com/skia.git/+log/541f310b3482..78fa08b279c5 git log 541f310b3482..78fa08b279c5 --date=short --no-merges --format='%ad %ae %s' 2019-11-16 csmartdalton@google.com Revert "Enable msaa ccpr on vulkan" 2019-11-16 csmartdalton@google.com Enable msaa ccpr on vulkan 2019-11-15 herb@google.com Add subspan() to SkZip 2019-11-15 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-15 herb@google.com Add last() to SkZip 2019-11-15 michaelludwig@google.com Revert "Replace GrTextureDomainEffect with GrDomainEffect." 2019-11-15 michaelludwig@google.com Revert "fix issue introduced in f29caf1433e3185df01b4a286d0fc9715ad32ae2" 2019-11-15 reed@google.com Expose the owning surface to a canvas? 2019-11-15 robertphillips@google.com Make GrProgramInfo take pointers to the GrPipeline and the GrPrimitiveProcessor Created with: gclient setdep -r src/third_party/skia@78fa08b279c5 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC halcanary@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=halcanary@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 52da81d86a470..9e0f7f40b0c6d 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '541f310b348296c1e2cdef182a3098d9ca5e1795', + 'skia_revision': '78fa08b279c5929fdeff66c163e68878d1ece39b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 3c54d796bff7a..0c807e44fad4f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 4d7c8c6647cb2443e468da2d73f41706 +Signature: 3fce83da8656c709f001aff51236495c UNUSED LICENSES: From 4da0645df1aaf1b00ef3c2cffa3920976e379390 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 17 Nov 2019 08:55:26 -0500 Subject: [PATCH 159/591] Roll fuchsia/sdk/core/mac-amd64 from Ass60... to pVwSb... (#13890) Roll fuchsia/sdk/core/mac-amd64 from Ass60... to pVwSb... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 9e0f7f40b0c6d..e369240513851 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'Ass60FmknQVVtKXVCtvkdSid7B__EwY_bCnWFbdVKLUC' + 'version': 'pVwSbyQ6Cgjyqf7QfuC8QgqLjmM7NpjxRYQLzMeusAoC' } ], 'condition': 'host_os == "mac"', From 3261324fab79b9738b916998390e77c74f98d34d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 17 Nov 2019 21:31:23 -0500 Subject: [PATCH 160/591] Roll fuchsia/sdk/core/mac-amd64 from pVwSb... to 6ZRA_... (#13891) Roll fuchsia/sdk/core/mac-amd64 from pVwSb... to 6ZRA_... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index e369240513851..a11360355acd6 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'pVwSbyQ6Cgjyqf7QfuC8QgqLjmM7NpjxRYQLzMeusAoC' + 'version': '6ZRA_KF76tgH77uFzIN5Ad1ywDXqv7Hib9M2wzSLcCIC' } ], 'condition': 'host_os == "mac"', From 5fb7eb753b28adbc1ca4914d9c27993027885aa7 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 18 Nov 2019 00:46:11 -0800 Subject: [PATCH 161/591] Roll src/third_party/dart 8617da2bf0..7b67303318 (3 commits) dart-lang/sdk@7b67303318 Make json.fuse(utf8) work correctly. dart-lang/sdk@4b5589e0cb Reland "[cfe] Use StaticTypeContext for getStaticType" and more" dart-lang/sdk@c96fe4e006 Add more explaination to existing examples --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a11360355acd6..530975107b64c 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8617da2bf0b840608850fd66e8f3a859c493770c', + 'dart_revision': '7b6730331849663d84c8740f452182896e781dbf', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 621cf816655b0..849ededae1a3c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: d1c9ac0a0ead435d19ff948071c097c3 +Signature: 47873e64a3a994cdbf7e9f2369ab5687 UNUSED LICENSES: From 9832bc8f2eb2c6c40cf62c2734699fcc048521ef Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 18 Nov 2019 08:11:40 -0500 Subject: [PATCH 162/591] Roll src/third_party/skia 78fa08b279c5..c9d263c1213e (1 commits) (#13893) https://skia.googlesource.com/skia.git/+log/78fa08b279c5..c9d263c1213e git log 78fa08b279c5..c9d263c1213e --date=short --no-merges --format='%ad %ae %s' 2019-11-18 kjlubick@google.com [canvaskit] Roll to 0.9.0 Created with: gclient setdep -r src/third_party/skia@c9d263c1213e If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 530975107b64c..8112616e20b9b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '78fa08b279c5929fdeff66c163e68878d1ece39b', + 'skia_revision': 'c9d263c1213e91dc6b77b34d323ed6217f78e64e', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0c807e44fad4f..78d8c24911a06 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 3fce83da8656c709f001aff51236495c +Signature: b0a429149cfc0be9f3798a32f979653f UNUSED LICENSES: From 0dbcb1fd92b129e05fe9bfd242dec4b5e9f59077 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 18 Nov 2019 10:09:56 -0500 Subject: [PATCH 163/591] Roll fuchsia/sdk/core/mac-amd64 from 6ZRA_... to Oo6E_... (#13894) Roll fuchsia/sdk/core/mac-amd64 from 6ZRA_... to Oo6E_... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8112616e20b9b..1e44081508be0 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '6ZRA_KF76tgH77uFzIN5Ad1ywDXqv7Hib9M2wzSLcCIC' + 'version': 'Oo6E_mnZFkOrXPLwuRYM89A9KItHmnYInwxs1JkeOkgC' } ], 'condition': 'host_os == "mac"', From 73d757c70c06fcea41d139d855a3b10c1bd04d47 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 18 Nov 2019 06:44:31 -0800 Subject: [PATCH 164/591] Roll src/third_party/dart 7b67303318..aceeba7567 (8 commits) dart-lang/sdk@aceeba7567 [vm/async] Encode the yield index -> token position in PcDescriptors. dart-lang/sdk@129565a71d [kernel] Handle property invocation in MethodInvocation.getStaticType dart-lang/sdk@706c54f763 [SDK] Adds disasm. flags for FORCE_INCLUDE_DISASSEMBLER. dart-lang/sdk@696069c9a2 [infra] test.dart: Download build results while running the tests dart-lang/sdk@9e03a639ea [cfe] Split inferMethodInvocation into invocation variants dart-lang/sdk@764926f37d [cfe] Use BinaryExpression in compounds dart-lang/sdk@29f127d055 [CFE] Clear initializers unconditionally in prepareInitializers dart-lang/sdk@ed6dd5b79b [dart2js] Address new UNUSED_ELEMENT warnings --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1e44081508be0..518630dd7f5ba 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '7b6730331849663d84c8740f452182896e781dbf', + 'dart_revision': 'aceeba75674064c0863c1e71c3c1838d3fe7a8f1', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 849ededae1a3c..96bd519cbd23b 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 47873e64a3a994cdbf7e9f2369ab5687 +Signature: fc06b037b29afba28460a0bac472bef1 UNUSED LICENSES: @@ -7619,6 +7619,7 @@ FILE: ../../../third_party/dart/runtime/vm/compiler/backend/sexpression_test.cc FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot_test.cc FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator_test.cc FILE: ../../../third_party/dart/runtime/vm/compiler/backend/typed_data_aot_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/yield_position_test.cc FILE: ../../../third_party/dart/runtime/vm/compiler/ffi.cc FILE: ../../../third_party/dart/runtime/vm/compiler/ffi.h FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_fingerprints.cc From dd77b73d7e03fcbbd49536a3b58395f086b91d8d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 18 Nov 2019 12:47:52 -0500 Subject: [PATCH 165/591] Roll fuchsia/sdk/core/linux-amd64 from dhwMR... to kSMF7... (#13899) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 518630dd7f5ba..37eb993f50fcf 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'dhwMRVa1EqOssqHjmArBMM7Ne4-LwwiF1c51klzy318C' + 'version': 'kSMF7s2jUaj1ejmNzMWNOHM8ATJx8KSoKOH-rNC5V2AC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 2402af9bb6498..29a1c9bd81ab3 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 9ccc5f19cc74f1d3b284857bd4436fbe +Signature: 0f0c3ebb82bab6fceb91a92b239ea330 UNUSED LICENSES: @@ -1293,7 +1293,17 @@ FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/sl4f_client.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/ssh.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/storage.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/test.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/common.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/flutter_frame_stats.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/scenic_frame_stats.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_results.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_spec.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_delta.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_point.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/trace_importing.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/trace_model.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/webdriver.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/trace_processing.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/meta.json FILE: ../../../fuchsia/sdk/linux/dart/zircon/meta.json FILE: ../../../fuchsia/sdk/linux/device/generic-arm64.json @@ -1379,6 +1389,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/images.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/player.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/publisher.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_consumer.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_core.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/usage_reporter.fidl @@ -1491,11 +1502,13 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl-async/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/envelope_frames.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/internal_callable_traits.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/runtime_flag.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/transformer.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/txn_header.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/visitor.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/internal.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/runtime_flag.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/transformer.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/txn_header.c FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/event_sender.h From e85d92197f6a4de2dade67d12c3dbd70399dd58f Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 18 Nov 2019 13:42:48 -0800 Subject: [PATCH 166/591] Fix single line bitmap canvas text shadow (#13901) --- lib/web_ui/lib/src/engine/text/paragraph.dart | 9 +++++++-- lib/web_ui/lib/src/engine/text/ruler.dart | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 17b1041a5e212..1889a5e2416b7 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -165,7 +165,8 @@ class EngineParagraph implements ui.Paragraph { } else { canDrawTextOnCanvas = _measurementResult.isSingleLine && _plainText != null && - _geometricStyle.ellipsis == null; + _geometricStyle.ellipsis == null && + _geometricStyle.shadows == null; } return canDrawTextOnCanvas && @@ -364,7 +365,9 @@ class EngineParagraphStyle implements ui.ParagraphStyle { double get _lineHeight { // TODO(mdebbar): Implement proper support for strut styles. // https://github.com/flutter/flutter/issues/32243 - if (_strutStyle == null || _strutStyle._height == null || _strutStyle._height == 0) { + if (_strutStyle == null || + _strutStyle._height == null || + _strutStyle._height == 0) { // When there's no strut height, always use paragraph style height. return _height; } @@ -908,6 +911,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { wordSpacing: wordSpacing, decoration: _textDecorationToCssString(decoration, decorationStyle), ellipsis: _paragraphStyle._ellipsis, + shadows: shadows, ), plainText: '', paint: paint, @@ -961,6 +965,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder { wordSpacing: wordSpacing, decoration: _textDecorationToCssString(decoration, decorationStyle), ellipsis: _paragraphStyle._ellipsis, + shadows: shadows, ), plainText: plainText, paint: paint, diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index 8c17b3d2c10e3..ff90efa14518b 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -17,6 +17,7 @@ class ParagraphGeometricStyle { this.wordSpacing, this.decoration, this.ellipsis, + this.shadows, }); final ui.FontWeight fontWeight; @@ -29,6 +30,7 @@ class ParagraphGeometricStyle { final double wordSpacing; final String decoration; final String ellipsis; + final List shadows; // Since all fields above are primitives, cache hashcode since ruler lookups // use this style as key. From df2db642849eba39491b64815c6b3158b0c38f05 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 18 Nov 2019 14:58:37 -0800 Subject: [PATCH 167/591] Roll src/third_party/dart aceeba7567..06e6554d2e (2 commits) (#13900) dart-lang/sdk@06e6554d2e [CFE] getFormal via Identifier (name and offset) and not just String (name) dart-lang/sdk@edbff4eae7 [CFE] Encapsulate 'Stack' and create DebugStack as an option --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 37eb993f50fcf..a2f8985072757 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'aceeba75674064c0863c1e71c3c1838d3fe7a8f1', + 'dart_revision': '06e6554d2e321732b5dfe02fde02a9791fe64467', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 73e1fb21fc0dad5392d5cbc8c0ad9f89388deeb3 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 18 Nov 2019 16:00:01 -0800 Subject: [PATCH 168/591] Fix withIn matcher distance function lookup (#13904) --- lib/web_ui/test/matchers.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/test/matchers.dart b/lib/web_ui/test/matchers.dart index c8ff6ba4f597e..d06ee239cca7b 100644 --- a/lib/web_ui/test/matchers.dart +++ b/lib/web_ui/test/matchers.dart @@ -138,12 +138,12 @@ Matcher within({ @required T from, DistanceFunction distanceFunction, }) { - distanceFunction ??= _kStandardDistanceFunctions[from.runtimeType]; + distanceFunction ??= _kStandardDistanceFunctions[T]; if (distanceFunction == null) { throw ArgumentError( 'The specified distanceFunction was null, and a standard distance ' - 'function was not found for type ${from.runtimeType} of the provided ' + 'function was not found for type ${T} of the provided ' '`from` argument.'); } From a3b89a31daaf7b5e404a1938394e6a1f36ee71a3 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 18 Nov 2019 17:12:51 -0800 Subject: [PATCH 169/591] Roll src/third_party/dart 06e6554d2e..830f291773 (22 commits) (#13905) dart-lang/sdk@830f291773 [vm] Use a wrapper for bootstrap natives too. dart-lang/sdk@29ff784ecb [dart/compiler] Loop analysis and BCE improvements dart-lang/sdk@b716efbfc0 Attempt to work around repeating LinkedBundleContext exceptions. dart-lang/sdk@860132c9e0 Fix using interfaceType() without nullability, remove unused class. dart-lang/sdk@7df44023f4 Issue 38878. Update SuperContext for annotations. dart-lang/sdk@e34e734b56 [nnbd_migration] track causations for substitution nodes dart-lang/sdk@7905e78db2 NNBD preview: Better messaging for non-late uninitialized variable dart-lang/sdk@b5b45ffe4b Issue 38953. Exit the unit element walker on enter into a directive. dart-lang/sdk@1e7d4fd81e Simplify ForEachPartsWithDeclaration case in LocalDeclarationVisitor. dart-lang/sdk@f69935b107 Migration: stop creating union edges for inferred types. dart-lang/sdk@6a8aae0095 [vm] Enable Dart VM to run in QEMU user-mode emulation for ARM. dart-lang/sdk@1ae6f1b031 Support for creating and displaying edits in the preview tool dart-lang/sdk@8fbb053092 Issue 39021. Guard against invalid generic type alias, without function type. dart-lang/sdk@fe3fadbdba Issue 39025. ForEachPartsWithIdentifier does not define anything, don't pretend that it is a LocalVariableElement. dart-lang/sdk@3a17bebf9c Issue 39028. Guard against mixin constructor in FieldFormalParameter completion. dart-lang/sdk@2a5d00bcd2 Issue 38992. Build annotation elements for type parameter elements on their creation. dart-lang/sdk@aa0cf44b05 Issue 38506. Don't attempt to use LibraryElement.metadata for not the first LibraryDirective. dart-lang/sdk@6df2491743 Issue 38551. Fix for reading references to PropertyAccessorElement(s) defined in extensions. dart-lang/sdk@7c1f58f9fe [cfe] Demote inferred type variables when used as type arguments dart-lang/sdk@16af4b7355 [vm] Set all entrypoints when reading JIT snapshots. dart-lang/sdk@369fdb7cb2 [SDK] Fixes FORCE_INCLUDE_DISASSEMBLER build in PRODUCT. dart-lang/sdk@043885676c [CFE] Mark initializer as inferred and don't redo work --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a2f8985072757..c43b1b6b638f9 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '06e6554d2e321732b5dfe02fde02a9791fe64467', + 'dart_revision': '830f29177386f7e45a5669e44250f0ca84cc7735', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 96bd519cbd23b..220326570a381 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: fc06b037b29afba28460a0bac472bef1 +Signature: 2b994ea47a06a0a7b4a6c71bdb293ac8 UNUSED LICENSES: From 762294cf2d29ca34bb324c8a55812f0e7d5a82b3 Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Mon, 18 Nov 2019 18:28:04 -0800 Subject: [PATCH 170/591] Revert "RendererContextSwitch guard flutter's gl context rework. (#13812)" (#13906) This reverts commit f456423cfb820d07bb36e9a8979e3d75cc9d8d76. This is being reverted because it caused flutter/flutter#45098 (images don't load on iOS). --- ci/licenses_golden/licenses_flutter | 4 - shell/common/BUILD.gn | 2 - shell/common/rasterizer.cc | 56 ++------- shell/common/rasterizer.h | 9 -- .../common/renderer_context_switch_manager.cc | 30 ----- .../common/renderer_context_switch_manager.h | 116 ------------------ shell/common/shell_test.cc | 12 +- shell/common/shell_test.h | 6 +- shell/common/shell_unittests.cc | 63 ---------- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 ++--- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/gpu/gpu_surface_metal.h | 3 +- shell/gpu/gpu_surface_metal.mm | 5 +- shell/platform/android/android_surface_gl.cc | 13 +- shell/platform/android/android_surface_gl.h | 7 +- shell/platform/darwin/ios/BUILD.gn | 2 - .../ios/framework/Source/FlutterEngine.mm | 1 - .../framework/Source/FlutterPlatformViews.mm | 16 +-- .../Source/FlutterPlatformViews_Internal.h | 6 - shell/platform/darwin/ios/ios_gl_context.h | 17 +-- shell/platform/darwin/ios/ios_gl_context.mm | 22 ++-- .../ios/ios_gl_context_switch_manager.h | 65 ---------- .../ios/ios_gl_context_switch_manager.mm | 79 ------------ .../darwin/ios/ios_gl_render_target.h | 16 ++- .../darwin/ios/ios_gl_render_target.mm | 43 +++---- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 10 +- shell/platform/darwin/ios/ios_surface_gl.mm | 18 +-- shell/platform/darwin/ios/ios_surface_metal.h | 3 +- .../platform/darwin/ios/ios_surface_metal.mm | 5 +- .../darwin/ios/ios_surface_software.h | 5 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 ++- .../platform/embedder/embedder_surface_gl.cc | 13 +- shell/platform/embedder/embedder_surface_gl.h | 7 +- .../Scenarios.xcodeproj/project.pbxproj | 16 +-- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ---- .../Scenarios/Scenarios/GLTestPlatformView.h | 30 ----- .../Scenarios/Scenarios/GLTestPlatformView.m | 90 -------------- .../ScenariosUITests/PlatformViewGLTests.m | 39 ------ testing/scenario_app/lib/main.dart | 1 - .../scenario_app/lib/src/platform_view.dart | 36 ++---- testing/scenario_app/lib/src/texture.dart | 0 47 files changed, 129 insertions(+), 864 deletions(-) delete mode 100644 shell/common/renderer_context_switch_manager.cc delete mode 100644 shell/common/renderer_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m delete mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1cf469f50c62d..a2b25ba2b017e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -514,8 +514,6 @@ FILE: ../../../flutter/shell/common/pointer_data_dispatcher.cc FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h FILE: ../../../flutter/shell/common/rasterizer.cc FILE: ../../../flutter/shell/common/rasterizer.h -FILE: ../../../flutter/shell/common/renderer_context_switch_manager.cc -FILE: ../../../flutter/shell/common/renderer_context_switch_manager.h FILE: ../../../flutter/shell/common/run_configuration.cc FILE: ../../../flutter/shell/common/run_configuration.h FILE: ../../../flutter/shell/common/shell.cc @@ -805,8 +803,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index d87b6abede0e5..f93bca63478f0 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -78,8 +78,6 @@ source_set("common") { "pointer_data_dispatcher.h", "rasterizer.cc", "rasterizer.h", - "renderer_context_switch_manager.cc", - "renderer_context_switch_manager.h", "run_configuration.cc", "run_configuration.h", "shell.cc", diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 61cd119af7a22..d61e1ff86ddc3 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -163,9 +163,7 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, // happen in case of software rendering. surface = SkSurface::MakeRaster(image_info); } else { - std::unique_ptr - context_switch = surface_->MakeRenderContextCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!surface_->MakeRenderContextCurrent()) { return nullptr; } @@ -379,7 +377,7 @@ static sk_sp CreateSnapshotSurface(GrContext* surface_context, return SkSurface::MakeRaster(image_info); } -sk_sp Rasterizer::ScreenshotLayerTreeAsImage( +static sk_sp ScreenshotLayerTreeAsImage( flutter::LayerTree* tree, flutter::CompositorContext& compositor_context, GrContext* surface_context, @@ -404,20 +402,21 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr, root_surface_transformation, false, nullptr); - canvas->clear(SK_ColorTRANSPARENT); frame->Raster(*tree, true); - ScreenshotFlushCanvas(*canvas); + canvas->flush(); // Prepare an image from the surface, this image may potentially be on th GPU. - auto potentially_gpu_snapshot = MakeImageSnapshot(snapshot_surface); + auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot(); if (!potentially_gpu_snapshot) { + FML_LOG(ERROR) << "Screenshot: unable to make image screenshot"; return nullptr; } // Copy the GPU image snapshot into CPU memory. - auto cpu_snapshot = MakeRasterImage(potentially_gpu_snapshot); + auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage(); if (!cpu_snapshot) { + FML_LOG(ERROR) << "Screenshot: unable to make raster image"; return nullptr; } @@ -437,47 +436,6 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize()); } -sk_sp Rasterizer::MakeImageSnapshot( - sk_sp snapshot_surface) { - std::unique_ptr - context_switch = surface_->MakeRenderContextCurrent(); - if (!context_switch->GetSwitchResult()) { - return nullptr; - } - auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot(); - if (!potentially_gpu_snapshot) { - FML_LOG(ERROR) << "Screenshot: unable to make image screenshot"; - return nullptr; - } - return potentially_gpu_snapshot; -} - -sk_sp Rasterizer::MakeRasterImage( - sk_sp potentially_gpu_snapshot) { - std::unique_ptr - context_switch = surface_->MakeRenderContextCurrent(); - if (!context_switch->GetSwitchResult()) { - return nullptr; - } - auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage(); - if (!cpu_snapshot) { - FML_LOG(ERROR) << "Screenshot: unable to make raster image"; - return nullptr; - } - return cpu_snapshot; -} - -void Rasterizer::ScreenshotFlushCanvas(SkCanvas& canvas) { - std::unique_ptr - context_switch = surface_->MakeRenderContextCurrent(); - if (!context_switch->GetSwitchResult()) { - FML_LOG(ERROR) - << "Screenshot: unable to switch gl context to flutter's context"; - return; - } - canvas.flush(); -} - Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 9117f3674b734..097d93fd4c92a 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -432,15 +432,6 @@ class Rasterizer final : public SnapshotDelegate { void FireNextFrameCallbackIfPresent(); - sk_sp ScreenshotLayerTreeAsImage( - flutter::LayerTree* tree, - flutter::CompositorContext& compositor_context, - GrContext* surface_context, - bool compressed); - sk_sp MakeImageSnapshot(sk_sp snapshot_surface); - sk_sp MakeRasterImage(sk_sp potentially_gpu_snapshot); - void ScreenshotFlushCanvas(SkCanvas& canvas); - FML_DISALLOW_COPY_AND_ASSIGN(Rasterizer); }; diff --git a/shell/common/renderer_context_switch_manager.cc b/shell/common/renderer_context_switch_manager.cc deleted file mode 100644 index 6bd5dd12832c7..0000000000000 --- a/shell/common/renderer_context_switch_manager.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "renderer_context_switch_manager.h" - -namespace flutter { - -RendererContextSwitchManager::RendererContextSwitchManager() = default; - -RendererContextSwitchManager::~RendererContextSwitchManager() = default; - -RendererContextSwitchManager::RendererContextSwitch::RendererContextSwitch() = - default; - -RendererContextSwitchManager::RendererContextSwitch::~RendererContextSwitch(){}; - -RendererContextSwitchManager::RendererContextSwitchPureResult:: - RendererContextSwitchPureResult(bool switch_result) - : switch_result_(switch_result){}; - -RendererContextSwitchManager::RendererContextSwitchPureResult:: - ~RendererContextSwitchPureResult() = default; - -bool RendererContextSwitchManager::RendererContextSwitchPureResult:: - GetSwitchResult() { - return switch_result_; -} - -} // namespace flutter diff --git a/shell/common/renderer_context_switch_manager.h b/shell/common/renderer_context_switch_manager.h deleted file mode 100644 index 323a3a5e5af32..0000000000000 --- a/shell/common/renderer_context_switch_manager.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ - -#include -#include "flutter/fml/macros.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// Manages `RendererContextSwitch`. -/// -/// Should be subclassed for platforms that uses GL and requires context -/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the -/// `RendererContextSwitchManager` to set gl contexts. -/// -class RendererContextSwitchManager { - public: - //------------------------------------------------------------------------------ - /// Switches the gl context to the flutter's contexts. - /// - /// Should be subclassed for each platform embedder that uses GL. - /// In construction, it should set the current context to a flutter's context - /// In destruction, it should rest the current context. - /// - class RendererContextSwitch { - public: - RendererContextSwitch(); - - virtual ~RendererContextSwitch(); - - virtual bool GetSwitchResult() = 0; - - FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitch); - }; - - RendererContextSwitchManager(); - ~RendererContextSwitchManager(); - - //---------------------------------------------------------------------------- - /// @brief Creates a shell instance using the provided settings. The - /// callbacks to create the various shell subcomponents will be - /// called on the appropriate threads before this method returns. - /// If this is the first instance of a shell in the process, this - /// call also bootstraps the Dart VM. - /// - /// @param[in] task_runners The task runners - /// @param[in] settings The settings - /// @param[in] on_create_platform_view The callback that must return a - /// platform view. This will be called on - /// the platform task runner before this - /// method returns. - /// @param[in] on_create_rasterizer That callback that must provide a - /// valid rasterizer. This will be called - /// on the render task runner before this - /// method returns. - /// - /// @return A full initialized shell if the settings and callbacks are - /// valid. The root isolate has been created but not yet launched. - /// It may be launched by obtaining the engine weak pointer and - /// posting a task onto the UI task runner with a valid run - /// configuration to run the isolate. The embedder must always - /// check the validity of the shell (using the IsSetup call) - /// immediately after getting a pointer to it. - /// - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's context as current context. - /// - /// @return A `RendererContextSwitch` with `GetSwitchResult` returning - /// true if the setting process is succesful. - virtual std::unique_ptr MakeCurrent() = 0; - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's resources context as current context. - /// - /// @return A `RendererContextSwitch` with `GetSwitchResult` returning - /// true if the setting process is succesful. - virtual std::unique_ptr ResourceMakeCurrent() = 0; - - //------------------------------------------------------------------------------ - /// A representation of a `RendererContextSwitch` that doesn't require actual - /// context switching. - /// - class RendererContextSwitchPureResult final : public RendererContextSwitch { - public: - // Constructor that creates an `RendererContextSwitchPureResult`. - // The `GetSwitchResult` will return the same value as `switch_result`. - - //---------------------------------------------------------------------------- - /// @brief Constructs a `RendererContextSwitchPureResult`. - /// - /// @param[in] switch_result the switch result that will be returned - /// in `GetSwitchResult()` - /// - RendererContextSwitchPureResult(bool switch_result); - - ~RendererContextSwitchPureResult(); - - bool GetSwitchResult() override; - - private: - bool switch_result_; - - FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchPureResult); - }; - - FML_DISALLOW_COPY_AND_ASSIGN(RendererContextSwitchManager); -}; - -} // namespace flutter - -#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 3fe5a2b9e0f87..51370d082862b 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,11 +355,8 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -std::unique_ptr -ShellTestPlatformView::GLContextMakeCurrent() { - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>( - gl_surface_.MakeCurrent()); +bool ShellTestPlatformView::GLContextMakeCurrent() { + return gl_surface_.MakeCurrent(); } // |GPUSurfaceGLDelegate| @@ -390,10 +387,5 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } -std::shared_ptr -ShellTestPlatformView::GetRendererContextSwitchManager() { - return nullptr; -} - } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 27d32f058ae0e..fdee9653b71ce 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,8 +144,7 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -162,9 +161,6 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - std::shared_ptr - GetRendererContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 0600853532e80..0146619cce4cf 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -952,68 +952,5 @@ TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) { DestroyShell(std::move(shell), std::move(task_runners)); } -TEST_F(ShellTest, RasterizerScreenshot) { - Settings settings = CreateSettingsForFixture(); - auto configuration = RunConfiguration::InferFromSettings(settings); - auto task_runner = CreateNewThread(); - TaskRunners task_runners("test", task_runner, task_runner, task_runner, - task_runner); - std::unique_ptr shell = - CreateShell(std::move(settings), std::move(task_runners)); - - ASSERT_TRUE(ValidateShell(shell.get())); - PlatformViewNotifyCreated(shell.get()); - - RunEngine(shell.get(), std::move(configuration)); - - auto latch = std::make_shared(); - - PumpOneFrame(shell.get()); - - fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetGPUTaskRunner(), [&shell, &latch]() { - Rasterizer::Screenshot screenshot = - shell->GetRasterizer()->ScreenshotLastLayerTree( - Rasterizer::ScreenshotType::CompressedImage, true); - EXPECT_NE(screenshot.data, nullptr); - - latch->Signal(); - }); - latch->Wait(); - DestroyShell(std::move(shell), std::move(task_runners)); -} - -TEST_F(ShellTest, RasterizerMakeRasterSnapshot) { - Settings settings = CreateSettingsForFixture(); - auto configuration = RunConfiguration::InferFromSettings(settings); - auto task_runner = CreateNewThread(); - TaskRunners task_runners("test", task_runner, task_runner, task_runner, - task_runner); - std::unique_ptr shell = - CreateShell(std::move(settings), std::move(task_runners)); - - ASSERT_TRUE(ValidateShell(shell.get())); - PlatformViewNotifyCreated(shell.get()); - - RunEngine(shell.get(), std::move(configuration)); - - auto latch = std::make_shared(); - - PumpOneFrame(shell.get()); - - fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetGPUTaskRunner(), [&shell, &latch]() { - SnapshotDelegate* delegate = - reinterpret_cast(shell->GetRasterizer().get()); - sk_sp image = delegate->MakeRasterSnapshot( - SkPicture::MakePlaceholder({0, 0, 50, 50}), SkISize::Make(50, 50)); - EXPECT_NE(image, nullptr); - - latch->Signal(); - }); - latch->Wait(); - DestroyShell(std::move(shell), std::move(task_runners)); -} - } // namespace testing } // namespace flutter diff --git a/shell/common/surface.cc b/shell/common/surface.cc index 2b876e0c0f558..b8a77ca1811d4 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,10 +60,8 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -std::unique_ptr -Surface::MakeRenderContextCurrent() { - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>(true); +bool Surface::MakeRenderContextCurrent() { + return true; } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index 0b19725da3b74..7bbc16e24c690 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,7 +10,6 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -59,8 +58,7 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual std::unique_ptr - MakeRenderContextCurrent(); + virtual bool MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index a7648b7f71d39..5f30a48375d33 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -9,7 +9,6 @@ #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" #include "flutter/shell/common/persistent_cache.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -40,10 +39,7 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -91,6 +87,8 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; + + delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -100,9 +98,7 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -118,9 +114,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -131,6 +126,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; + + delegate_->GLContextClearCurrent(); } // |Surface| @@ -256,9 +253,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -290,9 +285,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - std::unique_ptr result = - std::make_unique(surface, submit_callback); - return result; + return std::make_unique(surface, submit_callback); } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -300,8 +293,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } - std::unique_ptr - context_switch = delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -338,6 +329,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } + return true; } @@ -368,8 +360,7 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -std::unique_ptr -GPUSurfaceGL::MakeRenderContextCurrent() { +bool GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index eb67440eee07c..97325569bfd16 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,8 +44,7 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - std::unique_ptr - MakeRenderContextCurrent() override; + bool MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 444e7301c1939..dfe0ce7f468db 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,7 +7,6 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -17,8 +16,7 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual std::unique_ptr - GLContextMakeCurrent() = 0; + virtual bool GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -61,9 +59,6 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; - - virtual std::shared_ptr - GetRendererContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index acb5ca02a7162..fc6b0964766ce 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -49,8 +49,7 @@ class GPUSurfaceMetal : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - std::unique_ptr MakeRenderContextCurrent() - override; + bool MakeRenderContextCurrent() override; void ReleaseUnusedDrawableIfNecessary(); diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 2aba2cd41f376..81abf740d48f6 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -175,10 +175,9 @@ } // |Surface| -std::unique_ptr -GPUSurfaceMetal::MakeRenderContextCurrent() { +bool GPUSurfaceMetal::MakeRenderContextCurrent() { // This backend has no such concept. - return std::make_unique(true); + return true; } void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() { diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index a06b13e68f6bb..737d9f293a518 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,12 +104,9 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -std::unique_ptr -AndroidSurfaceGL::GLContextMakeCurrent() { +bool AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>( - onscreen_context_->MakeCurrent()); + return onscreen_context_->MakeCurrent(); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -133,10 +130,4 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -AndroidSurfaceGL::GetRendererContextSwitchManager() { - return nullptr; -} - } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index bfe7b2ce06fb2..d59302ad66509 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,8 +47,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -62,10 +61,6 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr - GetRendererContextSwitchManager() override; - private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 3e67beb97e717..a66c3bf48c5ae 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,8 +86,6 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", - "ios_gl_context_switch_manager.h", - "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 2d76a30227713..a9f20708f15b5 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -553,7 +553,6 @@ - (void)performAction:(FlutterTextInputAction)action withClient:(int)client { - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type asBase64Encoded:(BOOL)base64Encode { FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell"; - NSLog(@"NSLog take screenshoit"); return _shell->Screenshot(type, base64Encode); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 487fd42f5ea0d..33ca14d9fabea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,11 +160,6 @@ frame_size_ = frame_size; } -void FlutterPlatformViewsController::SetRendererContextSwitchManager( - std::shared_ptr gl_context_guard_manager) { - renderer_context_switch_manager_ = gl_context_guard_manager; -} - void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -373,12 +368,7 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - if (renderer_context_switch_manager_ != nullptr) { - std::unique_ptr contextSwitch = - renderer_context_switch_manager_->MakeCurrent(); - } - - EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); + EnsureOverlayInitialized(view_id, gl_context, gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -465,10 +455,6 @@ std::shared_ptr gl_context, GrContext* gr_context) { FML_DCHECK(flutter_view_); - if (renderer_context_switch_manager_ != nullptr) { - std::unique_ptr contextSwitch = - renderer_context_switch_manager_->MakeCurrent(); - } auto overlay_it = overlays_.find(overlay_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 77b8ece44dc5d..c8daeaa605946 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,7 +11,6 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -81,9 +80,6 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); - void SetRendererContextSwitchManager( - std::shared_ptr gl_context_guard_manager); - void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -208,8 +204,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - std::shared_ptr renderer_context_switch_manager_; - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 8c73ce75dc028..232645d9c8592 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,25 +26,16 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - std::unique_ptr - MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); - - std::shared_ptr GetIOSGLContextSwitchManager() { - return renderer_context_switch_manager_; - } + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } - fml::scoped_nsobject GetContext() const { - return renderer_context_switch_manager_->GetContext(); - } - private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; sk_sp color_space_; - std::shared_ptr renderer_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index a54387731e8d9..52fb85f8f19a9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,7 +13,15 @@ namespace flutter { IOSGLContext::IOSGLContext() { - renderer_context_switch_manager_ = std::make_shared(); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -40,16 +48,16 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), renderer_context_switch_manager_); + return std::make_unique(std::move(layer), context_.get(), + resource_context_.get()); } -std::unique_ptr IOSGLContext::MakeCurrent() { - return renderer_context_switch_manager_->MakeCurrent(); +bool IOSGLContext::MakeCurrent() { + return [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr -IOSGLContext::ResourceMakeCurrent() { - return renderer_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLContext::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h deleted file mode 100644 index 56ee4f2eabb3e..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ - -#define GLES_SILENCE_DEPRECATION - -#import -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// The iOS implementation of `RendererContextSwitchManager`. -/// -/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and -/// sets the flutter's gl context as current. -/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to -/// current. -/// -class IOSGLContextSwitchManager final : public RendererContextSwitchManager { - public: - class IOSGLContextSwitch final : public RendererContextSwitch { - public: - IOSGLContextSwitch(IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context); - - ~IOSGLContextSwitch(); - - bool GetSwitchResult() override; - - private: - IOSGLContextSwitchManager& manager_; - bool switch_result_; - bool has_pushed_context_; - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); - }; - - IOSGLContextSwitchManager(); - - ~IOSGLContextSwitchManager(); - - std::unique_ptr MakeCurrent() override; - std::unique_ptr ResourceMakeCurrent() override; - - fml::scoped_nsobject GetContext(); - - private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; - fml::scoped_nsobject stored_; - - bool PushContext(fml::scoped_nsobject context); - void PopContext(); - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); -}; - -} - -#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm deleted file mode 100644 index 0be0a3bcb4df7..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ios_gl_context_switch_manager.h" - -namespace flutter { - -IOSGLContextSwitchManager::IOSGLContextSwitchManager() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } -}; - -IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; - -std::unique_ptr -IOSGLContextSwitchManager::MakeCurrent() { - return std::make_unique(*this, context_); -} - -std::unique_ptr -IOSGLContextSwitchManager::ResourceMakeCurrent() { - return std::make_unique(*this, resource_context_); -} - -fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { - return context_; -} - -bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { - EAGLContext* current = [EAGLContext currentContext]; - if (current == nil) { - [stored_.get() addObject:[NSNull null]]; - } else { - [stored_.get() addObject:current]; - } - bool result = [EAGLContext setCurrentContext:context.get()]; - return result; -} - -void IOSGLContextSwitchManager::PopContext() { - EAGLContext* last = [stored_.get() lastObject]; - [stored_.get() removeLastObject]; - if ([last isEqual:[NSNull null]]) { - [EAGLContext setCurrentContext:nil]; - return; - } - [EAGLContext setCurrentContext:last]; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( - IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context) - : manager_(manager) { - bool result = manager_.PushContext(context); - has_pushed_context_ = true; - switch_result_ = result; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { - if (!has_pushed_context_) { - return; - } - manager_.PopContext(); -} - -bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { - return switch_result_; -} -} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index f52c562fd96d0..b2eafe16e0950 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,15 +13,14 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager); + IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context); ~IOSGLRenderTarget(); @@ -33,17 +32,16 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - std::unique_ptr - MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - std::shared_ptr renderer_context_switch_manager_; + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a7037746369db..a57ba9c46b414 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,20 +12,22 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager) +IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context) : layer_(std::move(layer)), - renderer_context_switch_manager_(gl_context_guard_manager), + context_([context retain]), + resource_context_([resource_context retain]), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - std::unique_ptr context_switch = - renderer_context_switch_manager_->MakeCurrent(); - bool context_current = context_switch->GetSwitchResult(); + FML_DCHECK(context_ != nullptr); + FML_DCHECK(resource_context_ != nullptr); + + bool context_current = [EAGLContext setCurrentContext:context_]; FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -60,8 +62,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - std::unique_ptr context_switch = - renderer_context_switch_manager_->MakeCurrent(); + EAGLContext* context = EAGLContext.currentContext; + [EAGLContext setCurrentContext:context_]; FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -69,6 +71,7 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); + [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -101,9 +104,8 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - std::unique_ptr context_switch = - renderer_context_switch_manager_->MakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (![EAGLContext setCurrentContext:context_]) { return false; } @@ -114,8 +116,7 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![renderer_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER - fromDrawable:layer_.get()]) { + if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { return false; } @@ -131,18 +132,12 @@ return true; } -std::unique_ptr -IOSGLRenderTarget::MakeCurrent() { - bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); - if (!isUpdateSuccessful) { - return std::make_unique(false); - } - return renderer_context_switch_manager_->MakeCurrent(); +bool IOSGLRenderTarget::MakeCurrent() { + return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr -IOSGLRenderTarget::ResourceMakeCurrent() { - return renderer_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLRenderTarget::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index b852a63ee4e9a..49f40f9eec76a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,7 +12,6 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -28,8 +27,7 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual std::unique_ptr - ResourceContextMakeCurrent() = 0; + virtual bool ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 708413c239400..c1019bb442bb0 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,7 +9,6 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -33,8 +32,7 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() - override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -42,8 +40,7 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - std::unique_ptr GLContextMakeCurrent() - override; + bool GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -56,9 +53,6 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetRendererContextSwitchManager() override; - // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 6417e7d899b42..48e70e00a4e7a 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,8 +28,7 @@ return render_target_->IsValid(); } -std::unique_ptr -IOSSurfaceGL::ResourceContextMakeCurrent() { +bool IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -57,12 +56,11 @@ return true; } -std::unique_ptr -IOSSurfaceGL::GLContextMakeCurrent() { +bool IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return std::make_unique(false); + return false; } - return render_target_->MakeCurrent(); + return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -75,11 +73,6 @@ return IsValid() && render_target_->PresentRenderBuffer(); } -// |GPUSurfaceGLDelegate| -std::shared_ptr IOSSurfaceGL::GetRendererContextSwitchManager() { - return context_->GetIOSGLContextSwitchManager(); -} - // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -151,8 +144,7 @@ if (platform_views_controller == nullptr) { return true; } - platform_views_controller->SetRendererContextSwitchManager( - context_->GetIOSGLContextSwitchManager()); + bool submitted = platform_views_controller->SubmitFrame(std::move(context), context_); [CATransaction commit]; return submitted; diff --git a/shell/platform/darwin/ios/ios_surface_metal.h b/shell/platform/darwin/ios/ios_surface_metal.h index 1263571870bd4..2b001ea2b692d 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.h +++ b/shell/platform/darwin/ios/ios_surface_metal.h @@ -30,8 +30,7 @@ class IOSSurfaceMetal final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() - override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_metal.mm b/shell/platform/darwin/ios/ios_surface_metal.mm index 827db668afa1f..58a5fc328647b 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.mm +++ b/shell/platform/darwin/ios/ios_surface_metal.mm @@ -22,9 +22,8 @@ } // |IOSSurface| -std::unique_ptr -IOSSurfaceMetal::ResourceContextMakeCurrent() { - return std::make_unique(false); +bool IOSSurfaceMetal::ResourceContextMakeCurrent() { + return false; } // |IOSSurface| diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index c0b6298e47bf7..daac2ffc77231 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/renderer_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" + @class CALayer; namespace flutter { @@ -28,8 +28,7 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() - override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 8c38ab5861e82..ab5490cf25140 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,9 +27,8 @@ return layer_; } -std::unique_ptr -IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return std::make_unique(false); +bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return false; } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index d867fc68fc6c8..bb37fa9610b2b 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,18 +107,15 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (gl_context_ != nullptr) { - std::unique_ptr context_switch = - gl_context_->ResourceMakeCurrent(); - if (context_switch->GetSwitchResult()) { - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); - } + if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 2efa1c01baf46..d37b03aae8d9e 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,11 +34,8 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -std::unique_ptr -EmbedderSurfaceGL::GLContextMakeCurrent() { - return std::make_unique< - RendererContextSwitchManager::RendererContextSwitchPureResult>( - gl_dispatch_table_.gl_make_current_callback()); +bool EmbedderSurfaceGL::GLContextMakeCurrent() { + return gl_dispatch_table_.gl_make_current_callback(); } // |GPUSurfaceGLDelegate| @@ -82,12 +79,6 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -EmbedderSurfaceGL::GetRendererContextSwitchManager() { - return nullptr; -} - // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 12f264f383c37..a01fa05d4e62c 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,8 +50,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -74,10 +73,6 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; - // |GPUSurfaceGLDelegate| - std::shared_ptr - GetRendererContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index dd2238aba3181..e667c4c88678d 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,8 +40,6 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -138,9 +136,6 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -207,8 +202,6 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -246,7 +239,6 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -406,7 +398,6 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -427,7 +418,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -502,7 +492,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -555,7 +545,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -571,7 +561,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -595,7 +584,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index cd1c5b7366a89..87799ad5e6434 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,15 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + + + + + * registrar = - [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; - [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; - self.window.rootViewController = flutterViewController; -} - @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h deleted file mode 100644 index 19cefe7025986..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GLTestPlatformView : NSObject - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; - -- (UIView*)view; - -@end - -@interface GLTestPlatformViewFactory : NSObject - -- (instancetype)initWithMessenger:(NSObject*)messenger; - -@end - -@interface GLTestView : UIView - -@end - -NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m deleted file mode 100644 index 8142550e52863..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "GLTestPlatformView.h" - -#define GLES_SILENCE_DEPRECATION - -@implementation GLTestPlatformView { - int64_t _viewId; - GLTestView* _view; -} - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id)args - binaryMessenger:(NSObject*)messenger { - if ([super init]) { - _viewId = viewId; - _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; - } - return self; -} - -- (UIView*)view { - return _view; -} - -@end - -@implementation GLTestPlatformViewFactory { - NSObject* _messenger; -} - -- (instancetype)initWithMessenger:(NSObject*)messenger { - self = [super init]; - if (self) { - _messenger = messenger; - } - return self; -} - -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger]; - return platformView; -} - -- (NSObject*)createArgsCodec { - return [FlutterStringCodec sharedInstance]; -} - -@end - -@interface GLTestView () - -@property(strong, nonatomic) EAGLContext* context; - -@end - -@implementation GLTestView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - _context.debugLabel = @"platform view context"; - [EAGLContext setCurrentContext:_context]; - self.backgroundColor = [UIColor redColor]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self checkEAGLContext]; - }); - } - return self; -} - -- (void)checkEAGLContext { - if ([EAGLContext currentContext] != _context) { - self.accessibilityIdentifier = @"gl_platformview_wrong_context"; - } else { - self.accessibilityIdentifier = @"gl_platformview_correct_context"; - } -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m deleted file mode 100644 index d581926ae5a6a..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface PlatformViewGLTests : XCTestCase - -@property(nonatomic, strong) XCUIApplication* application; - -@end - -@implementation PlatformViewGLTests - -- (void)setUp { - self.continueAfterFailure = NO; - - self.application = [[XCUIApplication alloc] init]; - self.application.launchArguments = @[ @"--platform-view-gl" ]; - [self.application launch]; -} - -- (void)testExample { - NSPredicate* predicateToFindPlatformView = - [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, - NSDictionary* _Nullable bindings) { - XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || - [element.identifier isEqualToString:@"gl_platformview_correct_context"]; - }]; - XCUIElement* firstElement = - [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; - if (![firstElement waitForExistenceWithTimeout:30]) { - XCTFail(@"Failed due to not able to find platform view with 30 seconds"); - } - XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); -} - -@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 82b589a6681fe..7a264b70008f0 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,7 +25,6 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), - 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 86fb4df1df449..05efe52fa8b25 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -281,24 +281,6 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } -/// Platform view scenario for testing EAGLContext on iOS. -class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Constructs a platform view to test EAGLContext on iOS. - PlatformViewGLScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - finishBuilderByAddingPlatformViewAndPicture(builder, 6); - } -} - mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -307,7 +289,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id, String viewType) { + void createPlatformView(Window window, String text, int id) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -331,8 +313,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - viewType.length, - ...utf8.encode(viewType), + 'scenarios/textPlatformView'.length, + ...utf8.encode('scenarios/textPlatformView'), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart deleted file mode 100644 index e69de29bb2d1d..0000000000000 From ecb32952ffcba0667456135cdd3a02fe70ac72d4 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 18 Nov 2019 18:42:45 -0800 Subject: [PATCH 171/591] Roll src/third_party/dart 830f291773..62c67e40dc (15 commits) dart-lang/sdk@62c67e40dc Added language test for reified torn-off methods with explicit variance. dart-lang/sdk@7c517ed007 [analyzer] Report invalid variance positions in fields. dart-lang/sdk@c3607e3d48 [dart:_internal] Fix some analyzer errors in the NNBD fork dart-lang/sdk@e12a7e0ca2 Fix for OSR assert (debug) or crash (release) dart-lang/sdk@bbe5d38c0e Add a script to analysis_server/tool for testing out NNBD migration. dart-lang/sdk@c0144141ce Move TestPluginManager, MockServerChannel, and ServerError into utilities dart-lang/sdk@28d9eaf230 Migration: Go ahead and generate migration output files even when a port is given dart-lang/sdk@a8bf0dc2e5 Move type parameter variance tests for LUB to LeastUpperBoundTest. dart-lang/sdk@cfd226a27f [ dart:http ] Fix issue where setting HttpClient.enableTimelineLogging wasn't actually enabling timeline logging dart-lang/sdk@c825038296 [dartdevc] Migrating dart:async patch files for DDC to be nnbd-compliant. dart-lang/sdk@73d0ef6a30 Remove 'visitedTypes' from appendTo(). dart-lang/sdk@99f88806ec (ddc) split call to build_pkgs: create packages for dartdevc-legacy and dartdevc separately dart-lang/sdk@bc449f160b Fix for dartbug.com/39401 -- quick fix improvement around angular generated files dart-lang/sdk@8dd2e8e693 Modify DeclarationsContext.getLibraries to consider the entire context if it is a BazelWorkspace dart-lang/sdk@ed971fd0c7 NNBD migrator: Add Locations for each edit made in an NN fix --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c43b1b6b638f9..974d6010827a1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '830f29177386f7e45a5669e44250f0ca84cc7735', + 'dart_revision': '62c67e40dc9838118b42a9dbe484f956cba4e0ff', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 220326570a381..93b26e32c0bf6 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 2b994ea47a06a0a7b4a6c71bdb293ac8 +Signature: 77edbd411724aeac33b05c2138aa025f UNUSED LICENSES: From 47e10267d40975eb0b466dee3cdea984e77405d0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 18 Nov 2019 22:22:50 -0500 Subject: [PATCH 172/591] Roll src/third_party/skia c9d263c1213e..d4fb7c7b140d (7 commits) (#13897) https://skia.googlesource.com/skia.git/+log/c9d263c1213e..d4fb7c7b140d git log c9d263c1213e..d4fb7c7b140d --date=short --no-merges --format='%ad %ae %s' 2019-11-18 robertphillips@google.com Rename opPODAllocator to recordTimeAllocator 2019-11-18 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update SKP version 2019-11-18 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 6961018759e7..c30db006f1d6 (480 commits) 2019-11-18 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 5f4db6a76640..51b5a3222b60 (9 commits) 2019-11-18 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 102fd19c65f4..aba1020d3914 (7 commits) 2019-11-18 rosasco@google.com SKQP Build for Fuchsia SDK 2019-11-18 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@d4fb7c7b140d If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 974d6010827a1..7769ef90e4f1f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c9d263c1213e91dc6b77b34d323ed6217f78e64e', + 'skia_revision': 'd4fb7c7b140d5d30778b184a8aae64be68b349c7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 78d8c24911a06..92bfa8b466612 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: b0a429149cfc0be9f3798a32f979653f +Signature: 4a7a3fb1016b4d6824116a7917c0c22f UNUSED LICENSES: @@ -1290,6 +1290,7 @@ FILE: ../../../third_party/skia/animations/redcross#1.jpg FILE: ../../../third_party/skia/animations/text#1.xml FILE: ../../../third_party/skia/bench/microbench.json FILE: ../../../third_party/skia/bench/skpbench.json +FILE: ../../../third_party/skia/build/fuchsia/skqp/skqp.cmx FILE: ../../../third_party/skia/docker/binary-size/Dockerfile FILE: ../../../third_party/skia/docker/cmake-release/Dockerfile FILE: ../../../third_party/skia/docker/skia-build-tools/Dockerfile From e16a4b71f14796f44429ec703b6776b0cf476fbb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 18 Nov 2019 22:50:31 -0500 Subject: [PATCH 173/591] Roll fuchsia/sdk/core/mac-amd64 from Oo6E_... to 8X5fE... (#13912) Roll fuchsia/sdk/core/mac-amd64 from Oo6E_... to 8X5fE... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 7769ef90e4f1f..dbe33b6113e36 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'Oo6E_mnZFkOrXPLwuRYM89A9KItHmnYInwxs1JkeOkgC' + 'version': '8X5fE11JpnM-gk8db0X0DfrQ4qrEFncy92Rsq8dVbHsC' } ], 'condition': 'host_os == "mac"', From 72b6a7e30428a786673a5468bb7a505df8f86080 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 18 Nov 2019 20:00:32 -0800 Subject: [PATCH 174/591] Roll buildroot to a985f7f63ac. (#13910) Pulls in the fixes to libwebp in https://github.com/flutter/buildroot/pull/332. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index dbe33b6113e36..77e41fdfe9df0 100644 --- a/DEPS +++ b/DEPS @@ -137,7 +137,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '0fec442d067a0998352ea12706fcae0a53b62884', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a985f7f63ac8ec8dae436523ec338516951ec3ff', # Fuchsia compatibility # From 7c5ed6c742d4aac2a721039b4267cdcc9f068773 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 18 Nov 2019 21:43:00 -0800 Subject: [PATCH 175/591] Roll src/third_party/dart 62c67e40dc..5b72c1c669 (17 commits) dart-lang/sdk@5b72c1c669 [dartdevc] Migrating internal_patch to nnbd. dart-lang/sdk@7c5f81eb0f [dartdevc] Cleanup unused optional parameter from NoSuchMethodError dart-lang/sdk@5eb0d5979a Add new bot configuration for analyzer NNBD dart-lang/sdk@bcc77d8605 (ddc) Generate the sdk.js files by reading a dill file directly dart-lang/sdk@19144db89f Use compile_platform to build the sdk.dill dart-lang/sdk@582cec84f4 Set up new test suites for migrating the tests to NNBD. dart-lang/sdk@baa2d8125f [dartdevc] Cleanup dynamic calls and downcasts in _BigIntImpl dart-lang/sdk@ed8c53ad49 Bump dartfix to 0.1.6 dart-lang/sdk@5ec93e211f [dartfuzz] Adding fuzzer support for extension methods on core library types dart-lang/sdk@2416116208 [analyzer] Support AST variance in visitors and cloning methods. dart-lang/sdk@93c9f97715 [dart:core] Fix analysis error in NNBD fork dart-lang/sdk@bde92ca34a [vm] Late modifier for final local variables. dart-lang/sdk@89b4f55010 Fix an import at the top of the analysis server workspace.dart file dart-lang/sdk@c38cde958d [dartdevc] Finishing NNBD migration of dart:typed_data. dart-lang/sdk@0d40d3736b Change mocks.dart to triple-slash comment style dart-lang/sdk@01614788c8 (ddc) split rule that compiles packages into separate rules for each package dart-lang/sdk@6f00b19174 Migrating dart:convert patch files to NNBD. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 77e41fdfe9df0..3196e21ff8c87 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '62c67e40dc9838118b42a9dbe484f956cba4e0ff', + 'dart_revision': '5b72c1c6695cd91c4394414cff88c43ff7495d23', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 93b26e32c0bf6..be5611b2ed44b 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 77edbd411724aeac33b05c2138aa025f +Signature: 967baee9da0c48803e2e6d2138c25811 UNUSED LICENSES: From 38147fad899b6b7261b6d89c82423b85117ef3c1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 19 Nov 2019 01:38:18 -0500 Subject: [PATCH 176/591] Roll fuchsia/sdk/core/linux-amd64 from kSMF7... to T9BAw... (#13914) Roll fuchsia/sdk/core/linux-amd64 from kSMF7... to T9BAw... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 3196e21ff8c87..8253c1f81fc3a 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'kSMF7s2jUaj1ejmNzMWNOHM8ATJx8KSoKOH-rNC5V2AC' + 'version': 'T9BAwXtY7x6eCpbnEKlsLi5wBJbVtwp4Gc9GdUzyagwC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 29a1c9bd81ab3..83463d027273c 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 0f0c3ebb82bab6fceb91a92b239ea330 +Signature: 93fab6fac8d4c4794020278cb30b613d UNUSED LICENSES: @@ -1517,6 +1517,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/service_conne FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/service_handler_base.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/transition.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/internal/proxy_controller_util.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/message_sender.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/internal/message_sender.cc @@ -2688,6 +2689,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/comparis FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/decoder.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/encoder.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/logging.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/proxy_controller_util.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/object_coding.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/string.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/traits.h @@ -2830,7 +2832,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/system FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/tls.h FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/surface/surface.dart FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/auth_provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/auth_provider_factory.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/token_manager.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/control.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/client.fidl From c4c1e57c38c9e3a84f2f4e8f48d324f8e4353f9c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 19 Nov 2019 02:15:30 -0500 Subject: [PATCH 177/591] Roll src/third_party/skia d4fb7c7b140d..d3ddcb403993 (21 commits) (#13915) https://skia.googlesource.com/skia.git/+log/d4fb7c7b140d..d3ddcb403993 git log d4fb7c7b140d..d3ddcb403993 --date=short --no-merges --format='%ad %ae %s' 2019-11-19 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 51b5a3222b60..e4b3136913c6 (6 commits) 2019-11-19 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src c30db006f1d6..b659e40a3998 (423 commits) 2019-11-19 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader aba1020d3914..79afb7620a69 (5 commits) 2019-11-19 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-18 fmalita@chromium.org [skottie] Fix single-node camera orientation relative to z=0 plane 2019-11-18 jsimmons@google.com Use the default font family if no match is found for any of the families in the text style 2019-11-18 robertphillips@google.com Only access the scissorState's rect when it is valid 2019-11-18 brianosman@google.com Remove some SK_API from src/ 2019-11-18 halcanary@google.com DEPS: add third_party/externals/libgifcodec 2019-11-18 halcanary@google.com GN tool: copy_git_directory.py script 2019-11-18 michaelludwig@google.com Make TessellationHelper resettable 2019-11-18 nigeltao@google.com Optimize SkWuffsCodec pixbuf zero-initialization 2019-11-18 egdaniel@google.com Make GrSemaphore no longer derive from GrGPUResource. 2019-11-18 scroggo@google.com Run nanobench on droids.gif 2019-11-18 jvanverth@google.com Metal: Fix buffer alignment issues on Mac 2019-11-18 reed@google.com restructure EdgeClipper to accommodate other callers (e.g. half-plane) 2019-11-18 benjaminwagner@google.com [infra] Update docs with respect to Go modules. 2019-11-18 robertphillips@google.com Add SampleLocationsTestOp::onPrePrepare 2019-11-18 jvanverth@google.com Add Metal support for dynamic texture state 2019-11-18 robertphillips@google.com Add FwidthSquircleTestOp::onPrePrepare 2019-11-18 robertphillips@google.com Reformat gn files Created with: gclient setdep -r src/third_party/skia@d3ddcb403993 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 8253c1f81fc3a..8bec0c788aef7 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd4fb7c7b140d5d30778b184a8aae64be68b349c7', + 'skia_revision': 'd3ddcb4039935aae7132f5e6c09d1e519991fde4', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 92bfa8b466612..27d8127319cb1 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 4a7a3fb1016b4d6824116a7917c0c22f +Signature: 6c4c894c8101b49b59d8dfc06b67ddfa UNUSED LICENSES: From ad0cbdcd9f1c9aa6b2203f5c63e855465c210e72 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 19 Nov 2019 09:09:04 -0800 Subject: [PATCH 178/591] Roll src/third_party/dart 5b72c1c669..d9d5fbc109 (5 commits) (#13917) dart-lang/sdk@d9d5fbc109 [cfe] Create TypeParameterTypes with default nullabilities dart-lang/sdk@1b295ef956 [cfe] Make nullability parameter of SubtypeTester.futureType required dart-lang/sdk@203583b41b [cfe] Account for invalid type being part of a type in subtype check dart-lang/sdk@0a98a7a153 Add helper predicates for upper/lower bounds. dart-lang/sdk@239e2eb4ed Issue 38813. If in a legacy library, use legacy interface types for CONFLICTING_GENERIC_INTERFACES. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8bec0c788aef7..9729d36d0de42 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '5b72c1c6695cd91c4394414cff88c43ff7495d23', + 'dart_revision': 'd9d5fbc109b9549a5de0a7c6d7148ee0d8cc41dc', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 2b007ed06c0a15ff93cbf08956907c454453cf7c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 19 Nov 2019 12:09:26 -0500 Subject: [PATCH 179/591] Roll fuchsia/sdk/core/mac-amd64 from 8X5fE... to bC9Qy... (#13919) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 9729d36d0de42..ac02c918fa264 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '8X5fE11JpnM-gk8db0X0DfrQ4qrEFncy92Rsq8dVbHsC' + 'version': 'bC9QyoghiSJSFqgRVvUeiVU0MhBMKzsRhvfWmaBc46oC' } ], 'condition': 'host_os == "mac"', From 84ed7be37113d9a3866a917a9756f131dfc8459c Mon Sep 17 00:00:00 2001 From: Ferhat Date: Tue, 19 Nov 2019 09:41:11 -0800 Subject: [PATCH 180/591] [web] Implement PathMetrics.length (#13909) * Implement PathMetrics.length, add tests --- lib/web_ui/lib/src/engine/rrect_renderer.dart | 24 + lib/web_ui/lib/src/ui/path_metrics.dart | 412 +++++++++++++++++- lib/web_ui/test/engine/path_metrics_test.dart | 232 ++++++++++ lib/web_ui/test/matchers.dart | 3 +- 4 files changed, 663 insertions(+), 8 deletions(-) create mode 100644 lib/web_ui/test/engine/path_metrics_test.dart diff --git a/lib/web_ui/lib/src/engine/rrect_renderer.dart b/lib/web_ui/lib/src/engine/rrect_renderer.dart index f463f2c5bad4f..d32ae205341c8 100644 --- a/lib/web_ui/lib/src/engine/rrect_renderer.dart +++ b/lib/web_ui/lib/src/engine/rrect_renderer.dart @@ -222,3 +222,27 @@ class _RRectToPathRenderer extends _RRectRenderer { antiClockwise ? startAngle - endAngle : endAngle - startAngle); } } + +typedef RRectRendererEllipseCallback = void Function(double centerX, double centerY, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool antiClockwise); +typedef RRectRendererCallback = void Function(double x, double y); + +/// Converts RRect to path primitives with callbacks. +class RRectMetricsRenderer extends _RRectRenderer { + RRectMetricsRenderer({this.moveToCallback, this.lineToCallback, this.ellipseCallback}); + + final RRectRendererEllipseCallback ellipseCallback; + final RRectRendererCallback lineToCallback; + final RRectRendererCallback moveToCallback; + @override + void beginPath() {} + + @override + void ellipse(double centerX, double centerY, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool antiClockwise) => ellipseCallback( + centerX, centerY, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise); + + @override + void lineTo(double x, double y) => lineToCallback(x, y); + + @override + void moveTo(double x, double y) => moveToCallback(x, y); +} diff --git a/lib/web_ui/lib/src/ui/path_metrics.dart b/lib/web_ui/lib/src/ui/path_metrics.dart index a13957e426aff..ffb99dff0a5a5 100644 --- a/lib/web_ui/lib/src/ui/path_metrics.dart +++ b/lib/web_ui/lib/src/ui/path_metrics.dart @@ -36,7 +36,8 @@ class PathMetricIterator implements Iterator { bool _firstTime = true; @override - PathMetric get current => _firstTime ? null : _pathMetric; + PathMetric get current => + _firstTime ? null : _pathMetric._segments.isEmpty ? null : _pathMetric; @override bool moveNext() { @@ -44,7 +45,7 @@ class PathMetricIterator implements Iterator { // first Path. Should only call _moveNext when done with the first one. if (_firstTime == true) { _firstTime = false; - return true; + return _pathMetric._segments.isNotEmpty; } else if (_pathMetric?._moveNext() == true) { return true; } @@ -53,6 +54,11 @@ class PathMetricIterator implements Iterator { } } +// Maximum range value used in curve subdivision using Casteljau algorithm. +const int _kMaxTValue = 0x3FFFFFFF; +// Distance at which we stop subdividing cubic and quadratic curves. +const double _fTolerance = 0.5; + /// Utilities for measuring a [Path] and extracting subpaths. /// /// Iterate over the object returned by [Path.computeMetrics] to obtain @@ -61,15 +67,29 @@ class PathMetricIterator implements Iterator { /// Once created, metrics will only be valid while the iterator is at the given /// contour. When the next contour's [PathMetric] is obtained, this object /// becomes invalid. +/// +/// Implementation is based on +/// https://github.com/google/skia/blob/master/src/core/SkContourMeasure.cpp +/// to maintain consistency with native platforms. class PathMetric { final Path _path; final bool _forceClosed; + // If the contour ends with a call to [Path.close] (which may + // have been implied when using [Path.addRect]) + bool _isClosed; + // Iterator index into [Path.subPaths] + int _subPathIndex = 0; + List<_PathSegment> _segments; + double _contourLength; + /// Create a new empty [Path] object. - PathMetric._(this._path, this._forceClosed); + PathMetric._(this._path, this._forceClosed) { + _buildSegments(); + } /// Return the total length of the current contour. - double get length => throw UnimplementedError(); + double get length => _contourLength; /// Computes the position of hte current contour at the given offset, and the /// angle of the path at that point. @@ -108,7 +128,9 @@ class PathMetric { /// have been implied when using [Path.addRect]) or if `forceClosed` was /// specified as true in the call to [Path.computeMetrics]. Returns false /// otherwise. - bool get isClosed => throw UnimplementedError(); + bool get isClosed { + return _isClosed; + } // Move to the next contour in the path. // @@ -120,12 +142,390 @@ class PathMetric { // [Iterator.current]. In this case, the [PathMetric] is valid before // calling `_moveNext` - `_moveNext` should be called after the first // iteration is done instead of before. - bool _moveNext() => throw UnimplementedError(); + bool _moveNext() { + if (_subPathIndex == (_path.subpaths.length - 1)) { + return false; + } + ++_subPathIndex; + _buildSegments(); + return true; + } + + void _buildSegments() { + _segments = <_PathSegment>[]; + _isClosed = false; + double distance = 0.0; + int pointIndex = -1; + bool haveSeenMoveTo = false; + bool haveSeenClose = false; + + if (_path.subpaths.isEmpty) { + _contourLength = 0; + return; + } + final engine.Subpath subpath = _path.subpaths[_subPathIndex]; + final List commands = subpath.commands; + double currentX = 0.0, currentY = 0.0; + final Function lineToHandler = (double x, double y) { + final double dx = currentX - x; + final double dy = currentY - y; + final double prevDistance = distance; + distance += math.sqrt(dx * dx + dy * dy); + // As we accumulate distance, we have to check that the result of += + // actually made it larger, since a very small delta might be > 0, but + // still have no effect on distance (if distance >>> delta). + if (distance > prevDistance) { + _segments.add(_PathSegment(engine.PathCommandTypes.lineTo, distance, + [currentX, currentY, x, y])); + } + currentX = x; + currentY = y; + }; + _EllipseSegmentResult ellipseResult; + for (engine.PathCommand command in commands) { + switch (command.type) { + case engine.PathCommandTypes.moveTo: + final engine.MoveTo moveTo = command; + currentX = moveTo.x; + currentY = moveTo.y; + haveSeenMoveTo = true; + break; + case engine.PathCommandTypes.lineTo: + assert(haveSeenMoveTo); + final engine.LineTo lineTo = command; + lineToHandler(lineTo.x, lineTo.y); + break; + case engine.PathCommandTypes.bezierCurveTo: + assert(haveSeenMoveTo); + final engine.BezierCurveTo curve = command; + // Compute cubic curve distance. + distance = _computeCubicSegments( + currentX, + currentY, + curve.x1, + curve.y1, + curve.x2, + curve.y2, + curve.x3, + curve.y3, + distance, + 0, + _kMaxTValue, + _segments); + break; + case engine.PathCommandTypes.quadraticCurveTo: + assert(haveSeenMoveTo); + final engine.QuadraticCurveTo quadraticCurveTo = command; + // Compute quad curve distance. + distance = _computeQuadSegments( + currentX, + currentY, + quadraticCurveTo.x1, + quadraticCurveTo.y1, + quadraticCurveTo.x2, + quadraticCurveTo.y2, + distance, + 0, + _kMaxTValue); + break; + case engine.PathCommandTypes.close: + haveSeenClose = true; + break; + case engine.PathCommandTypes.ellipse: + final engine.Ellipse ellipse = command; + ellipseResult ??= _EllipseSegmentResult(); + _computeEllipseSegments( + currentX, + currentY, + distance, + ellipse.x, + ellipse.y, + ellipse.startAngle, + ellipse.endAngle, + ellipse.rotation, + ellipse.radiusX, + ellipse.radiusY, + ellipse.anticlockwise, + ellipseResult, + _segments); + distance = ellipseResult.distance; + currentX = ellipseResult.endPointX; + currentY = ellipseResult.endPointY; + _isClosed = true; + break; + case engine.PathCommandTypes.rRect: + final engine.RRectCommand rrectCommand = command; + final RRect rrect = rrectCommand.rrect; + engine.RRectMetricsRenderer(moveToCallback: (double x, double y) { + currentX = x; + currentY = y; + _isClosed = true; + haveSeenMoveTo = true; + }, lineToCallback: (double x, double y) { + lineToHandler(x, y); + }, ellipseCallback: (double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise) { + ellipseResult ??= _EllipseSegmentResult(); + _computeEllipseSegments( + currentX, + currentY, + distance, + centerX, + centerY, + startAngle, + endAngle, + rotation, + radiusX, + radiusY, + antiClockwise, + ellipseResult, + _segments); + distance = ellipseResult.distance; + currentX = ellipseResult.endPointX; + currentY = ellipseResult.endPointY; + }).render(rrect); + _isClosed = true; + break; + case engine.PathCommandTypes.rect: + final engine.RectCommand rectCommand = command; + final double x = rectCommand.x; + final double y = rectCommand.y; + final double width = rectCommand.width; + final double height = rectCommand.height; + currentX = x; + currentY = y; + lineToHandler(x + width, y); + lineToHandler(x + width, y + height); + lineToHandler(x, y + height); + lineToHandler(x, y); + _isClosed = true; + break; + default: + throw UnimplementedError('Unknown path command $command'); + } + } + if (!_isClosed && _forceClosed && _segments.isNotEmpty) { + _PathSegment firstSegment = _segments.first; + lineToHandler(firstSegment.points[0], firstSegment.points[1]); + } + _contourLength = distance; + } + + static bool _tspanBigEnough(int tSpan) => (tSpan >> 10) != 0; + + static bool _cubicTooCurvy(double x0, double y0, double x1, double y1, + double x2, double y2, double x3, double y3) { + // Measure distance from start-end line at 1/3 and 2/3rds to control + // points. If distance is less than _fTolerance we should continue + // subdividing curve. Uses approx distance for speed. + // + // p1 = point 1/3rd between start,end points. + final double p1x = (x0 * 2 / 3) + (x3 / 3); + final double p1y = (y0 * 2 / 3) + (y3 / 3); + if ((p1x - x1).abs() > _fTolerance) { + return true; + } + if ((p1y - y1).abs() > _fTolerance) { + return true; + } + // p2 = point 2/3rd between start,end points. + final double p2x = (x0 / 3) + (x3 * 2 / 3); + final double p2y = (y0 / 3) + (y3 * 2 / 3); + if ((p2x - x2).abs() > _fTolerance) { + return true; + } + if ((p2y - y2).abs() > _fTolerance) { + return true; + } + return false; + } + + // Recursively subdivides cubic and adds segments. + static double _computeCubicSegments( + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + double distance, + int tMin, + int tMax, + List<_PathSegment> segments) { + if (_tspanBigEnough(tMax - tMin) && + _cubicTooCurvy(x0, y0, x1, y1, x2, y2, x3, y3)) { + // Chop cubic into two halves (De Cateljau's algorithm) + // See https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm + final double abX = (x0 + x1) / 2; + final double abY = (y0 + y1) / 2; + final double bcX = (x1 + x2) / 2; + final double bcY = (y1 + y2) / 2; + final double cdX = (x2 + x3) / 2; + final double cdY = (y2 + y3) / 2; + final double abcX = (abX + bcX) / 2; + final double abcY = (abY + bcY) / 2; + final double bcdX = (bcX + cdX) / 2; + final double bcdY = (bcY + cdY) / 2; + final double abcdX = (abcX + bcdX) / 2; + final double abcdY = (abcY + bcdY) / 2; + final int tHalf = (tMin + tMax) >> 1; + distance = _computeCubicSegments( + x0, y0, abX, abY, abcX, abcY, abcdX, abcdY, distance, tMin, tHalf, segments); + distance = _computeCubicSegments( + abcdX, abcdY, bcdX, bcdY, cdX, cdY, x3, y3, distance, tHalf, tMax, segments); + } else { + final double dx = x0 - x3; + final double dy = y0 - y3; + final double startToEndDistance = math.sqrt(dx * dx + dy * dy); + final double prevDistance = distance; + distance += startToEndDistance; + if (distance > prevDistance) { + segments.add(_PathSegment(engine.PathCommandTypes.bezierCurveTo, + distance, [x0, y0, x1, y1, x2, y2, x3, y3])); + } + } + return distance; + } + + static bool _quadTooCurvy( + double x0, double y0, double x1, double y1, double x2, double y2) { + // (a/4 + b/2 + c/4) - (a/2 + c/2) = -a/4 + b/2 - c/4 + final double dx = (x1 / 2) - (x0 + x2) / 4; + if (dx.abs() > _fTolerance) { + return true; + } + final double dy = (y1 / 2) - (y0 + y2) / 4; + if (dy.abs() > _fTolerance) { + return true; + } + return false; + } + + double _computeQuadSegments(double x0, double y0, double x1, double y1, + double x2, double y2, double distance, int tMin, int tMax) { + if (_tspanBigEnough(tMax - tMin) && _quadTooCurvy(x0, y0, x1, y1, x2, y2)) { + final double p01x = (x0 + x1) / 2; + final double p01y = (y0 + y1) / 2; + final double p12x = (x1 + x2) / 2; + final double p12y = (y1 + y2) / 2; + final double p012x = (p01x + p12x) / 2; + final double p012y = (p01y + p12y) / 2; + final int tHalf = (tMin + tMax) >> 1; + distance = _computeQuadSegments( + x0, y0, p01x, p01y, p012x, p012y, distance, tMin, tHalf); + distance = _computeQuadSegments( + p012x, p012y, p12x, p12y, x2, y2, distance, tMin, tHalf); + } else { + final double dx = x0 - x2; + final double dy = y0 - y2; + final double startToEndDistance = math.sqrt(dx * dx + dy * dy); + final double prevDistance = distance; + distance += startToEndDistance; + if (distance > prevDistance) { + _segments.add(_PathSegment(engine.PathCommandTypes.quadraticCurveTo, + distance, [x0, y0, x1, y1, x2, y2])); + } + } + return distance; + } + + // Create segments by converting arc to cubics. + // See http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter. + static void _computeEllipseSegments( + double startX, + double startY, + double distance, + double cx, + double cy, + double startAngle, + double endAngle, + double rotation, + double radiusX, + double radiusY, + bool anticlockwise, + _EllipseSegmentResult result, + List<_PathSegment> segments) { + final double endX = cx + (radiusX * math.cos(endAngle)); + final double endY = cy + (radiusY * math.sin(endAngle)); + result.endPointX = endX; + result.endPointY = endY; + // Check for http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters + // Treat as line segment from start to end if arc has zero radii. + // If start and end point are the same treat as zero length path. + if ((radiusX == 0 || radiusY == 0) || (startX == endX && startY == endY)) { + result.distance = distance; + return; + } + final double rxAbs = radiusX.abs(); + final double ryAbs = radiusY.abs(); + + final double theta1 = startAngle; + final double theta2 = endAngle; + final double thetaArc = theta2 - theta1; + + // Add 0.01f to make sure we have enough segments when thetaArc is close + // to pi/2. + final int numSegments = (thetaArc / ((math.pi / 2.0) + 0.01)).abs().ceil(); + double x0 = startX; + double y0 = startY; + for (int segmentIndex = 0; segmentIndex < numSegments; segmentIndex++) { + final double startTheta = + theta1 + (segmentIndex * thetaArc / numSegments); + final double endTheta = + theta1 + ((segmentIndex + 1) * thetaArc / numSegments); + final double t = (4.0 / 3.0) * math.tan((endTheta - startTheta) / 4); + if (!t.isFinite) { + result.distance = distance; + return; + } + final double sinStartTheta = math.sin(startTheta); + final double cosStartTheta = math.cos(startTheta); + final double sinEndTheta = math.sin(endTheta); + final double cosEndTheta = math.cos(endTheta); + + // Compute cubic segment start, control point and end (target). + final double p1x = rxAbs * (cosStartTheta - t * sinStartTheta) + cx; + final double p1y = ryAbs * (sinStartTheta + t * cosStartTheta) + cy; + final double targetPointX = rxAbs * cosEndTheta + cx; + final double targetPointY = ryAbs * sinEndTheta + cy; + final double p2x = targetPointX + rxAbs * (t * sinEndTheta); + final double p2y = targetPointY + ryAbs * (-t * cosEndTheta); + + distance = _computeCubicSegments(x0, y0, p1x, p1y, p2x, p2y, targetPointX, + targetPointY, distance, 0, _kMaxTValue, segments); + x0 = targetPointX; + y0 = targetPointY; + } + result.distance = distance; + } @override String toString() => 'PathMetric'; } +class _EllipseSegmentResult { + double endPointX; + double endPointY; + double distance; + _EllipseSegmentResult(); +} + +class _PathSegment { + _PathSegment(this.segmentType, this.distance, this.points); + + final int segmentType; + final double distance; + final List points; +} + /// The geometric description of a tangent: the angle at a point. /// /// See also: diff --git a/lib/web_ui/test/engine/path_metrics_test.dart b/lib/web_ui/test/engine/path_metrics_test.dart new file mode 100644 index 0000000000000..44fd9e0aab055 --- /dev/null +++ b/lib/web_ui/test/engine/path_metrics_test.dart @@ -0,0 +1,232 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'package:ui/ui.dart'; +import 'package:test/test.dart'; + +import '../matchers.dart'; + +const double kTolerance = 0.001; + +void main() { + group('PathMetric length', () { + test('empty path', () { + Path path = Path(); + expect(path.computeMetrics().isEmpty, isTrue); + }); + + test('simple line', () { + Path path = Path(); + path.moveTo(100.0, 50.0); + path.lineTo(200.0, 100.0); + expect(path.computeMetrics().isEmpty, isFalse); + final List metrics = path.computeMetrics().toList(); + expect(metrics.length, 1); + expect(metrics[0].length, within(distance: kTolerance, from: 111.803)); + }); + + test('2 lines', () { + Path path = Path(); + path.moveTo(100.0, 50.0); + path.lineTo(200.0, 50.0); + path.lineTo(100.0, 200.0); + expect(path.computeMetrics().isEmpty, isFalse); + final List metrics = path.computeMetrics().toList(); + expect(metrics.length, 1); + expect(metrics[0].length, within(distance: kTolerance, from: 280.277)); + }); + + test('2 lines forceClosed', () { + Path path = Path(); + path.moveTo(100.0, 50.0); + path.lineTo(200.0, 50.0); + path.lineTo(100.0, 200.0); + expect(path.computeMetrics(forceClosed: true).isEmpty, isFalse); + final List metrics = + path.computeMetrics(forceClosed: true).toList(); + expect(metrics.length, 1); + expect(metrics[0].length, within(distance: kTolerance, from: 430.277)); + }); + + test('2 subpaths', () { + Path path = Path(); + path.moveTo(100.0, 50.0); + path.lineTo(200.0, 100.0); + path.moveTo(200.0, 100.0); + path.lineTo(200.0, 200.0); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 2); + expect(contourLengths[0], within(distance: kTolerance, from: 111.803)); + expect(contourLengths[1], within(distance: kTolerance, from: 100.0)); + }); + + test('quadratic curve', () { + Path path = Path(); + path.moveTo(20, 100); + path.quadraticBezierTo(80, 10, 140, 110); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 159.473)); + }); + + test('cubic curve', () { + Path path = Path(); + path.moveTo(20, 100); + path.cubicTo(80, 10, 120, 90, 140, 40); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 146.567)); + }); + + test('addRect', () { + Path path = Path(); + path.addRect(Rect.fromLTRB(20, 30, 220, 130)); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 600.0)); + }); + + test('addRRect with zero radius', () { + Path path = Path(); + path.addRRect(RRect.fromLTRBR(20, 30, 220, 130, Radius.circular(0))); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 600.0)); + }); + + test('addRRect with elliptical radius', () { + Path path = Path(); + path.addRRect(RRect.fromLTRBR(20, 30, 220, 130, Radius.elliptical(8, 4))); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 590.361)); + }); + + test('arcToPoint < 90 degrees', () { + const double rx = 100; + const double ry = 100; + const double cx = 150; + const double cy = 100; + const double startAngle = 0.0; + const double endAngle = 90.0; + double startRad = startAngle * math.pi / 180.0; + double endRad = endAngle * math.pi / 180.0; + + final double startX = cx + (rx * math.cos(startRad)); + final double startY = cy + (ry * math.sin(startRad)); + final double endX = cx + (rx * math.cos(endRad)); + final double endY = cy + (ry * math.sin(endRad)); + + final bool clockwise = endAngle > startAngle; + final bool largeArc = (endAngle - startAngle).abs() > 180.0; + final Path path = Path() + ..moveTo(startX, startY) + ..arcToPoint(Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + clockwise: clockwise, + largeArc: largeArc, + rotation: 0.0); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 156.994)); + }); + + test('arcToPoint 180 degrees', () { + const double rx = 100; + const double ry = 100; + const double cx = 150; + const double cy = 100; + const double startAngle = 0.0; + const double endAngle = 180.0; + double startRad = startAngle * math.pi / 180.0; + double endRad = endAngle * math.pi / 180.0; + + final double startX = cx + (rx * math.cos(startRad)); + final double startY = cy + (ry * math.sin(startRad)); + final double endX = cx + (rx * math.cos(endRad)); + final double endY = cy + (ry * math.sin(endRad)); + + final bool clockwise = endAngle > startAngle; + final bool largeArc = (endAngle - startAngle).abs() > 180.0; + final Path path = Path() + ..moveTo(startX, startY) + ..arcToPoint(Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + clockwise: clockwise, + largeArc: largeArc, + rotation: 0.0); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 313.989)); + }); + + test('arcToPoint 270 degrees', () { + const double rx = 100; + const double ry = 100; + const double cx = 150; + const double cy = 100; + const double startAngle = 0.0; + const double endAngle = 270.0; + double startRad = startAngle * math.pi / 180.0; + double endRad = endAngle * math.pi / 180.0; + + final double startX = cx + (rx * math.cos(startRad)); + final double startY = cy + (ry * math.sin(startRad)); + final double endX = cx + (rx * math.cos(endRad)); + final double endY = cy + (ry * math.sin(endRad)); + + final bool clockwise = endAngle > startAngle; + final bool largeArc = (endAngle - startAngle).abs() > 180.0; + final Path path = Path() + ..moveTo(startX, startY) + ..arcToPoint(Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + clockwise: clockwise, + largeArc: largeArc, + rotation: 0.0); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 470.983)); + }); + + test('arcToPoint 270 degrees rx!=ry', () { + const double rx = 100; + const double ry = 50; + const double cx = 150; + const double cy = 100; + const double startAngle = 0.0; + const double endAngle = 270.0; + double startRad = startAngle * math.pi / 180.0; + double endRad = endAngle * math.pi / 180.0; + + final double startX = cx + (rx * math.cos(startRad)); + final double startY = cy + (ry * math.sin(startRad)); + final double endX = cx + (rx * math.cos(endRad)); + final double endY = cy + (ry * math.sin(endRad)); + + final bool clockwise = endAngle > startAngle; + final bool largeArc = (endAngle - startAngle).abs() > 180.0; + final Path path = Path() + ..moveTo(startX, startY) + ..arcToPoint(Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + clockwise: clockwise, + largeArc: largeArc, + rotation: 0.0); + final List contourLengths = computeLengths(path.computeMetrics()); + expect(contourLengths.length, 1); + expect(contourLengths[0], within(distance: kTolerance, from: 363.090)); + }); + }); +} + +List computeLengths(PathMetrics pathMetrics) { + final List lengths = []; + for (PathMetric metric in pathMetrics) { + lengths.add(metric.length); + } + return lengths; +} diff --git a/lib/web_ui/test/matchers.dart b/lib/web_ui/test/matchers.dart index d06ee239cca7b..24c2b972f5e74 100644 --- a/lib/web_ui/test/matchers.dart +++ b/lib/web_ui/test/matchers.dart @@ -116,8 +116,7 @@ double _sizeDistance(Size a, Size b) { /// The distance is computed by a [DistanceFunction]. /// /// If `distanceFunction` is null, a standard distance function is used for the -/// `runtimeType` of the `from` argument. Standard functions are defined for -/// the following types: +/// type `T` . Standard functions are defined for the following types: /// /// * [Color], whose distance is the maximum component-wise delta. /// * [Offset], whose distance is the Euclidean distance computed using the From 86e82fc8871fb3ae5b9fde00862213c7574d16a3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 19 Nov 2019 12:42:43 -0500 Subject: [PATCH 181/591] Roll src/third_party/skia d3ddcb403993..c5e528e15b1f (1 commits) (#13916) https://skia.googlesource.com/skia.git/+log/d3ddcb403993..c5e528e15b1f git log d3ddcb403993..c5e528e15b1f --date=short --no-merges --format='%ad %ae %s' 2019-11-19 benjaminwagner@google.com Update Valgrind to 3.15.0. Created with: gclient setdep -r src/third_party/skia@c5e528e15b1f If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ac02c918fa264..b2871ec6828d4 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd3ddcb4039935aae7132f5e6c09d1e519991fde4', + 'skia_revision': 'c5e528e15b1f2270714c5c19b4ba3fe922f64881', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 27d8127319cb1..d97baea774948 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6c4c894c8101b49b59d8dfc06b67ddfa +Signature: e2786ca638b91b01bb4302bb337a15ac UNUSED LICENSES: From f4fba66c2fad1c1d5705ea0734ee4250211f6757 Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Tue, 19 Nov 2019 09:48:04 -0800 Subject: [PATCH 182/591] Adding opacity -> alpha method to Color class (#13902) --- lib/ui/painting.dart | 8 ++++++++ lib/web_ui/lib/src/ui/painting.dart | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 0eacb1bca4e07..b3861970b55d6 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -307,6 +307,14 @@ class Color { } } + /// Returns an alpha value representative of the provided [opacity] value. + /// + /// The [opacity] value may not be null. + static int getAlphaFromOpacity(double opacity) { + assert(opacity != null); + return (opacity.clamp(0.0, 1.0) * 255).round(); + } + @override bool operator ==(dynamic other) { if (identical(this, other)) diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index ff280ad0bda16..09a268c97afdb 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -226,6 +226,14 @@ class Color { } } + /// Returns an alpha value representative of the provided [opacity] value. + /// + /// The [opacity] value may not be null. + static int getAlphaFromOpacity(double opacity) { + assert(opacity != null); + return (opacity.clamp(0.0, 1.0) * 255).round(); + } + @override bool operator ==(dynamic other) { if (identical(this, other)) { From 132d38cd222a9e9009d969defbdde658ad3079af Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Tue, 19 Nov 2019 09:48:25 -0800 Subject: [PATCH 183/591] Moves pointer event sanitizing to engine. (#13697) * Moves pointer event sanitizing to engine * fix comment format * fix formatting * addressing comment * fix format * fix format * addressing comment --- ci/licenses_golden/licenses_flutter | 3 + lib/ui/BUILD.gn | 3 + lib/ui/hooks.dart | 9 +- lib/ui/pointer.dart | 28 + lib/ui/window/pointer_data.cc | 5 +- lib/ui/window/pointer_data.h | 7 + .../window/pointer_data_packet_converter.cc | 288 ++++++++++ lib/ui/window/pointer_data_packet_converter.h | 111 ++++ ...pointer_data_packet_converter_unittests.cc | 537 ++++++++++++++++++ lib/web_ui/lib/src/ui/pointer.dart | 28 + shell/common/fixtures/shell_test.dart | 8 +- shell/common/input_events_unittests.cc | 154 +++++ shell/common/platform_view.cc | 3 +- shell/common/platform_view.h | 2 + shell/common/shell_test.cc | 17 +- shell/common/shell_test.h | 3 +- .../android/AndroidTouchProcessor.java | 7 +- .../framework/Source/FlutterViewController.mm | 10 + shell/platform/embedder/embedder.cc | 5 + 19 files changed, 1212 insertions(+), 16 deletions(-) create mode 100644 lib/ui/window/pointer_data_packet_converter.cc create mode 100644 lib/ui/window/pointer_data_packet_converter.h create mode 100644 lib/ui/window/pointer_data_packet_converter_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a2b25ba2b017e..c6bcdb3230073 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -344,6 +344,9 @@ FILE: ../../../flutter/lib/ui/window/pointer_data.cc FILE: ../../../flutter/lib/ui/window/pointer_data.h FILE: ../../../flutter/lib/ui/window/pointer_data_packet.cc FILE: ../../../flutter/lib/ui/window/pointer_data_packet.h +FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc +FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h +FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter_unittests.cc FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc FILE: ../../../flutter/lib/ui/window/viewport_metrics.h FILE: ../../../flutter/lib/ui/window/window.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 003efe1cb1b66..d548d94594184 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -101,6 +101,8 @@ source_set("ui") { "window/pointer_data.h", "window/pointer_data_packet.cc", "window/pointer_data_packet.h", + "window/pointer_data_packet_converter.cc", + "window/pointer_data_packet_converter.h", "window/viewport_metrics.cc", "window/viewport_metrics.h", "window/window.cc", @@ -161,6 +163,7 @@ if (current_toolchain == host_toolchain) { sources = [ "painting/image_decoder_unittests.cc", + "window/pointer_data_packet_converter_unittests.cc", ] deps = [ diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index cee8de37388a3..83ff3a1059cb4 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -308,8 +308,9 @@ void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1 // If this value changes, update the encoding code in the following files: // // * pointer_data.cc -// * FlutterView.java -const int _kPointerDataFieldCount = 24; +// * pointers.dart +// * AndroidTouchProcessor.java +const int _kPointerDataFieldCount = 28; PointerDataPacket _unpackPointerDataPacket(ByteData packet) { const int kStride = Int64List.bytesPerElement; @@ -325,10 +326,14 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) { kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], device: packet.getInt64(kStride * offset++, _kFakeHostEndian), + pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian), physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, + synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), diff --git a/lib/ui/pointer.dart b/lib/ui/pointer.dart index 4879e2f4d7bb7..83247ea6092f1 100644 --- a/lib/ui/pointer.dart +++ b/lib/ui/pointer.dart @@ -75,10 +75,14 @@ class PointerData { this.kind = PointerDeviceKind.touch, this.signalKind, this.device = 0, + this.pointerIdentifier = 0, this.physicalX = 0.0, this.physicalY = 0.0, + this.physicalDeltaX = 0.0, + this.physicalDeltaY = 0.0, this.buttons = 0, this.obscured = false, + this.synthesized = false, this.pressure = 0.0, this.pressureMin = 0.0, this.pressureMax = 0.0, @@ -111,6 +115,12 @@ class PointerData { /// Unique identifier for the pointing device, reused across interactions. final int device; + /// Unique identifier for the pointer. + /// + /// This field changes for each new pointer down event. Framework uses this + /// identifier to determine hit test result. + final int pointerIdentifier; + /// X coordinate of the position of the pointer, in physical pixels in the /// global coordinate space. final double physicalX; @@ -119,6 +129,12 @@ class PointerData { /// global coordinate space. final double physicalY; + /// The distance of pointer movement on X coordinate in physical pixels. + final double physicalDeltaX; + + /// The distance of pointer movement on Y coordinate in physical pixels. + final double physicalDeltaY; + /// Bit field using the *Button constants (primaryMouseButton, /// secondaryStylusButton, etc). For example, if this has the value 6 and the /// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an @@ -130,6 +146,14 @@ class PointerData { /// implemented.) final bool obscured; + /// Set if this pointer data was synthesized by pointer data packet converter. + /// pointer data packet converter will synthesize additional pointer datas if + /// the input sequence of pointer data is illegal. + /// + /// For example, a down pointer data will be synthesized if the converter receives + /// a move pointer data while the pointer is not previously down. + final bool synthesized; + /// The pressure of the touch as a number ranging from 0.0, indicating a touch /// with no discernible pressure, to 1.0, indicating a touch with "normal" /// pressure, and possibly beyond, indicating a stronger touch. For devices @@ -242,9 +266,13 @@ class PointerData { 'kind: $kind, ' 'signalKind: $signalKind, ' 'device: $device, ' + 'pointerIdentifier: $pointerIdentifier, ' 'physicalX: $physicalX, ' 'physicalY: $physicalY, ' + 'physicalDeltaX: $physicalDeltaX, ' + 'physicalDeltaY: $physicalDeltaY, ' 'buttons: $buttons, ' + 'synthesized: $synthesized, ' 'pressure: $pressure, ' 'pressureMin: $pressureMin, ' 'pressureMax: $pressureMax, ' diff --git a/lib/ui/window/pointer_data.cc b/lib/ui/window/pointer_data.cc index a2d92e8eed36e..a3292d486bab6 100644 --- a/lib/ui/window/pointer_data.cc +++ b/lib/ui/window/pointer_data.cc @@ -8,10 +8,7 @@ namespace flutter { -// If this value changes, update the pointer data unpacking code in hooks.dart. -static constexpr int kPointerDataFieldCount = 24; - -static_assert(sizeof(PointerData) == sizeof(int64_t) * kPointerDataFieldCount, +static_assert(sizeof(PointerData) == kBytesPerField * kPointerDataFieldCount, "PointerData has the wrong size"); void PointerData::Clear() { diff --git a/lib/ui/window/pointer_data.h b/lib/ui/window/pointer_data.h index 05adf0682133a..96a9e7d5dad91 100644 --- a/lib/ui/window/pointer_data.h +++ b/lib/ui/window/pointer_data.h @@ -9,6 +9,9 @@ namespace flutter { +// If this value changes, update the pointer data unpacking code in hooks.dart. +static constexpr int kPointerDataFieldCount = 28; +static constexpr int kBytesPerField = sizeof(int64_t); // Must match the button constants in events.dart. enum PointerButtonMouse : int64_t { kPointerButtonMousePrimary = 1 << 0, @@ -60,10 +63,14 @@ struct alignas(8) PointerData { DeviceKind kind; SignalKind signal_kind; int64_t device; + int64_t pointer_identifier; double physical_x; double physical_y; + double physical_delta_x; + double physical_delta_y; int64_t buttons; int64_t obscured; + int64_t synthesized; double pressure; double pressure_min; double pressure_max; diff --git a/lib/ui/window/pointer_data_packet_converter.cc b/lib/ui/window/pointer_data_packet_converter.cc new file mode 100644 index 0000000000000..c7f643f25417d --- /dev/null +++ b/lib/ui/window/pointer_data_packet_converter.cc @@ -0,0 +1,288 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/pointer_data_packet_converter.h" +#include "flutter/fml/logging.h" + +#include + +namespace flutter { + +PointerDataPacketConverter::PointerDataPacketConverter() : pointer_(0) {} + +PointerDataPacketConverter::~PointerDataPacketConverter() = default; + +std::unique_ptr PointerDataPacketConverter::Convert( + std::unique_ptr packet) { + size_t kBytesPerPointerData = kPointerDataFieldCount * kBytesPerField; + auto buffer = packet->data(); + size_t buffer_length = buffer.size(); + + std::vector converted_pointers; + // Converts each pointer data in the buffer and stores it in the + // converted_pointers. + for (size_t i = 0; i < buffer_length / kBytesPerPointerData; i++) { + PointerData pointer_data; + memcpy(&pointer_data, &buffer[i * kBytesPerPointerData], + sizeof(PointerData)); + ConvertPointerData(pointer_data, converted_pointers); + } + + // Writes converted_pointers into converted_packet. + auto converted_packet = + std::make_unique(converted_pointers.size()); + size_t count = 0; + for (auto& converted_pointer : converted_pointers) { + converted_packet->SetPointerData(count++, converted_pointer); + } + + return converted_packet; +} + +void PointerDataPacketConverter::ConvertPointerData( + PointerData pointer_data, + std::vector& converted_pointers) { + if (pointer_data.signal_kind == PointerData::SignalKind::kNone) { + switch (pointer_data.change) { + case PointerData::Change::kCancel: { + // Android's three finger gesture will send a cancel event + // to a non-existing pointer. Drops the cancel if pointer + // is not previously added. + // https://github.com/flutter/flutter/issues/20517 + auto iter = states_.find(pointer_data.device); + if (iter != states_.end()) { + PointerState state = iter->second; + FML_DCHECK(state.isDown); + UpdatePointerIdentifier(pointer_data, state, false); + + if (LocationNeedsUpdate(pointer_data, state)) { + // Synthesizes a move event if the location does not match. + PointerData synthesized_move_event = pointer_data; + synthesized_move_event.change = PointerData::Change::kMove; + synthesized_move_event.synthesized = 1; + + UpdateDeltaAndState(synthesized_move_event, state); + converted_pointers.push_back(synthesized_move_event); + } + + state.isDown = false; + states_[pointer_data.device] = state; + converted_pointers.push_back(pointer_data); + } + break; + } + case PointerData::Change::kAdd: { + FML_DCHECK(states_.find(pointer_data.device) == states_.end()); + EnsurePointerState(pointer_data); + converted_pointers.push_back(pointer_data); + break; + } + case PointerData::Change::kRemove: { + // Makes sure we have an existing pointer + auto iter = states_.find(pointer_data.device); + FML_DCHECK(iter != states_.end()); + PointerState state = iter->second; + + if (state.isDown) { + // Synthesizes cancel event if the pointer is down. + PointerData synthesized_cancel_event = pointer_data; + synthesized_cancel_event.change = PointerData::Change::kCancel; + synthesized_cancel_event.synthesized = 1; + UpdatePointerIdentifier(synthesized_cancel_event, state, false); + + state.isDown = false; + states_[synthesized_cancel_event.device] = state; + converted_pointers.push_back(synthesized_cancel_event); + } + + if (LocationNeedsUpdate(pointer_data, state)) { + // Synthesizes a hover event if the location does not match. + PointerData synthesized_hover_event = pointer_data; + synthesized_hover_event.change = PointerData::Change::kHover; + synthesized_hover_event.synthesized = 1; + + UpdateDeltaAndState(synthesized_hover_event, state); + converted_pointers.push_back(synthesized_hover_event); + } + + states_.erase(pointer_data.device); + converted_pointers.push_back(pointer_data); + break; + } + case PointerData::Change::kHover: { + auto iter = states_.find(pointer_data.device); + PointerState state; + if (iter == states_.end()) { + // Synthesizes add event if the pointer is not previously added. + PointerData synthesized_add_event = pointer_data; + synthesized_add_event.change = PointerData::Change::kAdd; + synthesized_add_event.synthesized = 1; + state = EnsurePointerState(synthesized_add_event); + converted_pointers.push_back(synthesized_add_event); + } else { + state = iter->second; + } + + FML_DCHECK(!state.isDown); + if (LocationNeedsUpdate(pointer_data, state)) { + UpdateDeltaAndState(pointer_data, state); + converted_pointers.push_back(pointer_data); + } + break; + } + case PointerData::Change::kDown: { + auto iter = states_.find(pointer_data.device); + PointerState state; + if (iter == states_.end()) { + // Synthesizes a add event if the pointer is not previously added. + PointerData synthesized_add_event = pointer_data; + synthesized_add_event.change = PointerData::Change::kAdd; + synthesized_add_event.synthesized = 1; + state = EnsurePointerState(synthesized_add_event); + converted_pointers.push_back(synthesized_add_event); + } else { + state = iter->second; + } + + FML_DCHECK(!state.isDown); + if (LocationNeedsUpdate(pointer_data, state)) { + // Synthesizes a hover event if the location does not match. + PointerData synthesized_hover_event = pointer_data; + synthesized_hover_event.change = PointerData::Change::kHover; + synthesized_hover_event.synthesized = 1; + + UpdateDeltaAndState(synthesized_hover_event, state); + converted_pointers.push_back(synthesized_hover_event); + } + + UpdatePointerIdentifier(pointer_data, state, true); + state.isDown = true; + states_[pointer_data.device] = state; + converted_pointers.push_back(pointer_data); + break; + } + case PointerData::Change::kMove: { + // Makes sure we have an existing pointer in down state + auto iter = states_.find(pointer_data.device); + FML_DCHECK(iter != states_.end()); + PointerState state = iter->second; + FML_DCHECK(state.isDown); + + if (LocationNeedsUpdate(pointer_data, state)) { + UpdatePointerIdentifier(pointer_data, state, false); + UpdateDeltaAndState(pointer_data, state); + converted_pointers.push_back(pointer_data); + } + break; + } + case PointerData::Change::kUp: { + // Makes sure we have an existing pointer in down state + auto iter = states_.find(pointer_data.device); + FML_DCHECK(iter != states_.end()); + PointerState state = iter->second; + FML_DCHECK(state.isDown); + + UpdatePointerIdentifier(pointer_data, state, false); + + if (LocationNeedsUpdate(pointer_data, state)) { + // Synthesizes a move event if the location does not match. + PointerData synthesized_move_event = pointer_data; + synthesized_move_event.change = PointerData::Change::kMove; + synthesized_move_event.synthesized = 1; + + UpdateDeltaAndState(synthesized_move_event, state); + converted_pointers.push_back(synthesized_move_event); + } + + state.isDown = false; + states_[pointer_data.device] = state; + converted_pointers.push_back(pointer_data); + break; + } + default: { + converted_pointers.push_back(pointer_data); + break; + } + } + } else { + switch (pointer_data.signal_kind) { + case PointerData::SignalKind::kScroll: { + // Makes sure we have an existing pointer + auto iter = states_.find(pointer_data.device); + FML_DCHECK(iter != states_.end()); + + PointerState state = iter->second; + if (LocationNeedsUpdate(pointer_data, state)) { + if (state.isDown) { + // Synthesizes a move event if the pointer is down. + PointerData synthesized_move_event = pointer_data; + synthesized_move_event.signal_kind = PointerData::SignalKind::kNone; + synthesized_move_event.change = PointerData::Change::kMove; + synthesized_move_event.synthesized = 1; + + UpdateDeltaAndState(synthesized_move_event, state); + converted_pointers.push_back(synthesized_move_event); + } else { + // Synthesizes a hover event if the pointer is up. + PointerData synthesized_hover_event = pointer_data; + synthesized_hover_event.signal_kind = + PointerData::SignalKind::kNone; + synthesized_hover_event.change = PointerData::Change::kHover; + synthesized_hover_event.synthesized = 1; + + UpdateDeltaAndState(synthesized_hover_event, state); + converted_pointers.push_back(synthesized_hover_event); + } + } + + converted_pointers.push_back(pointer_data); + break; + } + default: { + // Ignores unknown signal kind. + break; + } + } + } +} + +PointerState PointerDataPacketConverter::EnsurePointerState( + PointerData pointer_data) { + PointerState state; + state.pointer_identifier = 0; + state.isDown = false; + state.physical_x = pointer_data.physical_x; + state.physical_y = pointer_data.physical_y; + states_[pointer_data.device] = state; + return state; +} + +void PointerDataPacketConverter::UpdateDeltaAndState(PointerData& pointer_data, + PointerState& state) { + pointer_data.physical_delta_x = pointer_data.physical_x - state.physical_x; + pointer_data.physical_delta_y = pointer_data.physical_y - state.physical_y; + state.physical_x = pointer_data.physical_x; + state.physical_y = pointer_data.physical_y; + states_[pointer_data.device] = state; +} + +bool PointerDataPacketConverter::LocationNeedsUpdate( + const PointerData pointer_data, + const PointerState state) { + return state.physical_x != pointer_data.physical_x || + state.physical_y != pointer_data.physical_y; +} + +void PointerDataPacketConverter::UpdatePointerIdentifier( + PointerData& pointer_data, + PointerState& state, + bool start_new_pointer) { + if (start_new_pointer) { + state.pointer_identifier = ++pointer_; + states_[pointer_data.device] = state; + } + pointer_data.pointer_identifier = state.pointer_identifier; +} + +} // namespace flutter diff --git a/lib/ui/window/pointer_data_packet_converter.h b/lib/ui/window/pointer_data_packet_converter.h new file mode 100644 index 0000000000000..29024d07fbd32 --- /dev/null +++ b/lib/ui/window/pointer_data_packet_converter.h @@ -0,0 +1,111 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_CONVERTER_H_ +#define FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_CONVERTER_H_ + +#include + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/pointer_data_packet.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// The current information about a pointer. This struct is used by +/// PointerDataPacketConverter to fill in necesarry information for raw pointer +/// packet sent from embedding. +/// +struct PointerState { + int64_t pointer_identifier; + bool isDown; + double physical_x; + double physical_y; +}; + +//------------------------------------------------------------------------------ +/// Converter to convert the raw pointer data packet from the platforms. +/// +/// Framework requires certain information to process pointer data. e.g. pointer +/// identifier and the delta of pointer moment. The converter keeps track each +/// pointer state and fill in those information appropriately. +/// +/// The converter is also resposible for providing a clean pointer data stream. +/// It will attempt to correct the stream if the it contains illegal pointer +/// transitions. +/// +/// Example 1 Missing Add: +/// +/// Down(position x) -> Up(position x) +/// +/// ###After Conversion### +/// +/// Synthesized_Add(position x) -> Down(position x) -> Up(position x) +/// +/// Example 2 Missing another move: +/// +/// Add(position x) -> Down(position x) -> Move(position y) -> +/// Up(position z) +/// +/// ###After Conversion### +/// +/// Add(position x) -> Down(position x) -> Move(position y) -> +/// Synthesized_Move(position z) -> Up(position z) +/// +/// Platform view is the only client that uses this class to convert all the +/// incoming pointer packet and is responsible for the life cycle of its +/// instance. +/// +class PointerDataPacketConverter { + public: + PointerDataPacketConverter(); + ~PointerDataPacketConverter(); + + //---------------------------------------------------------------------------- + /// @brief Converts pointer data packet into a form that framework + /// understands. The raw pointer data packet from embedding does + /// not have sufficient information and may contain illegal + /// pointer transitions. This method will fill out that + /// information and attempt to correct pointer transitions. + /// + /// @param[in] packet The raw pointer packet sent from + /// embedding. + /// + /// @return A full converted packet with all the required information + /// filled. + /// It may contain synthetic pointer data as the result of + /// converter's attempt to correct illegal pointer transitions. + /// + std::unique_ptr Convert( + std::unique_ptr packet); + + private: + std::map states_; + + int64_t pointer_; + + void ConvertPointerData(PointerData pointer_data, + std::vector& converted_pointers); + + PointerState EnsurePointerState(PointerData pointer_data); + + void UpdateDeltaAndState(PointerData& pointer_data, PointerState& state); + + void UpdatePointerIdentifier(PointerData& pointer_data, + PointerState& state, + bool start_new_pointer); + + bool LocationNeedsUpdate(const PointerData pointer_data, + const PointerState state); + + FML_DISALLOW_COPY_AND_ASSIGN(PointerDataPacketConverter); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_CONVERTER_H_ diff --git a/lib/ui/window/pointer_data_packet_converter_unittests.cc b/lib/ui/window/pointer_data_packet_converter_unittests.cc new file mode 100644 index 0000000000000..d18b7f3e474ed --- /dev/null +++ b/lib/ui/window/pointer_data_packet_converter_unittests.cc @@ -0,0 +1,537 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/pointer_data_packet_converter.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +void CreateSimulatedPointerData(PointerData& data, + PointerData::Change change, + int64_t device, + double dx, + double dy) { + data.time_stamp = 0; + data.change = change; + data.kind = PointerData::DeviceKind::kTouch; + data.signal_kind = PointerData::SignalKind::kNone; + data.device = device; + data.pointer_identifier = 0; + data.physical_x = dx; + data.physical_y = dy; + data.physical_delta_x = 0.0; + data.physical_delta_y = 0.0; + data.buttons = 0; + data.obscured = 0; + data.synthesized = 0; + data.pressure = 0.0; + data.pressure_min = 0.0; + data.pressure_max = 0.0; + data.distance = 0.0; + data.distance_max = 0.0; + data.size = 0.0; + data.radius_major = 0.0; + data.radius_minor = 0.0; + data.radius_min = 0.0; + data.radius_max = 0.0; + data.orientation = 0.0; + data.tilt = 0.0; + data.platformData = 0; + data.scroll_delta_x = 0.0; + data.scroll_delta_y = 0.0; +} + +void CreateSimulatedMousePointerData(PointerData& data, + PointerData::Change change, + PointerData::SignalKind signal_kind, + int64_t device, + double dx, + double dy, + double scroll_delta_x, + double scroll_delta_y) { + data.time_stamp = 0; + data.change = change; + data.kind = PointerData::DeviceKind::kMouse; + data.signal_kind = signal_kind; + data.device = device; + data.pointer_identifier = 0; + data.physical_x = dx; + data.physical_y = dy; + data.physical_delta_x = 0.0; + data.physical_delta_y = 0.0; + data.buttons = 0; + data.obscured = 0; + data.synthesized = 0; + data.pressure = 0.0; + data.pressure_min = 0.0; + data.pressure_max = 0.0; + data.distance = 0.0; + data.distance_max = 0.0; + data.size = 0.0; + data.radius_major = 0.0; + data.radius_minor = 0.0; + data.radius_min = 0.0; + data.radius_max = 0.0; + data.orientation = 0.0; + data.tilt = 0.0; + data.platformData = 0; + data.scroll_delta_x = scroll_delta_x; + data.scroll_delta_y = scroll_delta_y; +} + +void UnpackPointerPacket(std::vector& output, + std::unique_ptr packet) { + size_t kBytesPerPointerData = kPointerDataFieldCount * kBytesPerField; + auto buffer = packet->data(); + size_t buffer_length = buffer.size(); + + for (size_t i = 0; i < buffer_length / kBytesPerPointerData; i++) { + PointerData pointer_data; + memcpy(&pointer_data, &buffer[i * kBytesPerPointerData], + sizeof(PointerData)); + output.push_back(pointer_data); + } + packet.reset(); +} + +TEST(PointerDataPacketConverterTest, CanConvetPointerDataPacket) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(6); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kHover, 0, 3.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 4.0); + packet->SetPointerData(3, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0); + packet->SetPointerData(4, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0); + packet->SetPointerData(5, data); + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)6); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].synthesized, 0); + + ASSERT_EQ(result[1].change, PointerData::Change::kHover); + ASSERT_EQ(result[1].synthesized, 0); + ASSERT_EQ(result[1].physical_delta_x, 3.0); + ASSERT_EQ(result[1].physical_delta_y, 0.0); + + ASSERT_EQ(result[2].change, PointerData::Change::kDown); + ASSERT_EQ(result[2].pointer_identifier, 1); + ASSERT_EQ(result[2].synthesized, 0); + + ASSERT_EQ(result[3].change, PointerData::Change::kMove); + ASSERT_EQ(result[3].pointer_identifier, 1); + ASSERT_EQ(result[3].synthesized, 0); + ASSERT_EQ(result[3].physical_delta_x, 0.0); + ASSERT_EQ(result[3].physical_delta_y, 4.0); + + ASSERT_EQ(result[4].change, PointerData::Change::kUp); + ASSERT_EQ(result[4].pointer_identifier, 1); + ASSERT_EQ(result[4].synthesized, 0); + + ASSERT_EQ(result[5].change, PointerData::Change::kRemove); + ASSERT_EQ(result[5].synthesized, 0); +} + +TEST(PointerDataPacketConverterTest, CanSynthesizeDownAndUp) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(4); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0); + packet->SetPointerData(3, data); + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)6); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].synthesized, 0); + + // A hover should be synthesized. + ASSERT_EQ(result[1].change, PointerData::Change::kHover); + ASSERT_EQ(result[1].synthesized, 1); + ASSERT_EQ(result[1].physical_delta_x, 3.0); + ASSERT_EQ(result[1].physical_delta_y, 0.0); + + ASSERT_EQ(result[2].change, PointerData::Change::kDown); + ASSERT_EQ(result[2].pointer_identifier, 1); + ASSERT_EQ(result[2].synthesized, 0); + + // A move should be synthesized. + ASSERT_EQ(result[3].change, PointerData::Change::kMove); + ASSERT_EQ(result[3].pointer_identifier, 1); + ASSERT_EQ(result[3].synthesized, 1); + ASSERT_EQ(result[3].physical_delta_x, 0.0); + ASSERT_EQ(result[3].physical_delta_y, 4.0); + + ASSERT_EQ(result[4].change, PointerData::Change::kUp); + ASSERT_EQ(result[4].pointer_identifier, 1); + ASSERT_EQ(result[4].synthesized, 0); + + ASSERT_EQ(result[5].change, PointerData::Change::kRemove); + ASSERT_EQ(result[5].synthesized, 0); +} + +TEST(PointerDataPacketConverterTest, CanUpdatePointerIdentifier) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(7); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + packet->SetPointerData(3, data); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0); + packet->SetPointerData(4, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0); + packet->SetPointerData(5, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0); + packet->SetPointerData(6, data); + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)7); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].synthesized, 0); + + ASSERT_EQ(result[1].change, PointerData::Change::kDown); + ASSERT_EQ(result[1].pointer_identifier, 1); + ASSERT_EQ(result[1].synthesized, 0); + + ASSERT_EQ(result[2].change, PointerData::Change::kUp); + ASSERT_EQ(result[2].pointer_identifier, 1); + ASSERT_EQ(result[2].synthesized, 0); + + // Pointer count increase to 2. + ASSERT_EQ(result[3].change, PointerData::Change::kDown); + ASSERT_EQ(result[3].pointer_identifier, 2); + ASSERT_EQ(result[3].synthesized, 0); + + ASSERT_EQ(result[4].change, PointerData::Change::kMove); + ASSERT_EQ(result[4].pointer_identifier, 2); + ASSERT_EQ(result[4].synthesized, 0); + ASSERT_EQ(result[4].physical_delta_x, 3.0); + ASSERT_EQ(result[4].physical_delta_y, 0.0); + + ASSERT_EQ(result[5].change, PointerData::Change::kUp); + ASSERT_EQ(result[5].pointer_identifier, 2); + ASSERT_EQ(result[5].synthesized, 0); + + ASSERT_EQ(result[6].change, PointerData::Change::kRemove); + ASSERT_EQ(result[6].synthesized, 0); +} + +TEST(PointerDataPacketConverterTest, CanWorkWithDifferentDevices) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(12); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 1, 0.0, 0.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 0.0, 0.0); + packet->SetPointerData(3, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + packet->SetPointerData(4, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + packet->SetPointerData(5, data); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 1, 0.0, 4.0); + packet->SetPointerData(6, data); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0); + packet->SetPointerData(7, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 1, 0.0, 4.0); + packet->SetPointerData(8, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0); + packet->SetPointerData(9, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0); + packet->SetPointerData(10, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 1, 0.0, 4.0); + packet->SetPointerData(11, data); + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)12); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].device, 0); + ASSERT_EQ(result[0].synthesized, 0); + + ASSERT_EQ(result[1].change, PointerData::Change::kDown); + ASSERT_EQ(result[1].device, 0); + ASSERT_EQ(result[1].pointer_identifier, 1); + ASSERT_EQ(result[1].synthesized, 0); + + ASSERT_EQ(result[2].change, PointerData::Change::kAdd); + ASSERT_EQ(result[2].device, 1); + ASSERT_EQ(result[2].synthesized, 0); + + ASSERT_EQ(result[3].change, PointerData::Change::kDown); + ASSERT_EQ(result[3].device, 1); + ASSERT_EQ(result[3].pointer_identifier, 2); + ASSERT_EQ(result[3].synthesized, 0); + + ASSERT_EQ(result[4].change, PointerData::Change::kUp); + ASSERT_EQ(result[4].device, 0); + ASSERT_EQ(result[4].pointer_identifier, 1); + ASSERT_EQ(result[4].synthesized, 0); + + ASSERT_EQ(result[5].change, PointerData::Change::kDown); + ASSERT_EQ(result[5].device, 0); + ASSERT_EQ(result[5].pointer_identifier, 3); + ASSERT_EQ(result[5].synthesized, 0); + + ASSERT_EQ(result[6].change, PointerData::Change::kMove); + ASSERT_EQ(result[6].device, 1); + ASSERT_EQ(result[6].pointer_identifier, 2); + ASSERT_EQ(result[6].synthesized, 0); + ASSERT_EQ(result[6].physical_delta_x, 0.0); + ASSERT_EQ(result[6].physical_delta_y, 4.0); + + ASSERT_EQ(result[7].change, PointerData::Change::kMove); + ASSERT_EQ(result[7].device, 0); + ASSERT_EQ(result[7].pointer_identifier, 3); + ASSERT_EQ(result[7].synthesized, 0); + ASSERT_EQ(result[7].physical_delta_x, 3.0); + ASSERT_EQ(result[7].physical_delta_y, 0.0); + + ASSERT_EQ(result[8].change, PointerData::Change::kUp); + ASSERT_EQ(result[8].device, 1); + ASSERT_EQ(result[8].pointer_identifier, 2); + ASSERT_EQ(result[8].synthesized, 0); + + ASSERT_EQ(result[9].change, PointerData::Change::kUp); + ASSERT_EQ(result[9].device, 0); + ASSERT_EQ(result[9].pointer_identifier, 3); + ASSERT_EQ(result[9].synthesized, 0); + + ASSERT_EQ(result[10].change, PointerData::Change::kRemove); + ASSERT_EQ(result[10].device, 0); + ASSERT_EQ(result[10].synthesized, 0); + + ASSERT_EQ(result[11].change, PointerData::Change::kRemove); + ASSERT_EQ(result[11].device, 1); + ASSERT_EQ(result[11].synthesized, 0); +} + +TEST(PointerDataPacketConverterTest, CanSynthesizeAdd) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(2); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 330.0, 450.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + packet->SetPointerData(1, data); + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)4); + // A add should be synthesized. + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].physical_x, 330.0); + ASSERT_EQ(result[0].physical_y, 450.0); + ASSERT_EQ(result[0].synthesized, 1); + + ASSERT_EQ(result[1].change, PointerData::Change::kDown); + ASSERT_EQ(result[1].physical_x, 330.0); + ASSERT_EQ(result[1].physical_y, 450.0); + ASSERT_EQ(result[1].synthesized, 0); + + // A move should be synthesized. + ASSERT_EQ(result[2].change, PointerData::Change::kMove); + ASSERT_EQ(result[2].physical_delta_x, -330.0); + ASSERT_EQ(result[2].physical_delta_y, -450.0); + ASSERT_EQ(result[2].physical_x, 0.0); + ASSERT_EQ(result[2].physical_y, 0.0); + ASSERT_EQ(result[2].synthesized, 1); + + ASSERT_EQ(result[3].change, PointerData::Change::kUp); + ASSERT_EQ(result[3].physical_x, 0.0); + ASSERT_EQ(result[3].physical_y, 0.0); + ASSERT_EQ(result[3].synthesized, 0); +} + +TEST(PointerDataPacketConverterTest, CanHandleThreeFingerGesture) { + // Regression test https://github.com/flutter/flutter/issues/20517. + PointerDataPacketConverter converter; + PointerData data; + std::vector result; + // First finger down. + auto packet = std::make_unique(1); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + packet->SetPointerData(0, data); + auto converted_packet = converter.Convert(std::move(packet)); + UnpackPointerPacket(result, std::move(converted_packet)); + // Second finger down. + packet = std::make_unique(1); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 33.0, 44.0); + packet->SetPointerData(0, data); + converted_packet = converter.Convert(std::move(packet)); + UnpackPointerPacket(result, std::move(converted_packet)); + // Triggers three cancels. + packet = std::make_unique(3); + CreateSimulatedPointerData(data, PointerData::Change::kCancel, 1, 33.0, 44.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kCancel, 0, 0.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kCancel, 2, 40.0, 50.0); + packet->SetPointerData(2, data); + converted_packet = converter.Convert(std::move(packet)); + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)6); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].device, 0); + ASSERT_EQ(result[0].physical_x, 0.0); + ASSERT_EQ(result[0].physical_y, 0.0); + ASSERT_EQ(result[0].synthesized, 1); + + ASSERT_EQ(result[1].change, PointerData::Change::kDown); + ASSERT_EQ(result[1].device, 0); + ASSERT_EQ(result[1].physical_x, 0.0); + ASSERT_EQ(result[1].physical_y, 0.0); + ASSERT_EQ(result[1].synthesized, 0); + + ASSERT_EQ(result[2].change, PointerData::Change::kAdd); + ASSERT_EQ(result[2].device, 1); + ASSERT_EQ(result[2].physical_x, 33.0); + ASSERT_EQ(result[2].physical_y, 44.0); + ASSERT_EQ(result[2].synthesized, 1); + + ASSERT_EQ(result[3].change, PointerData::Change::kDown); + ASSERT_EQ(result[3].device, 1); + ASSERT_EQ(result[3].physical_x, 33.0); + ASSERT_EQ(result[3].physical_y, 44.0); + ASSERT_EQ(result[3].synthesized, 0); + + ASSERT_EQ(result[4].change, PointerData::Change::kCancel); + ASSERT_EQ(result[4].device, 1); + ASSERT_EQ(result[4].physical_x, 33.0); + ASSERT_EQ(result[4].physical_y, 44.0); + ASSERT_EQ(result[4].synthesized, 0); + + ASSERT_EQ(result[5].change, PointerData::Change::kCancel); + ASSERT_EQ(result[5].device, 0); + ASSERT_EQ(result[5].physical_x, 0.0); + ASSERT_EQ(result[5].physical_y, 0.0); + ASSERT_EQ(result[5].synthesized, 0); + // Third cancel should be dropped +} + +TEST(PointerDataPacketConverterTest, CanConvetScroll) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(5); + PointerData data; + CreateSimulatedMousePointerData(data, PointerData::Change::kAdd, + PointerData::SignalKind::kNone, 0, 0.0, 0.0, + 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedMousePointerData(data, PointerData::Change::kAdd, + PointerData::SignalKind::kNone, 1, 0.0, 0.0, + 0.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedMousePointerData(data, PointerData::Change::kDown, + PointerData::SignalKind::kNone, 1, 0.0, 0.0, + 0.0, 0.0); + packet->SetPointerData(2, data); + CreateSimulatedMousePointerData(data, PointerData::Change::kHover, + PointerData::SignalKind::kScroll, 0, 34.0, + 34.0, 30.0, 0.0); + packet->SetPointerData(3, data); + CreateSimulatedMousePointerData(data, PointerData::Change::kHover, + PointerData::SignalKind::kScroll, 1, 49.0, + 49.0, 50.0, 0.0); + packet->SetPointerData(4, data); + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)7); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].signal_kind, PointerData::SignalKind::kNone); + ASSERT_EQ(result[0].device, 0); + ASSERT_EQ(result[0].physical_x, 0.0); + ASSERT_EQ(result[0].physical_y, 0.0); + ASSERT_EQ(result[0].synthesized, 0); + + ASSERT_EQ(result[1].change, PointerData::Change::kAdd); + ASSERT_EQ(result[1].signal_kind, PointerData::SignalKind::kNone); + ASSERT_EQ(result[1].device, 1); + ASSERT_EQ(result[1].physical_x, 0.0); + ASSERT_EQ(result[1].physical_y, 0.0); + ASSERT_EQ(result[1].synthesized, 0); + + ASSERT_EQ(result[2].change, PointerData::Change::kDown); + ASSERT_EQ(result[2].signal_kind, PointerData::SignalKind::kNone); + ASSERT_EQ(result[2].device, 1); + ASSERT_EQ(result[2].physical_x, 0.0); + ASSERT_EQ(result[2].physical_y, 0.0); + ASSERT_EQ(result[2].synthesized, 0); + + // Converter will synthesize a hover to position. + ASSERT_EQ(result[3].change, PointerData::Change::kHover); + ASSERT_EQ(result[3].signal_kind, PointerData::SignalKind::kNone); + ASSERT_EQ(result[3].device, 0); + ASSERT_EQ(result[3].physical_x, 34.0); + ASSERT_EQ(result[3].physical_y, 34.0); + ASSERT_EQ(result[3].physical_delta_x, 34.0); + ASSERT_EQ(result[3].physical_delta_y, 34.0); + ASSERT_EQ(result[3].synthesized, 1); + + ASSERT_EQ(result[4].change, PointerData::Change::kHover); + ASSERT_EQ(result[4].signal_kind, PointerData::SignalKind::kScroll); + ASSERT_EQ(result[4].device, 0); + ASSERT_EQ(result[4].physical_x, 34.0); + ASSERT_EQ(result[4].physical_y, 34.0); + ASSERT_EQ(result[4].scroll_delta_x, 30.0); + ASSERT_EQ(result[4].scroll_delta_y, 0.0); + + // Converter will synthesize a move to position. + ASSERT_EQ(result[5].change, PointerData::Change::kMove); + ASSERT_EQ(result[5].signal_kind, PointerData::SignalKind::kNone); + ASSERT_EQ(result[5].device, 1); + ASSERT_EQ(result[5].physical_x, 49.0); + ASSERT_EQ(result[5].physical_y, 49.0); + ASSERT_EQ(result[5].physical_delta_x, 49.0); + ASSERT_EQ(result[5].physical_delta_y, 49.0); + ASSERT_EQ(result[5].synthesized, 1); + + ASSERT_EQ(result[6].change, PointerData::Change::kHover); + ASSERT_EQ(result[6].signal_kind, PointerData::SignalKind::kScroll); + ASSERT_EQ(result[6].device, 1); + ASSERT_EQ(result[6].physical_x, 49.0); + ASSERT_EQ(result[6].physical_y, 49.0); + ASSERT_EQ(result[6].scroll_delta_x, 50.0); + ASSERT_EQ(result[6].scroll_delta_y, 0.0); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/web_ui/lib/src/ui/pointer.dart b/lib/web_ui/lib/src/ui/pointer.dart index 9a6a3ba0d3050..1aeb13cc04a59 100644 --- a/lib/web_ui/lib/src/ui/pointer.dart +++ b/lib/web_ui/lib/src/ui/pointer.dart @@ -75,10 +75,14 @@ class PointerData { this.kind = PointerDeviceKind.touch, this.signalKind, this.device = 0, + this.pointerIdentifier = 0, this.physicalX = 0.0, this.physicalY = 0.0, + this.physicalDeltaX = 0.0, + this.physicalDeltaY = 0.0, this.buttons = 0, this.obscured = false, + this.synthesized = false, this.pressure = 0.0, this.pressureMin = 0.0, this.pressureMax = 0.0, @@ -111,6 +115,12 @@ class PointerData { /// Unique identifier for the pointing device, reused across interactions. final int device; + /// Unique identifier for the pointer. + /// + /// This field changes for each new pointer down event. Framework uses this + /// identifier to determine hit test result. + final int pointerIdentifier; + /// X coordinate of the position of the pointer, in physical pixels in the /// global coordinate space. final double physicalX; @@ -119,6 +129,12 @@ class PointerData { /// global coordinate space. final double physicalY; + /// The distance of pointer movement on X coordinate in physical pixels. + final double physicalDeltaX; + + /// The distance of pointer movement on Y coordinate in physical pixels. + final double physicalDeltaY; + /// Bit field using the *Button constants (primaryMouseButton, /// secondaryStylusButton, etc). For example, if this has the value 6 and the /// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an @@ -130,6 +146,14 @@ class PointerData { /// implemented.) final bool obscured; + /// Set if this pointer data was synthesized by pointer data packet converter. + /// pointer data packet converter will synthesize additional pointer datas if + /// the input sequence of pointer data is illegal. + /// + /// For example, a down pointer data will be synthesized if the converter receives + /// a move pointer data while the pointer is not previously down. + final bool synthesized; + /// The pressure of the touch as a number ranging from 0.0, indicating a touch /// with no discernible pressure, to 1.0, indicating a touch with "normal" /// pressure, and possibly beyond, indicating a stronger touch. For devices @@ -242,9 +266,13 @@ class PointerData { 'kind: $kind, ' 'signalKind: $signalKind, ' 'device: $device, ' + 'pointerIdentifier: $pointerIdentifier, ' 'physicalX: $physicalX, ' 'physicalY: $physicalY, ' + 'physicalDeltaX: $physicalDeltaX, ' + 'physicalDeltaY: $physicalDeltaY, ' 'buttons: $buttons, ' + 'synthesized: $synthesized, ' 'pressure: $pressure, ' 'pressureMin: $pressureMin, ' 'pressureMax: $pressureMax, ' diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 3c27d561e2ccd..c6873995b4885 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -11,7 +11,7 @@ void main() {} void nativeReportTimingsCallback(List timings) native 'NativeReportTimingsCallback'; void nativeOnBeginFrame(int microseconds) native 'NativeOnBeginFrame'; -void nativeOnPointerDataPacket() native 'NativeOnPointerDataPacket'; +void nativeOnPointerDataPacket(List sequences) native 'NativeOnPointerDataPacket'; @pragma('vm:entry-point') void reportTimingsMain() { @@ -36,7 +36,11 @@ void onBeginFrameMain() { @pragma('vm:entry-point') void onPointerDataPacketMain() { window.onPointerDataPacket = (PointerDataPacket packet) { - nativeOnPointerDataPacket(); + List sequence= []; + for (PointerData data in packet.data) { + sequence.add(PointerChange.values.indexOf(data.change)); + } + nativeOnPointerDataPacket(sequence); }; } diff --git a/shell/common/input_events_unittests.cc b/shell/common/input_events_unittests.cc index 187bde7a35f1c..3e08663fff90e 100644 --- a/shell/common/input_events_unittests.cc +++ b/shell/common/input_events_unittests.cc @@ -141,6 +141,40 @@ static void TestSimulatedInputEvents( ASSERT_EQ(events_consumed_at_frame.back(), num_events); } +void CreateSimulatedPointerData(PointerData& data, + PointerData::Change change, + double dx, + double dy) { + data.time_stamp = 0; + data.change = change; + data.kind = PointerData::DeviceKind::kTouch; + data.signal_kind = PointerData::SignalKind::kNone; + data.device = 0; + data.pointer_identifier = 0; + data.physical_x = dx; + data.physical_y = dy; + data.physical_delta_x = 0.0; + data.physical_delta_y = 0.0; + data.buttons = 0; + data.obscured = 0; + data.synthesized = 0; + data.pressure = 0.0; + data.pressure_min = 0.0; + data.pressure_max = 0.0; + data.distance = 0.0; + data.distance_max = 0.0; + data.size = 0.0; + data.radius_major = 0.0; + data.radius_minor = 0.0; + data.radius_min = 0.0; + data.radius_max = 0.0; + data.orientation = 0.0; + data.tilt = 0.0; + data.platformData = 0; + data.scroll_delta_x = 0.0; + data.scroll_delta_y = 0.0; +} + TEST_F(ShellTest, MissAtMostOneFrameForIrregularInputEvents) { // We don't use `constexpr int frame_time` here because MSVC doesn't handle // it well with lambda capture. @@ -259,5 +293,125 @@ TEST_F(ShellTest, HandlesActualIphoneXsInputEvents) { } } +TEST_F(ShellTest, CanCorrectlyPipePointerPacket) { + // Sets up shell with test fixture. + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings, true); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("onPointerDataPacketMain"); + // Sets up native handler. + fml::AutoResetWaitableEvent reportLatch; + std::vector result_sequence; + auto nativeOnPointerDataPacket = [&reportLatch, &result_sequence]( + Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + result_sequence = tonic::DartConverter>::FromArguments( + args, 0, exception); + reportLatch.Signal(); + }; + // Starts engine. + AddNativeCallback("NativeOnPointerDataPacket", + CREATE_NATIVE_ENTRY(nativeOnPointerDataPacket)); + ASSERT_TRUE(configuration.IsValid()); + RunEngine(shell.get(), std::move(configuration)); + // Starts test. + auto packet = std::make_unique(6); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kHover, 3.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 3.0, 0.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 3.0, 4.0); + packet->SetPointerData(3, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 3.0, 4.0); + packet->SetPointerData(4, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0); + packet->SetPointerData(5, data); + ShellTest::DispatchPointerData(shell.get(), std::move(packet)); + bool will_draw_new_frame; + ShellTest::VSyncFlush(shell.get(), will_draw_new_frame); + + reportLatch.Wait(); + size_t expect_length = 6; + ASSERT_EQ(result_sequence.size(), expect_length); + ASSERT_EQ(PointerData::Change(result_sequence[0]), PointerData::Change::kAdd); + ASSERT_EQ(PointerData::Change(result_sequence[1]), + PointerData::Change::kHover); + ASSERT_EQ(PointerData::Change(result_sequence[2]), + PointerData::Change::kDown); + ASSERT_EQ(PointerData::Change(result_sequence[3]), + PointerData::Change::kMove); + ASSERT_EQ(PointerData::Change(result_sequence[4]), PointerData::Change::kUp); + ASSERT_EQ(PointerData::Change(result_sequence[5]), + PointerData::Change::kRemove); + + // Cleans up shell. + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + DestroyShell(std::move(shell)); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, CanCorrectlySynthesizePointerPacket) { + // Sets up shell with test fixture. + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings, true); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("onPointerDataPacketMain"); + // Sets up native handler. + fml::AutoResetWaitableEvent reportLatch; + std::vector result_sequence; + auto nativeOnPointerDataPacket = [&reportLatch, &result_sequence]( + Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + result_sequence = tonic::DartConverter>::FromArguments( + args, 0, exception); + reportLatch.Signal(); + }; + // Starts engine. + AddNativeCallback("NativeOnPointerDataPacket", + CREATE_NATIVE_ENTRY(nativeOnPointerDataPacket)); + ASSERT_TRUE(configuration.IsValid()); + RunEngine(shell.get(), std::move(configuration)); + // Starts test. + auto packet = std::make_unique(4); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 3.0, 0.0); + packet->SetPointerData(1, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 3.0, 4.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0); + packet->SetPointerData(3, data); + ShellTest::DispatchPointerData(shell.get(), std::move(packet)); + bool will_draw_new_frame; + ShellTest::VSyncFlush(shell.get(), will_draw_new_frame); + + reportLatch.Wait(); + size_t expect_length = 6; + ASSERT_EQ(result_sequence.size(), expect_length); + ASSERT_EQ(PointerData::Change(result_sequence[0]), PointerData::Change::kAdd); + // The pointer data packet converter should synthesize a hover event. + ASSERT_EQ(PointerData::Change(result_sequence[1]), + PointerData::Change::kHover); + ASSERT_EQ(PointerData::Change(result_sequence[2]), + PointerData::Change::kDown); + // The pointer data packet converter should synthesize a move event. + ASSERT_EQ(PointerData::Change(result_sequence[3]), + PointerData::Change::kMove); + ASSERT_EQ(PointerData::Change(result_sequence[4]), PointerData::Change::kUp); + ASSERT_EQ(PointerData::Change(result_sequence[5]), + PointerData::Change::kRemove); + + // Cleans up shell. + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + DestroyShell(std::move(shell)); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 058ad55d0c801..3c4ed02998536 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -38,7 +38,8 @@ void PlatformView::DispatchPlatformMessage( void PlatformView::DispatchPointerDataPacket( std::unique_ptr packet) { - delegate_.OnPlatformViewDispatchPointerDataPacket(std::move(packet)); + delegate_.OnPlatformViewDispatchPointerDataPacket( + pointer_data_packet_converter_.Convert(std::move(packet))); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index ad4170f776a79..0ef0b193b9cfa 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -15,6 +15,7 @@ #include "flutter/lib/ui/semantics/semantics_node.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" +#include "flutter/lib/ui/window/pointer_data_packet_converter.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/shell/common/pointer_data_dispatcher.h" #include "flutter/shell/common/surface.h" @@ -544,6 +545,7 @@ class PlatformView { PlatformView::Delegate& delegate_; const TaskRunners task_runners_; + PointerDataPacketConverter pointer_data_packet_converter_; SkISize size_; fml::WeakPtrFactory weak_factory_; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 51370d082862b..ed7f1febaf51c 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -173,12 +173,19 @@ void ShellTest::PumpOneFrame(Shell* shell, } void ShellTest::DispatchFakePointerData(Shell* shell) { + auto packet = std::make_unique(1); + DispatchPointerData(shell, std::move(packet)); +} + +void ShellTest::DispatchPointerData(Shell* shell, + std::unique_ptr packet) { fml::AutoResetWaitableEvent latch; - shell->GetTaskRunners().GetPlatformTaskRunner()->PostTask([&latch, shell]() { - auto packet = std::make_unique(1); - shell->OnPlatformViewDispatchPointerDataPacket(std::move(packet)); - latch.Signal(); - }); + shell->GetTaskRunners().GetPlatformTaskRunner()->PostTask( + [&latch, shell, &packet]() { + // Goes through PlatformView to ensure packet is corrected converted. + shell->GetPlatformView()->DispatchPointerDataPacket(std::move(packet)); + latch.Signal(); + }); latch.Wait(); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index fdee9653b71ce..270ea812dbf8a 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -62,7 +62,8 @@ class ShellTest : public ThreadTest { LayerTreeBuilder = {}); static void DispatchFakePointerData(Shell* shell); - + static void DispatchPointerData(Shell* shell, + std::unique_ptr packet); // Declare |UnreportedTimingsCount|, |GetNeedsReportTimings| and // |SetNeedsReportTimings| inside |ShellTest| mainly for easier friend class // declarations as shell unit tests and Shell are in different name spaces. diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java index 51dffdaffe918..10798cb9d7957 100644 --- a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java +++ b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java @@ -66,7 +66,7 @@ public class AndroidTouchProcessor { } // Must match the unpacking code in hooks.dart. - private static final int POINTER_DATA_FIELD_COUNT = 24; + private static final int POINTER_DATA_FIELD_COUNT = 28; private static final int BYTES_PER_FIELD = 8; // This value must match the value in framework's platform_view.dart. @@ -198,8 +198,11 @@ private void addPointerForIndex( packet.putLong(pointerKind); // kind packet.putLong(signalKind); // signal_kind packet.putLong(event.getPointerId(pointerIndex)); // device + packet.putLong(0); // pointer_identifier, will be generated in pointer_data_packet_converter.cc. packet.putDouble(event.getX(pointerIndex)); // physical_x packet.putDouble(event.getY(pointerIndex)); // physical_y + packet.putDouble(0.0); // physical_delta_x, will be generated in pointer_data_packet_converter.cc. + packet.putDouble(0.0); // physical_delta_y, will be generated in pointer_data_packet_converter.cc. long buttons; if (pointerKind == PointerDeviceKind.MOUSE) { @@ -220,6 +223,8 @@ private void addPointerForIndex( packet.putLong(0); // obscured + packet.putLong(0); // synthesized + packet.putDouble(event.getPressure(pointerIndex)); // pressure double pressureMin = 0.0; double pressureMax = 1.0; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 1af3dc648f34d..8e351383b0ff6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -507,10 +507,13 @@ - (void)flushOngoingTouches { pointer_data.change = flutter::PointerData::Change::kCancel; pointer_data.kind = flutter::PointerData::DeviceKind::kTouch; pointer_data.device = device.longLongValue; + pointer_data.pointer_identifier = 0; // Anything we put here will be arbitrary since there are no touches. pointer_data.physical_x = 0; pointer_data.physical_y = 0; + pointer_data.physical_delta_x = 0.0; + pointer_data.physical_delta_y = 0.0; pointer_data.pressure = 1.0; pointer_data.pressure_max = 1.0; @@ -629,9 +632,16 @@ - (void)dispatchTouches:(NSSet*)touches pointer_data.device = reinterpret_cast(touch); + // Pointer will be generated in pointer_data_packet_converter.cc. + pointer_data.pointer_identifier = 0; + pointer_data.physical_x = windowCoordinates.x * scale; pointer_data.physical_y = windowCoordinates.y * scale; + // Delta will be generated in pointer_data_packet_converter.cc. + pointer_data.physical_delta_x = 0.0; + pointer_data.physical_delta_y = 0.0; + NSNumber* deviceKey = [NSNumber numberWithLongLong:pointer_data.device]; // Track touches that began and not yet stopped so we can flush them // if the view controller goes away. diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b277c56ee7a5e..36a4785954e39 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1121,7 +1121,12 @@ FlutterEngineResult FlutterEngineSendPointerEvent( SAFE_ACCESS(current, phase, FlutterPointerPhase::kCancel)); pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0); pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0); + // Delta will be generated in pointer_data_packet_converter.cc. + pointer_data.physical_delta_x = 0.0; + pointer_data.physical_delta_y = 0.0; pointer_data.device = SAFE_ACCESS(current, device, 0); + // Pointer identifier will be generated in pointer_data_packet_converter.cc. + pointer_data.pointer_identifier = 0; pointer_data.signal_kind = ToPointerDataSignalKind( SAFE_ACCESS(current, signal_kind, kFlutterPointerSignalKindNone)); pointer_data.scroll_delta_x = SAFE_ACCESS(current, scroll_delta_x, 0.0); From 5924eebd0cbd844a39249edeba45b4cadf9bcfab Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 19 Nov 2019 10:15:34 -0800 Subject: [PATCH 184/591] Add virtual destructor to GPUSurfaceSoftwareDelegate. (#13918) --- shell/gpu/gpu_surface_software_delegate.cc | 2 ++ shell/gpu/gpu_surface_software_delegate.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/shell/gpu/gpu_surface_software_delegate.cc b/shell/gpu/gpu_surface_software_delegate.cc index 1566fe92df103..004e8b2a42c04 100644 --- a/shell/gpu/gpu_surface_software_delegate.cc +++ b/shell/gpu/gpu_surface_software_delegate.cc @@ -6,6 +6,8 @@ namespace flutter { +GPUSurfaceSoftwareDelegate::~GPUSurfaceSoftwareDelegate() = default; + ExternalViewEmbedder* GPUSurfaceSoftwareDelegate::GetExternalViewEmbedder() { return nullptr; } diff --git a/shell/gpu/gpu_surface_software_delegate.h b/shell/gpu/gpu_surface_software_delegate.h index fa62d26ce4cd3..1c24e13c82710 100644 --- a/shell/gpu/gpu_surface_software_delegate.h +++ b/shell/gpu/gpu_surface_software_delegate.h @@ -26,6 +26,8 @@ namespace flutter { /// class GPUSurfaceSoftwareDelegate { public: + virtual ~GPUSurfaceSoftwareDelegate(); + //---------------------------------------------------------------------------- /// @brief Called when the GPU surface needs a new buffer to render a new /// frame into. From 7cd3e29c78b9cc96adb06b7387410bc6d35c9517 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 19 Nov 2019 09:47:22 -0800 Subject: [PATCH 185/591] Roll src/third_party/dart d9d5fbc109..eeca3fb1cb (5 commits) dart-lang/sdk@eeca3fb1cb New "general" status file added dart-lang/sdk@626639f11f Added nonfunction-type-alias tests, skipped for non-fasta compiler dart-lang/sdk@fae6affeea [CFE] Incremental compiler has experimental invalidation strategy (first checkpoint) dart-lang/sdk@7543d27445 [CFE] Disable colors for incremental compiler test suite dart-lang/sdk@6f5b3775ff Simplify IgnoreInfo. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index b2871ec6828d4..45a6cbd0b7dc0 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd9d5fbc109b9549a5de0a7c6d7148ee0d8cc41dc', + 'dart_revision': 'eeca3fb1cbad65db70b317f9b2987b784c2c42b5', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From c812a62b8810091f5be7326374dbeb1432e5353b Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 19 Nov 2019 10:21:31 -0800 Subject: [PATCH 186/591] allow ignoring toString, hashCode, and == in api_conform_test (#13907) --- lib/web_ui/lib/src/ui/painting.dart | 9 --------- lib/web_ui/lib/src/ui/text.dart | 24 ------------------------ web_sdk/test/api_conform_test.dart | 10 ++++++++++ 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index 09a268c97afdb..9a26fc9527177 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -1480,15 +1480,6 @@ abstract class ColorFilter { List webOnlySerializeToCssPaint() { throw UnsupportedError('ColorFilter for CSS paint not yet supported'); } - - @override - bool operator ==(dynamic other); - - @override - int get hashCode; - - @override - String toString(); } /// Styles to use for blurs in [MaskFilter] objects. diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index f4a798ef4cbc4..3e2ad9cd5f6c6 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -472,15 +472,6 @@ abstract class TextStyle { List shadows, List fontFeatures, }) = engine.EngineTextStyle; - - @override - int get hashCode; - - @override - bool operator ==(dynamic other); - - @override - String toString(); } /// An opaque object that determines the configuration used by @@ -553,15 +544,6 @@ abstract class ParagraphStyle { String ellipsis, Locale locale, }) = engine.EngineParagraphStyle; - - @override - bool operator ==(dynamic other); - - @override - int get hashCode; - - @override - String toString(); } abstract class StrutStyle { @@ -610,12 +592,6 @@ abstract class StrutStyle { FontStyle fontStyle, bool forceStrutHeight, }) = engine.EngineStrutStyle; - - @override - int get hashCode; - - @override - bool operator ==(dynamic other); } /// A direction in which text flows. diff --git a/web_sdk/test/api_conform_test.dart b/web_sdk/test/api_conform_test.dart index ad0fc1810bc37..63ef0c70c7ec5 100644 --- a/web_sdk/test/api_conform_test.dart +++ b/web_sdk/test/api_conform_test.dart @@ -6,6 +6,13 @@ import 'dart:io'; import 'package:analyzer/analyzer.dart'; +// Ignore members defined on Object. +const Set _kObjectMembers = { + '==', + 'toString', + 'hashCode', +}; + void main() { // These files just contain imports to the part files; final CompilationUnit uiUnit = parseDartFile('lib/ui/ui.dart', @@ -105,6 +112,9 @@ void main() { } for (String methodName in uiMethods.keys) { + if (_kObjectMembers.contains(methodName)) { + continue; + } final MethodDeclaration uiMethod = uiMethods[methodName]; final MethodDeclaration webMethod = webMethods[methodName]; if (webMethod == null) { From 75ea3e244542fcc7d18504f641b33d7e13cdfbd8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 19 Nov 2019 10:35:41 -0800 Subject: [PATCH 187/591] Expose the platform view mutator stack to custom compositors. (#13731) This allows custom compositors to affect scene builder modifications made to the platform view. Fixes https://github.com/flutter/flutter/issues/44211 Fixes b/143612326 --- ci/licenses_golden/licenses_flutter | 2 + flow/embedded_views.h | 1 + shell/platform/embedder/BUILD.gn | 2 + shell/platform/embedder/embedder.h | 70 +++- .../embedder_external_view_embedder.cc | 115 +----- shell/platform/embedder/embedder_layers.cc | 215 ++++++++++++ shell/platform/embedder/embedder_layers.h | 52 +++ shell/platform/embedder/fixtures/main.dart | 43 +++ .../embedder/tests/embedder_assertions.h | 166 ++++++++- .../embedder/tests/embedder_unittests.cc | 332 +++++++++++++++++- 10 files changed, 866 insertions(+), 132 deletions(-) create mode 100644 shell/platform/embedder/embedder_layers.cc create mode 100644 shell/platform/embedder/embedder_layers.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c6bcdb3230073..40add69d39167 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -856,6 +856,8 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.cc FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.h FILE: ../../../flutter/shell/platform/embedder/embedder_include.c +FILE: ../../../flutter/shell/platform/embedder/embedder_layers.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_layers.h FILE: ../../../flutter/shell/platform/embedder/embedder_platform_message_response.cc FILE: ../../../flutter/shell/platform/embedder/embedder_platform_message_response.h FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.cc diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 13c3feefd1e89..919ee83a8bb86 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -19,6 +19,7 @@ namespace flutter { +// TODO(chinmaygarde): Make these enum names match the style guide. enum MutatorType { clip_rect, clip_rrect, clip_path, transform, opacity }; // Stores mutation information like clipping or transform. diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index a6429c6f837f3..abb7fe16c4d9f 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -32,6 +32,8 @@ template("embedder_source_set") { "embedder_external_view_embedder.cc", "embedder_external_view_embedder.h", "embedder_include.c", + "embedder_layers.cc", + "embedder_layers.h", "embedder_platform_message_response.cc", "embedder_platform_message_response.h", "embedder_render_target.cc", diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 2913e85ec4ed5..95724c423c231 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -464,6 +464,24 @@ typedef struct { double bottom; } FlutterRect; +typedef struct { + double x; + double y; +} FlutterPoint; + +typedef struct { + double width; + double height; +} FlutterSize; + +typedef struct { + FlutterRect rect; + FlutterSize upper_left_corner_radius; + FlutterSize upper_right_corner_radius; + FlutterSize lower_right_corner_radius; + FlutterSize lower_left_corner_radius; +} FlutterRoundedRect; + /// The identifier of the platform view. This identifier is specified by the /// application when a platform view is added to the scene via the /// `SceneBuilder.addPlatformView` call. @@ -667,6 +685,32 @@ typedef struct { VoidCallback destruction_callback; } FlutterSoftwareBackingStore; +typedef enum { + /// Indicates that the Flutter application requested that an opacity be + /// applied to the platform view. + kFlutterPlatformViewMutationTypeOpacity, + /// Indicates that the Flutter application requested that the platform view be + /// clipped using a rectangle. + kFlutterPlatformViewMutationTypeClipRect, + /// Indicates that the Flutter application requested that the platform view be + /// clipped using a rounded rectangle. + kFlutterPlatformViewMutationTypeClipRoundedRect, + /// Indicates that the Flutter application requested that the platform view be + /// transformed before composition. + kFlutterPlatformViewMutationTypeTransformation, +} FlutterPlatformViewMutationType; + +typedef struct { + /// The type of the mutation described by the subsequent union. + FlutterPlatformViewMutationType type; + union { + double opacity; + FlutterRect clip_rect; + FlutterRoundedRect clip_rounded_rect; + FlutterTransformation transformation; + }; +} FlutterPlatformViewMutation; + typedef struct { /// The size of this struct. Must be sizeof(FlutterPlatformView). size_t struct_size; @@ -674,6 +718,22 @@ typedef struct { /// application when a platform view is added to the scene via the /// `SceneBuilder.addPlatformView` call. FlutterPlatformViewIdentifier identifier; + /// The number of mutations to be applied to the platform view by the embedder + /// before on-screen composition. + size_t mutations_count; + /// The mutations to be applied by this platform view before it is composited + /// on-screen. The Flutter application may transform the platform view but + /// these transformations cannot be affected by the Flutter compositor because + /// it does not render platform views. Since the embedder is responsible for + /// composition of these views, it is also the embedder's responsibility to + /// affect the appropriate transformation. + /// + /// The mutations must be applied in order. The mutations done in the + /// collection don't take into account the device pixel ratio or the root + /// surface transformation. If these exist, the first mutation in the list + /// will be a transformation mutation to make sure subsequent mutations are in + /// the correct coordinate space. + const FlutterPlatformViewMutation** mutations; } FlutterPlatformView; typedef enum { @@ -704,16 +764,6 @@ typedef struct { }; } FlutterBackingStore; -typedef struct { - double x; - double y; -} FlutterPoint; - -typedef struct { - double width; - double height; -} FlutterSize; - typedef struct { /// The size of this struct. Must be sizeof(FlutterBackingStoreConfig). size_t struct_size; diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index dcb6f977bcb72..ac20b010a191d 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -6,6 +6,7 @@ #include +#include "flutter/shell/platform/embedder/embedder_layers.h" #include "flutter/shell/platform/embedder/embedder_render_target.h" namespace flutter { @@ -146,71 +147,6 @@ SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) { return found->second->GetSpyingCanvas(); } -static FlutterLayer MakeBackingStoreLayer( - const SkISize& frame_size, - const FlutterBackingStore* store, - const SkMatrix& surface_transformation) { - FlutterLayer layer = {}; - - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypeBackingStore; - layer.backing_store = store; - - const auto layer_bounds = - SkRect::MakeWH(frame_size.width(), frame_size.height()); - - const auto transformed_layer_bounds = - surface_transformation.mapRect(layer_bounds); - - layer.offset.x = transformed_layer_bounds.x(); - layer.offset.y = transformed_layer_bounds.y(); - layer.size.width = transformed_layer_bounds.width(); - layer.size.height = transformed_layer_bounds.height(); - - return layer; -} - -static FlutterPlatformView MakePlatformView( - FlutterPlatformViewIdentifier identifier) { - FlutterPlatformView view = {}; - - view.struct_size = sizeof(view); - - view.identifier = identifier; - - return view; -} - -static FlutterLayer MakePlatformViewLayer( - const EmbeddedViewParams& params, - const FlutterPlatformView& platform_view, - const SkMatrix& surface_transformation, - double device_pixel_ratio) { - FlutterLayer layer = {}; - - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypePlatformView; - layer.platform_view = &platform_view; - - const auto layer_bounds = SkRect::MakeXYWH(params.offsetPixels.x(), // - params.offsetPixels.y(), // - params.sizePoints.width(), // - params.sizePoints.height() // - ); - - const auto transformed_layer_bounds = - SkMatrix::Concat(surface_transformation, - SkMatrix::MakeScale(device_pixel_ratio)) - .mapRect(layer_bounds); - - layer.offset.x = transformed_layer_bounds.x(); - layer.offset.y = transformed_layer_bounds.y(); - layer.size.width = transformed_layer_bounds.width(); - layer.size.height = transformed_layer_bounds.height(); - - return layer; -} - bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget( sk_sp picture, const EmbedderRenderTarget* render_target) const { @@ -240,11 +176,10 @@ bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget( // |ExternalViewEmbedder| bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { - std::map - presented_platform_views; - // Layers may contain pointers to platform views in the collection above. - std::vector presented_layers; Registry render_targets_used; + EmbedderLayers presented_layers(pending_frame_size_, + pending_device_pixel_ratio_, + pending_surface_transformation_); if (!root_render_target_) { FML_LOG(ERROR) @@ -264,11 +199,8 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { { // The root surface is expressed as a layer. - presented_layers.push_back(MakeBackingStoreLayer( - pending_frame_size_, // frame size - root_render_target_->GetBackingStore(), // backing store - pending_surface_transformation_ // surface transformation - )); + presented_layers.PushBackingStoreLayer( + root_render_target_->GetBackingStore()); } const auto surface_size = TransformedSurfaceSize( @@ -289,21 +221,12 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { return false; } - // Indicate a layer for the platform view. Add to `presented_platform_views` - // in order to keep at allocated just for the scope of the current method. - // The layers presented to the embedder will contain a back pointer to this - // struct. It is safe to deallocate when the embedder callback is done. - presented_platform_views[view_id] = MakePlatformView(view_id); - presented_layers.push_back(MakePlatformViewLayer( - params, // embedded view params - presented_platform_views.at(view_id), // platform view - pending_surface_transformation_, // surface transformation - pending_device_pixel_ratio_ // device pixel ratio - )); + // Tell the embedder that a platform view layer is present at this point. + presented_layers.PushPlatformViewLayer(view_id, params); if (!pending_canvas_spies_.at(view_id)->DidDrawIntoCanvas()) { - // Nothing was drawn into the overlay canvas, we don't need to composite - // it. + // Nothing was drawn into the overlay canvas, we don't need to tell the + // embedder to composite it. continue; } @@ -340,22 +263,14 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { // Indicate a layer for the backing store containing contents rendered by // Flutter. - presented_layers.push_back(MakeBackingStoreLayer( - pending_frame_size_, // frame size - render_target->GetBackingStore(), // backing store - pending_surface_transformation_ // surface transformation - )); + presented_layers.PushBackingStoreLayer(render_target->GetBackingStore()); } - { - std::vector presented_layers_pointers; - presented_layers_pointers.reserve(presented_layers.size()); - for (const auto& layer : presented_layers) { - presented_layers_pointers.push_back(&layer); - } - present_callback_(std::move(presented_layers_pointers)); - } + // Flush the layer description down to the embedder for presentation. + presented_layers.InvokePresentCallback(present_callback_); + // Keep the previously used render target around in case they are required + // next frame. registry_ = std::move(render_targets_used); return true; diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc new file mode 100644 index 0000000000000..6b26edbcb1019 --- /dev/null +++ b/shell/platform/embedder/embedder_layers.cc @@ -0,0 +1,215 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/embedder_layers.h" + +#include + +namespace flutter { + +EmbedderLayers::EmbedderLayers(SkISize frame_size, + double device_pixel_ratio, + SkMatrix root_surface_transformation) + : frame_size_(frame_size), + device_pixel_ratio_(device_pixel_ratio), + root_surface_transformation_(root_surface_transformation) {} + +EmbedderLayers::~EmbedderLayers() = default; + +void EmbedderLayers::PushBackingStoreLayer(const FlutterBackingStore* store) { + FlutterLayer layer = {}; + + layer.struct_size = sizeof(FlutterLayer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = store; + + const auto layer_bounds = + SkRect::MakeWH(frame_size_.width(), frame_size_.height()); + + const auto transformed_layer_bounds = + root_surface_transformation_.mapRect(layer_bounds); + + layer.offset.x = transformed_layer_bounds.x(); + layer.offset.y = transformed_layer_bounds.y(); + layer.size.width = transformed_layer_bounds.width(); + layer.size.height = transformed_layer_bounds.height(); + + presented_layers_.push_back(layer); +} + +static std::unique_ptr ConvertMutation( + double opacity) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = opacity; + return std::make_unique(mutation); +} + +static std::unique_ptr ConvertMutation( + const SkRect& rect) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect.left = rect.left(); + mutation.clip_rect.top = rect.top(); + mutation.clip_rect.right = rect.right(); + mutation.clip_rect.bottom = rect.bottom(); + return std::make_unique(mutation); +} + +static FlutterSize VectorToSize(const SkVector& vector) { + FlutterSize size = {}; + size.width = vector.x(); + size.height = vector.y(); + return size; +} + +static std::unique_ptr ConvertMutation( + const SkRRect& rrect) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeClipRoundedRect; + const auto& rect = rrect.rect(); + mutation.clip_rounded_rect.rect.left = rect.left(); + mutation.clip_rounded_rect.rect.top = rect.top(); + mutation.clip_rounded_rect.rect.right = rect.right(); + mutation.clip_rounded_rect.rect.bottom = rect.bottom(); + mutation.clip_rounded_rect.upper_left_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kUpperLeft_Corner)); + mutation.clip_rounded_rect.upper_right_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kUpperRight_Corner)); + mutation.clip_rounded_rect.lower_right_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kLowerRight_Corner)); + mutation.clip_rounded_rect.lower_left_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kLowerLeft_Corner)); + return std::make_unique(mutation); +} + +static std::unique_ptr ConvertMutation( + const SkMatrix& matrix) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeTransformation; + mutation.transformation.scaleX = matrix[SkMatrix::kMScaleX]; + mutation.transformation.skewX = matrix[SkMatrix::kMSkewX]; + mutation.transformation.transX = matrix[SkMatrix::kMTransX]; + mutation.transformation.skewY = matrix[SkMatrix::kMSkewY]; + mutation.transformation.scaleY = matrix[SkMatrix::kMScaleY]; + mutation.transformation.transY = matrix[SkMatrix::kMTransY]; + mutation.transformation.pers0 = matrix[SkMatrix::kMPersp0]; + mutation.transformation.pers1 = matrix[SkMatrix::kMPersp1]; + mutation.transformation.pers2 = matrix[SkMatrix::kMPersp2]; + return std::make_unique(mutation); +} + +void EmbedderLayers::PushPlatformViewLayer( + FlutterPlatformViewIdentifier identifier, + const EmbeddedViewParams& params) { + { + FlutterPlatformView view = {}; + view.struct_size = sizeof(FlutterPlatformView); + view.identifier = identifier; + + const auto& mutators = params.mutatorsStack; + + std::vector mutations_array; + + if (std::distance(mutators.Bottom(), mutators.Top()) > 0) { + // If there are going to be any mutations, they must first take into + // account the transformation for the device pixel ratio and root surface + // transformation. + auto base_xformation = + SkMatrix::Concat(root_surface_transformation_, + SkMatrix::MakeScale(device_pixel_ratio_)); + if (!base_xformation.isIdentity()) { + mutations_array.push_back( + mutations_referenced_.emplace_back(ConvertMutation(base_xformation)) + .get()); + } + } + + for (auto i = mutators.Bottom(); i != mutators.Top(); ++i) { + const auto& mutator = *i; + switch (mutator->GetType()) { + case MutatorType::clip_rect: { + mutations_array.push_back( + mutations_referenced_ + .emplace_back(ConvertMutation(mutator->GetRect())) + .get()); + } break; + case MutatorType::clip_rrect: { + mutations_array.push_back( + mutations_referenced_ + .emplace_back(ConvertMutation(mutator->GetRRect())) + .get()); + } break; + case MutatorType::clip_path: { + // Unsupported mutation. + } break; + case MutatorType::transform: { + const auto& matrix = mutator->GetMatrix(); + if (!matrix.isIdentity()) { + mutations_array.push_back( + mutations_referenced_.emplace_back(ConvertMutation(matrix)) + .get()); + } + } break; + case MutatorType::opacity: { + const double opacity = + std::clamp(mutator->GetAlphaFloat(), 0.0f, 1.0f); + if (opacity < 1.0) { + mutations_array.push_back( + mutations_referenced_.emplace_back(ConvertMutation(opacity)) + .get()); + } + } break; + } + } + + if (mutations_array.size() > 0) { + auto mutations = + std::make_unique>( + mutations_array); + mutations_arrays_referenced_.emplace_back(std::move(mutations)); + + view.mutations_count = mutations_array.size(); + view.mutations = mutations_arrays_referenced_.back().get()->data(); + } + + platform_views_referenced_.emplace_back( + std::make_unique(view)); + } + + FlutterLayer layer = {}; + + layer.struct_size = sizeof(FlutterLayer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = platform_views_referenced_.back().get(); + + const auto layer_bounds = SkRect::MakeXYWH(params.offsetPixels.x(), // + params.offsetPixels.y(), // + params.sizePoints.width(), // + params.sizePoints.height() // + ); + + const auto transformed_layer_bounds = + SkMatrix::Concat(root_surface_transformation_, + SkMatrix::MakeScale(device_pixel_ratio_)) + .mapRect(layer_bounds); + + layer.offset.x = transformed_layer_bounds.x(); + layer.offset.y = transformed_layer_bounds.y(); + layer.size.width = transformed_layer_bounds.width(); + layer.size.height = transformed_layer_bounds.height(); + + presented_layers_.push_back(layer); +} // namespace flutter + +void EmbedderLayers::InvokePresentCallback(PresentCallback callback) const { + std::vector presented_layers_pointers; + presented_layers_pointers.reserve(presented_layers_.size()); + for (const auto& layer : presented_layers_) { + presented_layers_pointers.push_back(&layer); + } + callback(std::move(presented_layers_pointers)); +} + +} // namespace flutter diff --git a/shell/platform/embedder/embedder_layers.h b/shell/platform/embedder/embedder_layers.h new file mode 100644 index 0000000000000..8f179f3c7e32e --- /dev/null +++ b/shell/platform/embedder/embedder_layers.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_FLUTTER_LAYERS_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_FLUTTER_LAYERS_H_ + +#include +#include + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flutter { + +class EmbedderLayers { + public: + EmbedderLayers(SkISize frame_size, + double device_pixel_ratio, + SkMatrix root_surface_transformation); + + ~EmbedderLayers(); + + void PushBackingStoreLayer(const FlutterBackingStore* store); + + void PushPlatformViewLayer(FlutterPlatformViewIdentifier identifier, + const EmbeddedViewParams& params); + + using PresentCallback = + std::function& layers)>; + void InvokePresentCallback(PresentCallback callback) const; + + private: + const SkISize frame_size_; + const double device_pixel_ratio_; + const SkMatrix root_surface_transformation_; + std::vector> platform_views_referenced_; + std::vector> + mutations_referenced_; + std::vector>> + mutations_arrays_referenced_; + std::vector presented_layers_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderLayers); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_FLUTTER_LAYERS_H_ diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index aa388c12829ac..5e8b0e2c6dc3f 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -557,3 +557,46 @@ void push_frames_over_and_over() { }; window.scheduleFrame(); } + + +@pragma('vm:entry-point') +void platform_view_mutators() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.pushOffset(0.0, 0.0); // base + builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(800.0, 600.0))); + + builder.pushOpacity(128); + builder.pushClipRect(Rect.fromLTWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0)); + builder.pushClipRRect(RRect.fromLTRBR(10.0, 10.0, 800.0 - 10.0, 600.0 - 10.0, Radius.circular(14.0))); + builder.addPlatformView(42, width: 800.0, height: 600.0); + builder.pop(); // clip rrect + builder.pop(); // clip rect + builder.pop(); // opacity + + builder.pop(); // base + window.render(builder.build()); + }; + window.scheduleFrame(); +} + +@pragma('vm:entry-point') +void platform_view_mutators_with_pixel_ratio() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.pushOffset(0.0, 0.0); // base + builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); + + builder.pushOpacity(128); + builder.pushClipRect(Rect.fromLTWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + builder.pushClipRRect(RRect.fromLTRBR(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0, Radius.circular(7.0))); + builder.addPlatformView(42, width: 400.0, height: 300.0); + builder.pop(); // clip rrect + builder.pop(); // clip rect + builder.pop(); // opacity + + builder.pop(); // base + window.render(builder.build()); + }; + window.scheduleFrame(); +} diff --git a/shell/platform/embedder/tests/embedder_assertions.h b/shell/platform/embedder/tests/embedder_assertions.h index d0c69015b92fd..a5da239e584bd 100644 --- a/shell/platform/embedder/tests/embedder_assertions.h +++ b/shell/platform/embedder/tests/embedder_assertions.h @@ -23,11 +23,34 @@ inline bool operator==(const FlutterPoint& a, const FlutterPoint& b) { flutter::testing::NumberNear(a.y, b.y); } +inline bool operator==(const FlutterRect& a, const FlutterRect& b) { + return flutter::testing::NumberNear(a.left, b.left) && + flutter::testing::NumberNear(a.top, b.top) && + flutter::testing::NumberNear(a.right, b.right) && + flutter::testing::NumberNear(a.bottom, b.bottom); +} + inline bool operator==(const FlutterSize& a, const FlutterSize& b) { return flutter::testing::NumberNear(a.width, b.width) && flutter::testing::NumberNear(a.height, b.height); } +inline bool operator==(const FlutterRoundedRect& a, + const FlutterRoundedRect& b) { + return a.rect == b.rect && + a.upper_left_corner_radius == b.upper_left_corner_radius && + a.upper_right_corner_radius == b.upper_right_corner_radius && + a.lower_right_corner_radius == b.lower_right_corner_radius && + a.lower_left_corner_radius == b.lower_left_corner_radius; +} + +inline bool operator==(const FlutterTransformation& a, + const FlutterTransformation& b) { + return a.scaleX == b.scaleX && a.skewX == b.skewX && a.transX == b.transX && + a.skewY == b.skewY && a.scaleY == b.scaleY && a.transY == b.transY && + a.pers0 == b.pers0 && a.pers1 == b.pers1 && a.pers2 == b.pers2; +} + inline bool operator==(const FlutterOpenGLTexture& a, const FlutterOpenGLTexture& b) { return a.target == b.target && a.name == b.name && a.format == b.format && @@ -82,9 +105,40 @@ inline bool operator==(const FlutterBackingStore& a, return false; } +inline bool operator==(const FlutterPlatformViewMutation& a, + const FlutterPlatformViewMutation& b) { + if (a.type != b.type) { + return false; + } + + switch (a.type) { + case kFlutterPlatformViewMutationTypeOpacity: + return flutter::testing::NumberNear(a.opacity, b.opacity); + case kFlutterPlatformViewMutationTypeClipRect: + return a.clip_rect == b.clip_rect; + case kFlutterPlatformViewMutationTypeClipRoundedRect: + return a.clip_rounded_rect == b.clip_rounded_rect; + case kFlutterPlatformViewMutationTypeTransformation: + return a.transformation == b.transformation; + } + + return false; +} + inline bool operator==(const FlutterPlatformView& a, const FlutterPlatformView& b) { - return a.struct_size == b.struct_size && a.identifier == b.identifier; + if (!(a.struct_size == b.struct_size && a.identifier == b.identifier && + a.mutations_count == b.mutations_count)) { + return false; + } + + for (size_t i = 0; i < a.mutations_count; ++i) { + if (!(*a.mutations[i] == *b.mutations[i])) { + return false; + } + } + + return true; } inline bool operator==(const FlutterLayer& a, const FlutterLayer& b) { @@ -111,10 +165,39 @@ inline std::ostream& operator<<(std::ostream& out, const FlutterPoint& point) { return out << "(" << point.x << ", " << point.y << ")"; } +inline std::ostream& operator<<(std::ostream& out, const FlutterRect& r) { + return out << "LTRB (" << r.left << ", " << r.top << ", " << r.right << ", " + << r.bottom << ")"; +} + inline std::ostream& operator<<(std::ostream& out, const FlutterSize& size) { return out << "(" << size.width << ", " << size.height << ")"; } +inline std::ostream& operator<<(std::ostream& out, + const FlutterRoundedRect& r) { + out << "Rect: " << r.rect << ", "; + out << "Upper Left Corner Radius: " << r.upper_left_corner_radius << ", "; + out << "Upper Right Corner Radius: " << r.upper_right_corner_radius << ", "; + out << "Lower Right Corner Radius: " << r.lower_right_corner_radius << ", "; + out << "Lower Left Corner Radius: " << r.lower_left_corner_radius; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const FlutterTransformation& t) { + out << "Scale X: " << t.scaleX << ", "; + out << "Skew X: " << t.skewX << ", "; + out << "Trans X: " << t.transX << ", "; + out << "Skew Y: " << t.skewY << ", "; + out << "Scale Y: " << t.scaleY << ", "; + out << "Trans Y: " << t.transY << ", "; + out << "Pers 0: " << t.pers0 << ", "; + out << "Pers 1: " << t.pers1 << ", "; + out << "Pers 2: " << t.pers2; + return out; +} + inline std::string FlutterLayerContentTypeToString( FlutterLayerContentType type) { switch (type) { @@ -153,11 +236,56 @@ inline std::ostream& operator<<(std::ostream& out, << " Destruction Callback: " << item.destruction_callback; } +inline std::string FlutterPlatformViewMutationTypeToString( + FlutterPlatformViewMutationType type) { + switch (type) { + case kFlutterPlatformViewMutationTypeOpacity: + return "kFlutterPlatformViewMutationTypeOpacity"; + case kFlutterPlatformViewMutationTypeClipRect: + return "kFlutterPlatformViewMutationTypeClipRect"; + case kFlutterPlatformViewMutationTypeClipRoundedRect: + return "kFlutterPlatformViewMutationTypeClipRoundedRect"; + case kFlutterPlatformViewMutationTypeTransformation: + return "kFlutterPlatformViewMutationTypeTransformation"; + } + return "Unknown"; +} + +inline std::ostream& operator<<(std::ostream& out, + const FlutterPlatformViewMutation& m) { + out << "(FlutterPlatformViewMutation) Type: " + << FlutterPlatformViewMutationTypeToString(m.type) << " "; + switch (m.type) { + case kFlutterPlatformViewMutationTypeOpacity: + out << "Opacity: " << m.opacity; + case kFlutterPlatformViewMutationTypeClipRect: + out << "Clip Rect: " << m.clip_rect; + case kFlutterPlatformViewMutationTypeClipRoundedRect: + out << "Clip Rounded Rect: " << m.clip_rounded_rect; + case kFlutterPlatformViewMutationTypeTransformation: + out << "Transformation: " << m.transformation; + } + return out; +} + inline std::ostream& operator<<(std::ostream& out, const FlutterPlatformView& platform_view) { - return out << "(FlutterPlatformView) Struct Size: " - << platform_view.struct_size - << " Identifier: " << platform_view.identifier; + out << "[" + << "(FlutterPlatformView) Struct Size: " << platform_view.struct_size + << " Identifier: " << platform_view.identifier + << " Mutations Count: " << platform_view.mutations_count; + + if (platform_view.mutations_count > 0) { + out << std::endl; + for (size_t i = 0; i < platform_view.mutations_count; i++) { + out << "Mutation " << i << ": " << *platform_view.mutations[i] + << std::endl; + } + } + + out << "]"; + + return out; } inline std::string FlutterOpenGLTargetTypeToString( @@ -248,6 +376,13 @@ inline FlutterSize FlutterSizeMake(double width, double height) { return size; } +inline FlutterSize FlutterSizeMake(const SkVector& vector) { + FlutterSize size = {}; + size.width = vector.x(); + size.height = vector.y(); + return size; +} + inline FlutterTransformation FlutterTransformationMake(const SkMatrix& matrix) { FlutterTransformation transformation = {}; transformation.scaleX = matrix[SkMatrix::kMScaleX]; @@ -266,4 +401,27 @@ inline flutter::EmbedderEngine* ToEmbedderEngine(const FlutterEngine& engine) { return reinterpret_cast(engine); } +inline FlutterRect FlutterRectMake(const SkRect& rect) { + FlutterRect r = {}; + r.left = rect.left(); + r.top = rect.top(); + r.right = rect.right(); + r.bottom = rect.bottom(); + return r; +} + +inline FlutterRoundedRect FlutterRoundedRectMake(const SkRRect& rect) { + FlutterRoundedRect r = {}; + r.rect = FlutterRectMake(rect.rect()); + r.upper_left_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kUpperLeft_Corner)); + r.upper_right_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kUpperRight_Corner)); + r.lower_right_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kLowerRight_Corner)); + r.lower_left_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kLowerLeft_Corner)); + return r; +} + #endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_ASSERTIONS_H_ diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 4fe669e663d6b..fddca5e997bce 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -608,7 +608,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -701,7 +701,7 @@ TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -874,7 +874,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -968,7 +968,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -1189,7 +1189,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1222,7 +1222,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) { // Layer 3 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[3]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 2; @@ -1365,7 +1365,7 @@ TEST_F(EmbedderTest, // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1398,7 +1398,7 @@ TEST_F(EmbedderTest, // Layer 3 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[3]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 2; @@ -1549,7 +1549,7 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -1740,7 +1740,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1863,7 +1863,7 @@ TEST_F(EmbedderTest, // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1896,7 +1896,7 @@ TEST_F(EmbedderTest, // Layer 3 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[3]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 2; @@ -2258,7 +2258,7 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -2377,7 +2377,7 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -2507,7 +2507,7 @@ TEST_F(EmbedderTest, VerifyB141980393) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1337; @@ -2794,7 +2794,7 @@ TEST_F(EmbedderTest, // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -2893,7 +2893,7 @@ TEST_F( // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -3041,7 +3041,7 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -3203,5 +3203,301 @@ TEST_F(EmbedderTest, ASSERT_EQ(frames_expected, frames_seen); } +TEST_F(EmbedderTest, PlatformViewMutatorsAreValid) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("platform_view_mutators"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + fml::CountDownLatch latch(1); + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 2u); + + // Layer 0 (Root) + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 2 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + platform_view.mutations_count = 3; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[1], layer); + + // There are no ordering guarantees. + for (size_t i = 0; i < platform_view.mutations_count; i++) { + FlutterPlatformViewMutation mutation = *platform_view.mutations[i]; + switch (mutation.type) { + case kFlutterPlatformViewMutationTypeClipRoundedRect: + mutation.clip_rounded_rect = + FlutterRoundedRectMake(SkRRect::MakeRectXY( + SkRect::MakeLTRB(10.0, 10.0, 800.0 - 10.0, + 600.0 - 10.0), + 14.0, 14.0)); + break; + case kFlutterPlatformViewMutationTypeClipRect: + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect = FlutterRectMake( + SkRect::MakeXYWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0)); + break; + case kFlutterPlatformViewMutationTypeOpacity: + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = 128.0 / 255.0; + break; + case kFlutterPlatformViewMutationTypeTransformation: + FML_CHECK(false) + << "There should be no transformation in the test."; + break; + } + + ASSERT_EQ(*platform_view.mutations[i], mutation); + } + } + latch.CountDown(); + }); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + +TEST_F(EmbedderTest, PlatformViewMutatorsAreValidWithPixelRatio) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("platform_view_mutators_with_pixel_ratio"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + fml::CountDownLatch latch(1); + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 2u); + + // Layer 0 (Root) + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 2 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + platform_view.mutations_count = 4; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[1], layer); + + // There are no ordering guarantees. + for (size_t i = 0; i < platform_view.mutations_count; i++) { + FlutterPlatformViewMutation mutation = *platform_view.mutations[i]; + switch (mutation.type) { + case kFlutterPlatformViewMutationTypeClipRoundedRect: + mutation.clip_rounded_rect = + FlutterRoundedRectMake(SkRRect::MakeRectXY( + SkRect::MakeLTRB(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0), + 7.0, 7.0)); + break; + case kFlutterPlatformViewMutationTypeClipRect: + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect = FlutterRectMake( + SkRect::MakeXYWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + break; + case kFlutterPlatformViewMutationTypeOpacity: + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = 128.0 / 255.0; + break; + case kFlutterPlatformViewMutationTypeTransformation: + mutation.type = kFlutterPlatformViewMutationTypeTransformation; + mutation.transformation = + FlutterTransformationMake(SkMatrix::MakeScale(2.0)); + break; + } + + ASSERT_EQ(*platform_view.mutations[i], mutation); + } + } + latch.CountDown(); + }); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 2.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + +TEST_F(EmbedderTest, + PlatformViewMutatorsAreValidWithPixelRatioAndRootSurfaceTransformation) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("platform_view_mutators_with_pixel_ratio"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + static const auto root_surface_transformation = + SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0); + + context.SetRootSurfaceTransformation(root_surface_transformation); + + fml::CountDownLatch latch(1); + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 2u); + + // Layer 0 (Root) + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(600.0, 800.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 2 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + platform_view.mutations_count = 4; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(600.0, 800.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[1], layer); + + // There are no ordering guarantees. + for (size_t i = 0; i < platform_view.mutations_count; i++) { + FlutterPlatformViewMutation mutation = *platform_view.mutations[i]; + switch (mutation.type) { + case kFlutterPlatformViewMutationTypeClipRoundedRect: + mutation.clip_rounded_rect = + FlutterRoundedRectMake(SkRRect::MakeRectXY( + SkRect::MakeLTRB(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0), + 7.0, 7.0)); + break; + case kFlutterPlatformViewMutationTypeClipRect: + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect = FlutterRectMake( + SkRect::MakeXYWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + break; + case kFlutterPlatformViewMutationTypeOpacity: + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = 128.0 / 255.0; + break; + case kFlutterPlatformViewMutationTypeTransformation: + mutation.type = kFlutterPlatformViewMutationTypeTransformation; + mutation.transformation = + FlutterTransformationMake(SkMatrix::Concat( + root_surface_transformation, SkMatrix::MakeScale(2.0))); + + break; + } + + ASSERT_EQ(*platform_view.mutations[i], mutation); + } + } + latch.CountDown(); + }); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 2.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + } // namespace testing } // namespace flutter From 226ea7b9c97eb192dcc9429653e5221806824f66 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 19 Nov 2019 14:29:21 -0500 Subject: [PATCH 188/591] Roll fuchsia/sdk/core/linux-amd64 from T9BAw... to 7mmHP... (#13924) Roll fuchsia/sdk/core/linux-amd64 from T9BAw... to 7mmHP... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 45a6cbd0b7dc0..90c531896eeaf 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'T9BAwXtY7x6eCpbnEKlsLi5wBJbVtwp4Gc9GdUzyagwC' + 'version': '7mmHPhROLk7ei9wAP1aDBMeONqeZHKQBDR2J5DtkY0QC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 83463d027273c..fba9b4572ff39 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 93fab6fac8d4c4794020278cb30b613d +Signature: 49f79b16acb1fa261c6f3f57173f65d0 UNUSED LICENSES: From 333a8058c642ebfbad42e50020e1addec184057e Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Tue, 19 Nov 2019 12:18:10 -0800 Subject: [PATCH 189/591] Changing test runner and platform to be browser independent (#13869) * changes to add firefox as one of the browser options to test_platform and test_runner * Creating a supported_browsers file to put all the different browser related maps and utilities * Remove accidentaly forgotten commented out lines. Clear imports * fix error in screenshot handler * Addressing PR comments. * addressing PR comments part 2: --- lib/web_ui/dev/browser.dart | 4 +- lib/web_ui/dev/chrome.dart | 18 ++++-- lib/web_ui/dev/chrome_installer.dart | 54 +++++++++++----- lib/web_ui/dev/common.dart | 33 ++++++++++ lib/web_ui/dev/firefox.dart | 2 + lib/web_ui/dev/firefox_installer.dart | 40 +++++++++++- lib/web_ui/dev/supported_browsers.dart | 49 ++++++++++++++ lib/web_ui/dev/test_platform.dart | 90 +++++++++++++++++--------- lib/web_ui/dev/test_runner.dart | 46 ++++++++----- 9 files changed, 264 insertions(+), 72 deletions(-) create mode 100644 lib/web_ui/dev/supported_browsers.dart diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index 8301289acf541..82e16872c9d1d 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -10,7 +10,9 @@ import 'package:pedantic/pedantic.dart'; import 'package:stack_trace/stack_trace.dart'; import 'package:typed_data/typed_buffers.dart'; -import 'package:test_api/src/utils.dart'; // ignore: implementation_imports +import 'package:test_api/src/utils.dart'; + +import 'common.dart'; // ignore: implementation_imports /// An interface for running browser instances. /// diff --git a/lib/web_ui/dev/chrome.dart b/lib/web_ui/dev/chrome.dart index 6a560856b0e09..0eb553b8f863a 100644 --- a/lib/web_ui/dev/chrome.dart +++ b/lib/web_ui/dev/chrome.dart @@ -32,6 +32,8 @@ class Chrome extends Browser { /// Starts a new instance of Chrome open to the given [url], which may be a /// [Uri] or a [String]. factory Chrome(Uri url, {bool debug = false}) { + version = ChromeArgParser.instance.version; + assert(version != null); var remoteDebuggerCompleter = Completer.sync(); return Chrome._(() async { @@ -50,13 +52,16 @@ class Chrome extends Browser { // --disable-gpu // --disallow-non-exact-resource-reuse // --disable-font-subpixel-positioning - final bool isChromeNoSandbox = Platform.environment['CHROME_NO_SANDBOX'] == 'true'; + final bool isChromeNoSandbox = + Platform.environment['CHROME_NO_SANDBOX'] == 'true'; var dir = createTempDir(); var args = [ '--user-data-dir=$dir', url.toString(), - if (!debug) '--headless', - if (isChromeNoSandbox) '--no-sandbox', + if (!debug) + '--headless', + if (isChromeNoSandbox) + '--no-sandbox', '--window-size=$kMaxScreenshotWidth,$kMaxScreenshotHeight', // When headless, this is the actual size of the viewport '--disable-extensions', '--disable-popup-blocking', @@ -69,10 +74,11 @@ class Chrome extends Browser { '--remote-debugging-port=$kDevtoolsPort', ]; - final Process process = await Process.start(installation.executable, args); + final Process process = + await Process.start(installation.executable, args); - remoteDebuggerCompleter.complete(getRemoteDebuggerUrl( - Uri.parse('http://localhost:${kDevtoolsPort}'))); + remoteDebuggerCompleter.complete( + getRemoteDebuggerUrl(Uri.parse('http://localhost:${kDevtoolsPort}'))); unawaited(process.exitCode .then((_) => Directory(dir).deleteSync(recursive: true))); diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index c03872b920e17..5c8dbe399bbb8 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -13,23 +13,42 @@ import 'package:yaml/yaml.dart'; import 'common.dart'; import 'environment.dart'; -void addChromeVersionOption(ArgParser argParser) { - final io.File lockFile = io.File( - path.join(environment.webUiRootDir.path, 'dev', 'browser_lock.yaml')); - final YamlMap lock = loadYaml(lockFile.readAsStringSync()); - final int pinnedChromeVersion = PlatformBinding.instance.getChromeBuild(lock); - - argParser - ..addOption( - 'chrome-version', - defaultsTo: '$pinnedChromeVersion', - help: 'The Chrome version to use while running tests. If the requested ' - 'version has not been installed, it will be downloaded and installed ' - 'automatically. A specific Chrome build version number, such as 695653 ' - 'this use that version of Chrome. Value "latest" will use the latest ' - 'available build of Chrome, installing it if necessary. Value "system" ' - 'will use the manually installed version of Chrome on this computer.', - ); +class ChromeArgParser extends BrowserArgParser { + static final ChromeArgParser _singletonInstance = ChromeArgParser._(); + + /// The [ChromeArgParser] singleton. + static ChromeArgParser get instance => _singletonInstance; + + String _version; + + ChromeArgParser._(); + + @override + void populateOptions(ArgParser argParser) { + final YamlMap browserLock = BrowserLock.instance.configuration; + final int pinnedChromeVersion = + PlatformBinding.instance.getChromeBuild(browserLock); + + argParser + ..addOption( + 'chrome-version', + defaultsTo: '$pinnedChromeVersion', + help: 'The Chrome version to use while running tests. If the requested ' + 'version has not been installed, it will be downloaded and installed ' + 'automatically. A specific Chrome build version number, such as 695653, ' + 'will use that version of Chrome. Value "latest" will use the latest ' + 'available build of Chrome, installing it if necessary. Value "system" ' + 'will use the manually installed version of Chrome on this computer.', + ); + } + + @override + void parseOptions(ArgResults argResults) { + _version = argResults['chrome-version']; + } + + @override + String get version => _version; } /// Returns the installation of Chrome, installing it if necessary. @@ -180,6 +199,7 @@ class ChromeInstaller { if (unzipResult.exitCode != 0) { throw BrowserInstallerException( 'Failed to unzip the downloaded Chrome archive ${downloadedFile.path}.\n' + 'With the version path ${versionDir.path}\n' 'The unzip process exited with code ${unzipResult.exitCode}.'); } diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index e96872da283c5..57102e8411519 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -4,10 +4,13 @@ import 'dart:io' as io; +import 'package:args/args.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import 'package:yaml/yaml.dart'; +import 'environment.dart'; + /// The port number for debugging. const int kDevtoolsPort = 12345; const int kMaxScreenshotWidth = 1024; @@ -124,6 +127,36 @@ class BrowserInstallation { final String executable; } +abstract class BrowserArgParser { + const BrowserArgParser(); + + /// Populate options specific to a browser to the [ArgParser]. + void populateOptions(ArgParser argParser); + + /// Populate browser with results of the arguments passed. + void parseOptions(ArgResults argResults); + + String get version; +} + +/// Provides access to the contents of the `browser_lock.yaml` file. +class BrowserLock { + /// Initializes the [BrowserLock] singleton. + static final BrowserLock _singletonInstance = BrowserLock._(); + + /// The [Keyboard] singleton. + static BrowserLock get instance => _singletonInstance; + + YamlMap _configuration = YamlMap(); + YamlMap get configuration => _configuration; + + BrowserLock._() { + final io.File lockFile = io.File( + path.join(environment.webUiRootDir.path, 'dev', 'browser_lock.yaml')); + this._configuration = loadYaml(lockFile.readAsStringSync()); + } +} + /// A string sink that swallows all input. class DevNull implements StringSink { @override diff --git a/lib/web_ui/dev/firefox.dart b/lib/web_ui/dev/firefox.dart index e1a43301eb128..98e4b242cfe76 100644 --- a/lib/web_ui/dev/firefox.dart +++ b/lib/web_ui/dev/firefox.dart @@ -32,6 +32,8 @@ class Firefox extends Browser { /// Starts a new instance of Firefox open to the given [url], which may be a /// [Uri] or a [String]. factory Firefox(Uri url, {bool debug = false}) { + version = FirefoxArgParser.instance.version; + assert(version != null); var remoteDebuggerCompleter = Completer.sync(); return Firefox._(() async { diff --git a/lib/web_ui/dev/firefox_installer.dart b/lib/web_ui/dev/firefox_installer.dart index 641b9ad638591..d9bd9b6989277 100644 --- a/lib/web_ui/dev/firefox_installer.dart +++ b/lib/web_ui/dev/firefox_installer.dart @@ -3,13 +3,51 @@ // found in the LICENSE file. import 'dart:io' as io; +import 'package:args/args.dart'; import 'package:http/http.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; +import 'package:yaml/yaml.dart'; import 'common.dart'; import 'environment.dart'; +class FirefoxArgParser extends BrowserArgParser { + static final FirefoxArgParser _singletonInstance = FirefoxArgParser._(); + + /// The [ChromeArgParser] singleton. + static FirefoxArgParser get instance => _singletonInstance; + + String _version; + + FirefoxArgParser._(); + + @override + void populateOptions(ArgParser argParser) { + final YamlMap browserLock = BrowserLock.instance.configuration; + String firefoxVersion = browserLock['firefox']['version']; + + argParser + ..addOption( + 'firefox-version', + defaultsTo: '$firefoxVersion', + help: 'The Firefox version to use while running tests. If the requested ' + 'version has not been installed, it will be downloaded and installed ' + 'automatically. Value "latest" will use the latest ' + 'stable build of Firefox, installing it if necessary. Value "system" ' + 'will use the manually installed version of Firefox on this computer.', + ); + } + + @override + void parseOptions(ArgResults argResults) { + _version = argResults['firefox-version']; + } + + @override + String get version => _version; +} + /// Returns the installation of Firefox, installing it if necessary. /// /// If [requestedVersion] is null, uses the version specified on the @@ -106,7 +144,7 @@ class FirefoxInstaller { /// HTTP client used to download Firefox. final Client client = Client(); - /// Root directory that contains Chrome versions. + /// Root directory that contains Firefox versions. final io.Directory firefoxInstallationDir; /// Installation directory for Firefox of the requested [version]. diff --git a/lib/web_ui/dev/supported_browsers.dart b/lib/web_ui/dev/supported_browsers.dart new file mode 100644 index 0000000000000..8cd0251ad54d5 --- /dev/null +++ b/lib/web_ui/dev/supported_browsers.dart @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test_api/src/backend/runtime.dart'; + +import 'browser.dart'; +import 'chrome.dart'; +import 'chrome_installer.dart'; +import 'common.dart'; +import 'firefox.dart'; +import 'firefox_installer.dart'; // ignore: implementation_imports + +/// Utilities for browsers, that tests are supported. +/// +/// Extending [Browser] is not enough for supporting test. +/// +/// Each new browser should be added to the [Runtime] map, to the [getBrowser] +/// method. +/// +/// One should also implement [BrowserArgParser] and add it to the [argParsers]. +class SupportedBrowsers { + final List argParsers = + List.of([ChromeArgParser.instance, FirefoxArgParser.instance]); + + final List supportedBrowserNames = ['chrome', 'firefox']; + + final Map supportedBrowsersToRuntimes = { + 'chrome': Runtime.chrome, + 'firefox': Runtime.firefox + }; + + static final SupportedBrowsers _singletonInstance = SupportedBrowsers._(); + + /// The [SupportedBrowsers] singleton. + static SupportedBrowsers get instance => _singletonInstance; + + SupportedBrowsers._(); + + Browser getBrowser(Runtime runtime, Uri url, {bool debug = false}) { + if (runtime == Runtime.chrome) { + return Chrome(url, debug: debug); + } else if (runtime == Runtime.firefox) { + return Firefox(url, debug: debug); + } else { + throw new UnsupportedError('The browser type not supported in tests'); + } + } +} diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index bf1b5f95614d0..3296941a283d9 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -38,19 +38,22 @@ import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' as wip; import 'browser.dart'; -import 'chrome.dart'; import 'common.dart'; import 'environment.dart' as env; import 'goldens.dart'; +import 'supported_browsers.dart'; class BrowserPlatform extends PlatformPlugin { /// Starts the server. /// /// [root] is the root directory that the server should serve. It defaults to /// the working directory. - static Future start({String root, bool doUpdateScreenshotGoldens: false}) async { + static Future start(String name, + {String root, bool doUpdateScreenshotGoldens: false}) async { + assert(SupportedBrowsers.instance.supportedBrowserNames.contains(name)); var server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); return BrowserPlatform._( + name, server, Configuration.current, p.fromUri(await Isolate.resolvePackageUri( @@ -66,6 +69,9 @@ class BrowserPlatform extends PlatformPlugin { /// The underlying server. final shelf.Server _server; + /// Name for the running browser. Not final on purpose can be mutated later. + String browserName; + /// A randomly-generated secret. /// /// This is used to ensure that other users on the same system can't snoop @@ -97,9 +103,11 @@ class BrowserPlatform extends PlatformPlugin { /// Whether to update screenshot golden files. final bool doUpdateScreenshotGoldens; - BrowserPlatform._(this._server, Configuration config, String faviconPath, + BrowserPlatform._( + String name, this._server, Configuration config, String faviconPath, {String root, this.doUpdateScreenshotGoldens}) - : _config = config, + : this.browserName = name, + _config = config, _root = root == null ? p.current : root, _http = config.pubServeUrl == null ? null : HttpClient() { var cascade = shelf.Cascade().add(_webSocketHandler.handler); @@ -109,7 +117,6 @@ class BrowserPlatform extends PlatformPlugin { final String staticFilePath = config.suiteDefaults.precompiledPath ?? _root; cascade = cascade - .add(_screeshotHandler) .add(packagesDirHandler()) .add(_jsHandler.handler) .add(createStaticHandler(staticFilePath, @@ -117,6 +124,10 @@ class BrowserPlatform extends PlatformPlugin { serveFilesOutsidePath: config.suiteDefaults.precompiledPath != null)) .add(_wrapperHandler); + // Screenshot tests are only enabled in chrome for now. + if (name == 'chrome') { + cascade = cascade.add(_screeshotHandler); + } } var pipeline = shelf.Pipeline() @@ -130,6 +141,10 @@ class BrowserPlatform extends PlatformPlugin { } Future _screeshotHandler(shelf.Request request) async { + if (browserName != 'chrome') { + throw Exception('Screenshots tests are only available in Chrome.'); + } + if (!request.requestedUri.path.endsWith('/screenshot')) { return shelf.Response.notFound( 'This request is not handled by the screenshot handler'); @@ -141,11 +156,14 @@ class BrowserPlatform extends PlatformPlugin { final bool write = requestData['write']; final double maxDiffRate = requestData['maxdiffrate']; final Map region = requestData['region']; - final String result = await _diffScreenshot(filename, write, maxDiffRate ?? kMaxDiffRateFailure, region); + final String result = await _diffScreenshot( + filename, write, maxDiffRate ?? kMaxDiffRateFailure, region); return shelf.Response.ok(json.encode(result)); } - Future _diffScreenshot(String filename, bool write, double maxDiffRateFailure, [ Map region ]) async { + Future _diffScreenshot( + String filename, bool write, double maxDiffRateFailure, + [Map region]) async { if (doUpdateScreenshotGoldens) { write = true; } @@ -195,7 +213,8 @@ To automatically create this file call matchGoldenFile('$filename', write: true) 'y': region['y'], 'width': region['width'], 'height': region['height'], - 'scale': 1, // This is NOT the DPI of the page, instead it's the "zoom level". + 'scale': + 1, // This is NOT the DPI of the page, instead it's the "zoom level". }, }; } @@ -208,8 +227,8 @@ To automatically create this file call matchGoldenFile('$filename', write: true) 'deviceScaleFactor': 1, 'mobile': false, }); - final wip.WipResponse response = - await wipConnection.sendCommand('Page.captureScreenshot', captureScreenshotParameters); + final wip.WipResponse response = await wipConnection.sendCommand( + 'Page.captureScreenshot', captureScreenshotParameters); // Compare screenshots final Image screenshot = decodePng(base64.decode(response.result['data'])); @@ -226,31 +245,37 @@ To automatically create this file call matchGoldenFile('$filename', write: true) } } - ImageDiff diff = ImageDiff(golden: decodeNamedImage(file.readAsBytesSync(), filename), other: screenshot); + ImageDiff diff = ImageDiff( + golden: decodeNamedImage(file.readAsBytesSync(), filename), + other: screenshot); - if (diff.rate > 0) { // Images are different, so produce some debug info + if (diff.rate > 0) { + // Images are different, so produce some debug info final String testResultsPath = isCirrus - ? p.join( - Platform.environment['CIRRUS_WORKING_DIR'], - 'test_results', - ) - : p.join( - env.environment.webUiDartToolDir.path, - 'test_results', - ); + ? p.join( + Platform.environment['CIRRUS_WORKING_DIR'], + 'test_results', + ) + : p.join( + env.environment.webUiDartToolDir.path, + 'test_results', + ); Directory(testResultsPath).createSync(recursive: true); final String basename = p.basenameWithoutExtension(file.path); - final File actualFile = File(p.join(testResultsPath, '$basename.actual.png')); + final File actualFile = + File(p.join(testResultsPath, '$basename.actual.png')); actualFile.writeAsBytesSync(encodePng(screenshot), flush: true); final File diffFile = File(p.join(testResultsPath, '$basename.diff.png')); - diffFile.writeAsBytesSync(encodePng(diff.diff), flush:true); + diffFile.writeAsBytesSync(encodePng(diff.diff), flush: true); - final File expectedFile = File(p.join(testResultsPath, '$basename.expected.png')); + final File expectedFile = + File(p.join(testResultsPath, '$basename.expected.png')); file.copySync(expectedFile.path); - final File reportFile = File(p.join(testResultsPath, '$basename.report.html')); + final File reportFile = + File(p.join(testResultsPath, '$basename.report.html')); reportFile.writeAsStringSync(''' Golden file $filename did not match the image generated by the test. @@ -275,16 +300,19 @@ Golden file $filename did not match the image generated by the test. '''); final StringBuffer message = StringBuffer(); - message.writeln('Golden file $filename did not match the image generated by the test.'); + message.writeln( + 'Golden file $filename did not match the image generated by the test.'); message.writeln(getPrintableDiffFilesInfo(diff.rate, maxDiffRateFailure)); - message.writeln('You can view the test report in your browser by opening:'); + message + .writeln('You can view the test report in your browser by opening:'); // Cirrus cannot serve HTML pages generated by build jobs, so we // archive all the files so that they can be downloaded and inspected // locally. if (isCirrus) { final String taskId = Platform.environment['CIRRUS_TASK_ID']; - final String baseArtifactsUrl = 'https://api.cirrus-ci.com/v1/artifact/task/$taskId/web_engine_test/test_results'; + final String baseArtifactsUrl = + 'https://api.cirrus-ci.com/v1/artifact/task/$taskId/web_engine_test/test_results'; final String cirrusReportUrl = '$baseArtifactsUrl/$basename.report.zip'; message.writeln(cirrusReportUrl); @@ -304,7 +332,8 @@ Golden file $filename did not match the image generated by the test. message.writeln(localReportPath); } - message.writeln('To update the golden file call matchGoldenFile(\'$filename\', write: true).'); + message.writeln( + 'To update the golden file call matchGoldenFile(\'$filename\', write: true).'); message.writeln('Golden file: ${expectedFile.path}'); message.writeln('Actual file: ${actualFile.path}'); @@ -649,8 +678,9 @@ class BrowserManager { /// Starts the browser identified by [browser] using [settings] and has it load [url]. /// /// If [debug] is true, starts the browser in debug mode. - static Browser _newBrowser(Uri url, Runtime browser, {bool debug = false}) { - return Chrome(url, debug: debug); + static Browser _newBrowser(Uri url, Runtime browser, + {bool debug = false}) { + return SupportedBrowsers.instance.getBrowser(browser, url, debug: debug); } /// Creates a new BrowserManager that communicates with [browser] over diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 842b4e4e7adfd..83025795080b7 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -14,8 +14,7 @@ import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_im import 'package:test_core/src/executable.dart' as test; // ignore: implementation_imports -import 'chrome.dart'; -import 'chrome_installer.dart'; +import 'supported_browsers.dart'; import 'test_platform.dart'; import 'environment.dart'; import 'utils.dart'; @@ -32,12 +31,20 @@ class TestCommand extends Command { ..addFlag( 'update-screenshot-goldens', defaultsTo: false, - help: 'When running screenshot tests writes them to the file system into ' + help: + 'When running screenshot tests writes them to the file system into ' '.dart_tool/goldens. Use this option to bulk-update all screenshots, ' 'for example, when a new browser version affects pixels.', + ) + ..addOption( + 'browser', + defaultsTo: 'chrome', + help: 'An option to choose a browser to run the tests. Tests only work ' + ' on Chrome for now.', ); - addChromeVersionOption(argParser); + SupportedBrowsers.instance.argParsers + .forEach((t) => t.populateOptions(argParser)); } @override @@ -48,7 +55,8 @@ class TestCommand extends Command { @override Future run() async { - Chrome.version = chromeVersion; + SupportedBrowsers.instance + ..argParsers.forEach((t) => t.parseOptions(argResults)); _copyTestFontsIntoWebUi(); await _buildHostPage(); @@ -73,8 +81,7 @@ class TestCommand extends Command { /// Paths to targets to run, e.g. a single test. List get targets => argResults.rest; - /// See [ChromeInstallerCommand.chromeVersion]. - String get chromeVersion => argResults['chrome-version']; + String get browser => argResults['browser']; /// When running screenshot tests writes them to the file system into /// ".dart_tool/goldens". @@ -161,7 +168,8 @@ class TestCommand extends Command { '$hostDartPath.js.timestamp', )); - final String timestamp = hostDartFile.statSync().modified.millisecondsSinceEpoch.toString(); + final String timestamp = + hostDartFile.statSync().modified.millisecondsSinceEpoch.toString(); if (timestampFile.existsSync()) { final String lastBuildTimestamp = timestampFile.readAsStringSync(); if (lastBuildTimestamp == timestamp) { @@ -195,7 +203,7 @@ class TestCommand extends Command { timestampFile.writeAsStringSync(timestamp); } - Future _buildTests({ List targets }) async { + Future _buildTests({List targets}) async { final int exitCode = await runProcess( environment.pubExecutable, [ @@ -206,11 +214,10 @@ class TestCommand extends Command { '-o', 'build', if (targets != null) - for (FilePath path in targets) - ...[ - '--build-filter=${path.relativeToWebUi}.js', - '--build-filter=${path.relativeToWebUi}.browser_test.dart.js', - ], + for (FilePath path in targets) ...[ + '--build-filter=${path.relativeToWebUi}.js', + '--build-filter=${path.relativeToWebUi}.browser_test.dart.js', + ], ], workingDirectory: environment.webUiRootDir.path, ); @@ -234,13 +241,17 @@ class TestCommand extends Command { ...['-r', 'compact'], '--concurrency=$concurrency', if (isDebug) '--pause-after-load', - '--platform=chrome', + '--platform=$browser', '--precompiled=${environment.webUiRootDir.path}/build', '--', ...testFiles.map((f) => f.relativeToWebUi).toList(), ]; - hack.registerPlatformPlugin([Runtime.chrome], () { + + hack.registerPlatformPlugin([ + SupportedBrowsers.instance.supportedBrowsersToRuntimes[browser] + ], () { return BrowserPlatform.start( + browser, root: io.Directory.current.path, // It doesn't make sense to update a screenshot for a test that is expected to fail. doUpdateScreenshotGoldens: !expectFailure && doUpdateScreenshotGoldens, @@ -280,7 +291,8 @@ void _copyTestFontsIntoWebUi() { for (String fontFile in _kTestFonts) { final io.File sourceTtf = io.File(path.join(fontsPath, fontFile)); - final String destinationTtfPath = path.join(environment.webUiRootDir.path, 'lib', 'assets', fontFile); + final String destinationTtfPath = + path.join(environment.webUiRootDir.path, 'lib', 'assets', fontFile); sourceTtf.copySync(destinationTtfPath); } } From 2d35b4ec1f04f19d801f94bc694a553214bf003a Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Tue, 19 Nov 2019 12:33:08 -0800 Subject: [PATCH 190/591] Roll Skia to e678b79c489d (2 commits) (#13923) Roll skia to e678b79c489d (2 commits) https://skia.googlesource.com/skia.git/+log/c5e528e15b1f..e678b79c489d e678b79 Remove use of kCTFontOpticalSizeAttribute by Ben Wagner 417d299 Fix windows DLL builds with shaper included by Brian Osman --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 90c531896eeaf..2673723225204 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c5e528e15b1f2270714c5c19b4ba3fe922f64881', + 'skia_revision': 'e678b79c489dcc98ce64643d2791c7f5b5835379', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index d97baea774948..39b94edb0329a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: e2786ca638b91b01bb4302bb337a15ac +Signature: 33653c8cbb61a7646d184419d7528d56 UNUSED LICENSES: From f240462bbf4af0e49bd8db19464ab36e7cc4c7ba Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Tue, 19 Nov 2019 12:37:28 -0800 Subject: [PATCH 191/591] Implement basic text rendering support in CanvasKit backend (#13903) * Implement basic text rendering support in CanvasKit backend * Update licenses * Address PR comments --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 1 + .../lib/src/engine/compositor/fonts.dart | 70 +-- .../lib/src/engine/compositor/path.dart | 8 +- .../engine/compositor/recording_canvas.dart | 23 +- .../lib/src/engine/compositor/text.dart | 495 ++++++++++++++++++ .../lib/src/engine/compositor/util.dart | 25 + lib/web_ui/lib/src/ui/initialization.dart | 14 +- lib/web_ui/lib/src/ui/text.dart | 91 +++- 9 files changed, 634 insertions(+), 94 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/compositor/text.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 40add69d39167..fe19287614df0 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -379,6 +379,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/rasterizer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/runtime_delegate.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/surface.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/text.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/util.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/vertices.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 7a130e0909e54..c1b40aa0dc857 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -44,6 +44,7 @@ part 'engine/compositor/rasterizer.dart'; part 'engine/compositor/recording_canvas.dart'; part 'engine/compositor/runtime_delegate.dart'; part 'engine/compositor/surface.dart'; +part 'engine/compositor/text.dart'; part 'engine/compositor/util.dart'; part 'engine/compositor/vertices.dart'; part 'engine/compositor/viewport_metrics.dart'; diff --git a/lib/web_ui/lib/src/engine/compositor/fonts.dart b/lib/web_ui/lib/src/engine/compositor/fonts.dart index 45707f61de2ed..3c74dc49778e9 100644 --- a/lib/web_ui/lib/src/engine/compositor/fonts.dart +++ b/lib/web_ui/lib/src/engine/compositor/fonts.dart @@ -5,13 +5,14 @@ part of engine; class SkiaFontCollection { - final Map, js.JsObject>> - _registeredTypefaces = , js.JsObject>>{}; - - final List> _fontLoadingFutures = >[]; + final List> _loadingFontBuffers = >[]; Future ensureFontsLoaded() async { - await Future.wait(_fontLoadingFutures); + final List fontBuffers = + (await Future.wait(_loadingFontBuffers)) + .map((ByteBuffer buffer) => buffer.asUint8List()) + .toList(); + skFontMgr = canvasKit['SkFontMgr'].callMethod('FromData', fontBuffers); } Future registerFonts(AssetManager assetManager) async { @@ -48,63 +49,12 @@ class SkiaFontCollection { for (dynamic fontAssetItem in fontAssets) { final Map fontAsset = fontAssetItem; final String asset = fontAsset['asset']; - final Map descriptors = {}; - for (String descriptor in fontAsset.keys) { - if (descriptor != 'asset') { - descriptors[descriptor] = '${fontAsset[descriptor]}'; - } - } - _fontLoadingFutures.add(_registerFont( - family, assetManager.getAssetUrl(asset), descriptors)); - } - } - } - - Future _registerFont( - String family, String url, Map descriptors) async { - final dynamic fetchResult = await html.window.fetch(url); - final ByteBuffer resultBuffer = await fetchResult.arrayBuffer(); - final js.JsObject skTypeFace = skFontMgr.callMethod( - 'MakeTypefaceFromData', [resultBuffer.asUint8List()]); - _registeredTypefaces.putIfAbsent( - family, () => , js.JsObject>{}); - _registeredTypefaces[family][descriptors] = skTypeFace; - } - - js.JsObject getFont(String family, double size) { - if (_registeredTypefaces[family] == null) { - if (assertionsEnabled) { - html.window.console.warn('Using unregistered font: $family'); + _loadingFontBuffers.add(html.window + .fetch(assetManager.getAssetUrl(asset)) + .then((dynamic fetchResult) => fetchResult.arrayBuffer())); } - return js.JsObject(canvasKit['SkFont'], [null, size]); } - - // We don't attempt to find a Typeface matching the text style. Instead, we - // try to find the "default" typeface. The default typeface either has no - // descriptors, or only has a descriptor of font-weight 400 (the default). - final Map, js.JsObject> typefaces = - _registeredTypefaces[family]; - js.JsObject skTypeface; - - for (MapEntry, js.JsObject> entry - in typefaces.entries) { - final Map descriptors = entry.key; - if (descriptors.isEmpty || - (descriptors.length == 1 && descriptors['weight'] == '400')) { - skTypeface = entry.value; - break; - } - } - - // If we couldn't find a suitable default, just use any typeface in the - // family. - if (skTypeface == null) { - skTypeface = typefaces.values.first; - } - - return js.JsObject(canvasKit['SkFont'], [skTypeface, size]); } - final js.JsObject skFontMgr = - js.JsObject(canvasKit['SkFontMgr']['RefDefault']); + js.JsObject skFontMgr; } diff --git a/lib/web_ui/lib/src/engine/compositor/path.dart b/lib/web_ui/lib/src/engine/compositor/path.dart index e501fb2cbb370..da2b6599a8aab 100644 --- a/lib/web_ui/lib/src/engine/compositor/path.dart +++ b/lib/web_ui/lib/src/engine/compositor/path.dart @@ -41,10 +41,11 @@ class SkPath implements ui.Path { @override void addArc(ui.Rect oval, double startAngle, double sweepAngle) { + const double _toDegrees = 180.0 / math.pi; _skPath.callMethod('addArc', [ makeSkRect(oval), - startAngle, - sweepAngle, + startAngle * _toDegrees, + sweepAngle * _toDegrees, ]); } @@ -205,8 +206,7 @@ class SkPath implements ui.Path { @override ui.Rect getBounds() { final js.JsObject bounds = _skPath.callMethod('getBounds'); - return ui.Rect.fromLTRB( - bounds['fLeft'], bounds['fTop'], bounds['fRight'], bounds['fBottom']); + return fromSkRect(bounds); } @override diff --git a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart index 6cb4b33ef69e3..b4ec063c483c8 100644 --- a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart @@ -179,26 +179,11 @@ class SkRecordingCanvas implements RecordingCanvas { @override void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { - // TODO(het): This doesn't support most paragraph features. We are just - // creating a font from the family and size, and drawing it with - // ShapedText. - final EngineParagraph engineParagraph = paragraph; - final ParagraphGeometricStyle style = engineParagraph.geometricStyle; - final js.JsObject skFont = skiaFontCollection.getFont( - style.effectiveFontFamily, style.fontSize ?? 12.0); - final js.JsObject skShapedTextOpts = js.JsObject.jsify({ - 'font': skFont, - 'leftToRight': true, - 'text': engineParagraph.plainText, - 'width': engineParagraph.width + 1, - }); - final js.JsObject skShapedText = - js.JsObject(canvasKit['ShapedText'], [skShapedTextOpts]); - skCanvas.callMethod('drawText', [ - skShapedText, - offset.dx + engineParagraph._alignOffset, + final SkParagraph skParagraph = paragraph; + skCanvas.callMethod('drawParagraph', [ + skParagraph.skParagraph, + offset.dx, offset.dy, - makeSkPaint(engineParagraph._paint) ]); } diff --git a/lib/web_ui/lib/src/engine/compositor/text.dart b/lib/web_ui/lib/src/engine/compositor/text.dart new file mode 100644 index 0000000000000..4286bd5ab2141 --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/text.dart @@ -0,0 +1,495 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +part of engine; + +// TODO(hterkelsen): Get rid of this once we can register font families with +// custom names. +/// CanvasKit does not yet allow us to specify family names when registering +/// fonts. CanvasKit reads the font name from the font's bytes. So, we map +/// some common family names to how they are registered in the Gallery app. +const Map _fontFamilyOverrides = { + 'Roboto': 'Google Sans', + 'GoogleSans': 'Google Sans', + 'GoogleSansDisplay': 'Google Sans Display', + 'MaterialIcons': 'Material Icons', + 'LibreFranklin': 'LibreFranklin', + 'AbrilFatface': 'Abril Fatface', + 'packages/cupertino_icons/CupertinoIcons': 'CupertinoIcons', + '.SF Pro Text': 'Google Sans', +}; + +class SkParagraphStyle implements ui.ParagraphStyle { + SkParagraphStyle({ + ui.TextAlign textAlign, + ui.TextDirection textDirection, + int maxLines, + String fontFamily, + double fontSize, + double height, + ui.FontWeight fontWeight, + ui.FontStyle fontStyle, + ui.StrutStyle strutStyle, + String ellipsis, + ui.Locale locale, + }) { + skParagraphStyle = toSkParagraphStyle( + textAlign, + textDirection, + maxLines, + fontFamily, + fontSize, + height, + fontWeight, + fontStyle, + ellipsis, + ); + assert(skParagraphStyle != null); + _textDirection = textDirection ?? ui.TextDirection.ltr; + _fontFamily = fontFamily; + } + + js.JsObject skParagraphStyle; + ui.TextDirection _textDirection; + String _fontFamily; + + static Map toSkTextStyle( + String fontFamily, + double fontSize, + ui.FontWeight fontWeight, + ui.FontStyle fontStyle, + ) { + final Map skTextStyle = {}; + if (fontWeight != null || fontStyle != null) { + skTextStyle['fontStyle'] = toSkFontStyle(fontWeight, fontStyle); + } + + if (fontSize != null) { + skTextStyle['fontSize'] = fontSize; + } + + if (fontFamily == null) { + fontFamily = 'Roboto'; + } + if (_fontFamilyOverrides.containsKey(fontFamily)) { + fontFamily = _fontFamilyOverrides[fontFamily]; + } + skTextStyle['fontFamilies'] = [fontFamily]; + + return skTextStyle; + } + + static js.JsObject toSkParagraphStyle( + ui.TextAlign textAlign, + ui.TextDirection textDirection, + int maxLines, + String fontFamily, + double fontSize, + double height, + ui.FontWeight fontWeight, + ui.FontStyle fontStyle, + String ellipsis, + ) { + final Map skParagraphStyle = {}; + + if (textAlign != null) { + switch (textAlign) { + case ui.TextAlign.left: + skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Left']; + break; + case ui.TextAlign.right: + skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Right']; + break; + case ui.TextAlign.center: + skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Center']; + break; + case ui.TextAlign.justify: + skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Justify']; + break; + case ui.TextAlign.start: + skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Start']; + break; + case ui.TextAlign.end: + skParagraphStyle['textAlign'] = canvasKit['TextAlign']['End']; + break; + } + } + + if (textDirection != null) { + switch (textDirection) { + case ui.TextDirection.ltr: + skParagraphStyle['textDirection'] = canvasKit['TextDirection']['LTR']; + break; + case ui.TextDirection.rtl: + skParagraphStyle['textDirection'] = canvasKit['TextDirection']['RTL']; + break; + } + } + + if (height != null) { + skParagraphStyle['heightMultiplier'] = height; + } + + if (maxLines != null) { + skParagraphStyle['maxLines'] = maxLines; + } + + if (ellipsis != null) { + skParagraphStyle['ellipsis'] = ellipsis; + } + + skParagraphStyle['textStyle'] = + toSkTextStyle(fontFamily, fontSize, fontWeight, fontStyle); + + return canvasKit.callMethod( + 'ParagraphStyle', [js.JsObject.jsify(skParagraphStyle)]); + } +} + +class SkTextStyle implements ui.TextStyle { + js.JsObject skTextStyle; + + SkTextStyle({ + ui.Color color, + ui.TextDecoration decoration, + ui.Color decorationColor, + ui.TextDecorationStyle decorationStyle, + double decorationThickness, + ui.FontWeight fontWeight, + ui.FontStyle fontStyle, + ui.TextBaseline textBaseline, + String fontFamily, + List fontFamilyFallback, + double fontSize, + double letterSpacing, + double wordSpacing, + double height, + ui.Locale locale, + ui.Paint background, + ui.Paint foreground, + List shadows, + List fontFeatures, + }) { + final Map style = {}; + + if (background != null) { + style['backgroundColor'] = makeSkPaint(background); + } + + if (color != null) { + style['color'] = color.value; + } + + if (decoration != null) { + int decorationValue = canvasKit['NoDecoration']; + if (decoration.contains(ui.TextDecoration.underline)) { + decorationValue |= canvasKit['UnderlineDecoration']; + } + if (decoration.contains(ui.TextDecoration.overline)) { + decorationValue |= canvasKit['OverlineDecoration']; + } + if (decoration.contains(ui.TextDecoration.lineThrough)) { + decorationValue |= canvasKit['LineThroughDecoration']; + } + style['decoration'] = decorationValue; + } + + if (decorationThickness != null) { + style['decorationThickness'] = decorationThickness; + } + + if (fontSize != null) { + style['fontSize'] = fontSize; + } + + if (fontFamily == null) { + fontFamily = 'Roboto'; + } + + if (_fontFamilyOverrides.containsKey(fontFamily)) { + fontFamily = _fontFamilyOverrides[fontFamily]; + } + List fontFamilies = [fontFamily]; + if (fontFamilyFallback != null) { + fontFamilies.addAll(fontFamilies); + } + + style['fontFamilies'] = fontFamilies; + + if (fontWeight != null || fontStyle != null) { + style['fontStyle'] = toSkFontStyle(fontWeight, fontStyle); + } + + if (foreground != null) { + style['foreground'] = makeSkPaint(foreground); + } + + // TODO(hterkelsen): Add support for + // - decorationColor + // - decorationStyle + // - textBaseline + // - letterSpacing + // - wordSpacing + // - height + // - locale + // - shadows + // - fontFeatures + skTextStyle = canvasKit + .callMethod('TextStyle', [js.JsObject.jsify(style)]); + assert(skTextStyle != null); + } +} + +Map toSkFontStyle( + ui.FontWeight fontWeight, ui.FontStyle fontStyle) { + Map style = {}; + if (fontWeight != null) { + switch (fontWeight) { + case ui.FontWeight.w100: + style['weight'] = canvasKit['FontWeight']['Thin']; + break; + case ui.FontWeight.w200: + style['weight'] = canvasKit['FontWeight']['ExtraLight']; + break; + case ui.FontWeight.w300: + style['weight'] = canvasKit['FontWeight']['Light']; + break; + case ui.FontWeight.w400: + style['weight'] = canvasKit['FontWeight']['Normal']; + break; + case ui.FontWeight.w500: + style['weight'] = canvasKit['FontWeight']['Medium']; + break; + case ui.FontWeight.w600: + style['weight'] = canvasKit['FontWeight']['SemiBold']; + break; + case ui.FontWeight.w700: + style['weight'] = canvasKit['FontWeight']['Bold']; + break; + case ui.FontWeight.w800: + style['weight'] = canvasKit['FontWeight']['ExtraBold']; + break; + case ui.FontWeight.w900: + style['weight'] = canvasKit['FontWeight']['ExtraBlack']; + break; + } + } + + if (fontStyle != null) { + switch (fontStyle) { + case ui.FontStyle.normal: + style['slant'] = canvasKit['FontSlant']['Upright']; + break; + case ui.FontStyle.italic: + style['slant'] = canvasKit['FontSlant']['Italic']; + break; + } + } +} + +class SkParagraph implements ui.Paragraph { + SkParagraph(this.skParagraph, this._textDirection, this._fontFamily); + + final js.JsObject skParagraph; + final ui.TextDirection _textDirection; + final String _fontFamily; + + @override + double get alphabeticBaseline => + skParagraph.callMethod('getAlphabeticBaseline'); + + @override + bool get didExceedMaxLines => skParagraph.callMethod('didExceedMaxLines'); + + @override + double get height => skParagraph.callMethod('getHeight'); + + @override + double get ideographicBaseline => + skParagraph.callMethod('getIdeographicBaseline'); + + @override + double get longestLine => skParagraph.callMethod('getLongestLine'); + + @override + double get maxIntrinsicWidth => + skParagraph.callMethod('getMaxIntrinsicWidth'); + + @override + double get minIntrinsicWidth => + skParagraph.callMethod('getMinIntrinsicWidth'); + + @override + double get width => skParagraph.callMethod('getMaxWidth'); + + // TODO(hterkelsen): Implement placeholders once it's in CanvasKit + @override + List getBoxesForPlaceholders() { + return const []; + } + + @override + List getBoxesForRange( + int start, + int end, { + ui.BoxHeightStyle boxHeightStyle: ui.BoxHeightStyle.tight, + ui.BoxWidthStyle boxWidthStyle: ui.BoxWidthStyle.tight, + }) { + js.JsObject heightStyle; + switch (boxHeightStyle) { + case ui.BoxHeightStyle.tight: + heightStyle = canvasKit['RectHeightStyle']['Tight']; + break; + case ui.BoxHeightStyle.max: + heightStyle = canvasKit['RectHeightStyle']['Max']; + break; + default: + // TODO(hterkelsen): Support all height styles + html.window.console.warn( + 'We do not support $boxHeightStyle. Defaulting to BoxHeightStyle.tight'); + heightStyle = canvasKit['RectHeightStyle']['Tight']; + break; + } + + js.JsObject widthStyle; + switch (boxWidthStyle) { + case ui.BoxWidthStyle.tight: + widthStyle = canvasKit['RectWidthStyle']['Tight']; + break; + case ui.BoxWidthStyle.max: + widthStyle = canvasKit['RectWidthStyle']['Max']; + break; + } + + List skRects = + skParagraph.callMethod('getRectsForRange', [ + start, + end, + heightStyle, + widthStyle, + ]); + + List result = List(skRects.length); + + for (int i = 0; i < skRects.length; i++) { + final js.JsObject rect = skRects[i]; + result[i] = ui.TextBox.fromLTRBD( + rect['fLeft'], + rect['fTop'], + rect['fRight'], + rect['fBottom'], + _textDirection, + ); + } + + return result; + } + + @override + ui.TextPosition getPositionForOffset(ui.Offset offset) { + js.JsObject positionWithAffinity = + skParagraph.callMethod('getGlyphPositionAtCoordinate', [ + offset.dx, + offset.dy, + ]); + return fromPositionWithAffinity(positionWithAffinity); + } + + @override + ui.TextRange getWordBoundary(ui.TextPosition position) { + js.JsObject skRange = + skParagraph.callMethod('getWordBoundary', [position.offset]); + return ui.TextRange(start: skRange['start'], end: skRange['end']); + } + + @override + void layout(ui.ParagraphConstraints constraints) { + assert(constraints.width != null); + // TODO(het): CanvasKit throws an exception when laid out with + // a font that wasn't registered. + try { + skParagraph.callMethod('layout', [constraints.width]); + } catch (e) { + html.window.console.warn('CanvasKit threw an exception while laying ' + 'out the paragraph. The font was "$_fontFamily". Exception:\n$e'); + rethrow; + } + } + + @override + ui.TextRange getLineBoundary(ui.TextPosition position) { + // TODO(hterkelsen): Implement this when it's added to CanvasKit + throw UnimplementedError('getLineBoundary'); + } + + @override + List computeLineMetrics() { + // TODO(hterkelsen): Implement this when it's added to CanvasKit + throw UnimplementedError('computeLineMetrics'); + } +} + +class SkParagraphBuilder implements ui.ParagraphBuilder { + js.JsObject _paragraphBuilder; + ui.TextDirection _textDirection; + String _fontFamily; + + SkParagraphBuilder(ui.ParagraphStyle style) { + SkParagraphStyle skStyle = style; + _textDirection = skStyle._textDirection; + _fontFamily = skStyle._fontFamily; + _paragraphBuilder = canvasKit['ParagraphBuilder'].callMethod( + 'Make', + [ + skStyle.skParagraphStyle, + skiaFontCollection.skFontMgr, + ], + ); + } + + // TODO(hterkelsen): Implement placeholders. + @override + void addPlaceholder( + double width, + double height, + ui.PlaceholderAlignment alignment, { + double scale, + double baselineOffset, + ui.TextBaseline baseline, + }) { + throw UnimplementedError('addPlaceholder'); + } + + @override + void addText(String text) { + _paragraphBuilder.callMethod('addText', [text]); + } + + @override + ui.Paragraph build() { + final SkParagraph paragraph = SkParagraph( + _paragraphBuilder.callMethod('build'), _textDirection, _fontFamily); + _paragraphBuilder.callMethod('delete'); + _paragraphBuilder = null; + return paragraph; + } + + @override + int get placeholderCount => throw UnimplementedError('placeholderCount'); + + // TODO(hterkelsen): Implement this once CanvasKit exposes placeholders. + @override + List get placeholderScales => const []; + + @override + void pop() { + _paragraphBuilder.callMethod('pop'); + } + + @override + void pushStyle(ui.TextStyle style) { + final SkTextStyle skStyle = style; + _paragraphBuilder + .callMethod('pushStyle', [skStyle.skTextStyle]); + } +} diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index 2ed77a6c62339..0647b404af13b 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -23,6 +23,31 @@ js.JsObject makeSkRRect(ui.RRect rrect) { }); } +ui.Rect fromSkRect(js.JsObject skRect) { + return ui.Rect.fromLTRB( + skRect['fLeft'], + skRect['fTop'], + skRect['fRight'], + skRect['fBottom'], + ); +} + +ui.TextPosition fromPositionWithAffinity(js.JsObject positionWithAffinity) { + if (positionWithAffinity['affinity'] == canvasKit['Affinity']['Upstream']) { + return ui.TextPosition( + offset: positionWithAffinity['pos'], + affinity: ui.TextAffinity.upstream, + ); + } else { + assert(positionWithAffinity['affinity'] == + canvasKit['Affinity']['Downstream']); + return ui.TextPosition( + offset: positionWithAffinity['pos'], + affinity: ui.TextAffinity.downstream, + ); + } +} + js.JsArray makeSkPoint(ui.Offset point) { final js.JsArray skPoint = js.JsArray(); skPoint.length = 2; diff --git a/lib/web_ui/lib/src/ui/initialization.dart b/lib/web_ui/lib/src/ui/initialization.dart index 041590bfebe26..e72d34e8e1794 100644 --- a/lib/web_ui/lib/src/ui/initialization.dart +++ b/lib/web_ui/lib/src/ui/initialization.dart @@ -22,9 +22,10 @@ Future webOnlyInitializePlatform({ assetManager ??= const engine.AssetManager(); await webOnlySetAssetManager(assetManager); - await _fontCollection.ensureFontsLoaded(); if (engine.experimentalUseSkia) { await engine.skiaFontCollection.ensureFontsLoaded(); + } else { + await _fontCollection.ensureFontsLoaded(); } _webOnlyIsInitialized = true; @@ -50,20 +51,21 @@ Future webOnlySetAssetManager(engine.AssetManager assetManager) async { if (engine.experimentalUseSkia) { engine.skiaFontCollection ??= engine.SkiaFontCollection(); + } else { + _fontCollection ??= engine.FontCollection(); + _fontCollection.clear(); } - _fontCollection ??= engine.FontCollection(); - _fontCollection.clear(); if (_assetManager != null) { - await _fontCollection.registerFonts(_assetManager); - if (engine.experimentalUseSkia) { await engine.skiaFontCollection.registerFonts(_assetManager); + } else { + await _fontCollection.registerFonts(_assetManager); } } - if (debugEmulateFlutterTesterEnvironment) { + if (debugEmulateFlutterTesterEnvironment && !engine.experimentalUseSkia) { _fontCollection.debugRegisterTestFonts(); } } diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 3e2ad9cd5f6c6..c3dc8edf3137f 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -471,7 +471,53 @@ abstract class TextStyle { Paint foreground, List shadows, List fontFeatures, - }) = engine.EngineTextStyle; + }) { + if (engine.experimentalUseSkia) { + return engine.SkTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + locale: locale, + background: background, + foreground: foreground, + shadows: shadows, + fontFeatures: fontFeatures, + ); + } else { + return engine.EngineTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + locale: locale, + background: background, + foreground: foreground, + shadows: shadows, + fontFeatures: fontFeatures, + ); + } + } } /// An opaque object that determines the configuration used by @@ -543,7 +589,37 @@ abstract class ParagraphStyle { StrutStyle strutStyle, String ellipsis, Locale locale, - }) = engine.EngineParagraphStyle; + }) { + if (engine.experimentalUseSkia) { + return engine.SkParagraphStyle( + textAlign: textAlign, + textDirection: textDirection, + maxLines: maxLines, + fontFamily: fontFamily, + fontSize: fontSize, + height: height, + fontWeight: fontWeight, + fontStyle: fontStyle, + strutStyle: strutStyle, + ellipsis: ellipsis, + locale: locale, + ); + } else { + return engine.EngineParagraphStyle( + textAlign: textAlign, + textDirection: textDirection, + maxLines: maxLines, + fontFamily: fontFamily, + fontSize: fontSize, + height: height, + fontWeight: fontWeight, + fontStyle: fontStyle, + strutStyle: strutStyle, + ellipsis: ellipsis, + locale: locale, + ); + } + } } abstract class StrutStyle { @@ -1216,7 +1292,7 @@ abstract class Paragraph { /// have word breaks on both sides. In such cases, this method will return /// [offset, offset+1]. Word boundaries are defined more precisely in Unicode /// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries - TextRange getWordBoundary(TextPosition position); + TextRange getWordBoundary(TextPosition position); /// Returns the [TextRange] of the line at the given [TextPosition]. /// @@ -1263,8 +1339,13 @@ abstract class Paragraph { abstract class ParagraphBuilder { /// Creates a [ParagraphBuilder] object, which is used to create a /// [Paragraph]. - factory ParagraphBuilder(ParagraphStyle style) => - engine.EngineParagraphBuilder(style); + factory ParagraphBuilder(ParagraphStyle style) { + if (engine.experimentalUseSkia) { + return engine.SkParagraphBuilder(style); + } else { + return engine.EngineParagraphBuilder(style); + } + } /// Applies the given style to the added text until [pop] is called. /// From ffcd8564a2ece0786f8c86fe863ca9b1d250d0ea Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 19 Nov 2019 13:43:06 -0800 Subject: [PATCH 192/591] Imagefilter wrapper object (#13711) Make ImageFilter objects comparable and printable. This will help in areas in the Widget and RenderObject trees which try to avoid marking objects for updates if a setter is called with the same value (previously all ImageFilter objects would compare as not equal and appear to be new values). --- lib/ui/compositing.dart | 4 +- lib/ui/painting.dart | 133 ++++++++++++++++-- .../src/engine/compositor/image_filter.dart | 24 +++- lib/web_ui/lib/src/engine/shader.dart | 17 +++ testing/dart/image_filter_test.dart | 126 +++++++++++++++++ 5 files changed, 288 insertions(+), 16 deletions(-) create mode 100644 testing/dart/image_filter_test.dart diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index 232514c0f1805..3eb13535e44cb 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -410,11 +410,11 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// See [pop] for details about the operation stack. BackdropFilterEngineLayer pushBackdropFilter(ImageFilter filter, { BackdropFilterEngineLayer oldLayer }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter')); - final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(_pushBackdropFilter(filter)); + final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(_pushBackdropFilter(filter._toNativeImageFilter())); assert(_debugPushLayer(layer)); return layer; } - EngineLayer _pushBackdropFilter(ImageFilter filter) native 'SceneBuilder_pushBackdropFilter'; + EngineLayer _pushBackdropFilter(_ImageFilter filter) native 'SceneBuilder_pushBackdropFilter'; /// Pushes a shader mask operation onto the operation stack. /// diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index b3861970b55d6..b30f3835902c5 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1387,16 +1387,24 @@ class Paint { /// /// * [MaskFilter], which is used for drawing geometry. ImageFilter get imageFilter { - if (_objects == null) + if (_objects == null || _objects[_kImageFilterIndex] == null) return null; - return _objects[_kImageFilterIndex]; + return _objects[_kImageFilterIndex].creator; } + set imageFilter(ImageFilter value) { - _objects ??= List(_kObjectCount); - _objects[_kImageFilterIndex] = value; + if (value == null) { + if (_objects != null) { + _objects[_kImageFilterIndex] = null; + } + } else { + _objects ??= List(_kObjectCount); + if (_objects[_kImageFilterIndex]?.creator != value) { + _objects[_kImageFilterIndex] = value._toNativeImageFilter(); + } + } } - /// Whether the colors of the image are inverted when drawn. /// /// Inverting the colors of an image applies a new color filter that will @@ -2659,7 +2667,7 @@ class ColorFilter { /// This is a private class, rather than being the implementation of the public /// ColorFilter, because we want ColorFilter to be const constructible and /// efficiently comparable, so that widgets can check for ColorFilter equality to -// avoid repainting. +/// avoid repainting. class _ColorFilter extends NativeFieldWrapperClass2 { _ColorFilter.mode(this.creator) : assert(creator != null), @@ -2706,13 +2714,107 @@ class _ColorFilter extends NativeFieldWrapperClass2 { /// * [BackdropFilter], a widget that applies [ImageFilter] to its rendering. /// * [SceneBuilder.pushBackdropFilter], which is the low-level API for using /// this class. -class ImageFilter extends NativeFieldWrapperClass2 { +class ImageFilter { + /// Creates an image filter that applies a Gaussian blur. + ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0 }) + : _data = _makeList(sigmaX, sigmaY), + _filterQuality = null, + _type = _kTypeBlur; + + /// Creates an image filter that applies a matrix transformation. + /// + /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) + /// when used with [BackdropFilter] would magnify the background image. + ImageFilter.matrix(Float64List matrix4, + { FilterQuality filterQuality = FilterQuality.low }) + : _data = Float64List.fromList(matrix4), + _filterQuality = filterQuality, + _type = _kTypeMatrix { + if (matrix4.length != 16) + throw ArgumentError('"matrix4" must have 16 entries.'); + } + + static Float64List _makeList(double a, double b) { + final Float64List list = Float64List(2); + if (a != null) + list[0] = a; + if (b != null) + list[1] = b; + return list; + } + + final Float64List _data; + final FilterQuality _filterQuality; + final int _type; + _ImageFilter _nativeFilter; + + // The type of SkImageFilter class to create for Skia. + static const int _kTypeBlur = 0; // MakeBlurFilter + static const int _kTypeMatrix = 1; // MakeMatrixFilterRowMajor255 + + @override + bool operator ==(dynamic other) { + if (other is! ImageFilter) { + return false; + } + final ImageFilter typedOther = other; + + if (_type != typedOther._type) { + return false; + } + if (!_listEquals(_data, typedOther._data)) { + return false; + } + + return _filterQuality == typedOther._filterQuality; + } + + _ImageFilter _toNativeImageFilter() => _nativeFilter ??= _makeNativeImageFilter(); + + _ImageFilter _makeNativeImageFilter() { + if (_data == null) { + return null; + } + switch (_type) { + case _kTypeBlur: + return _ImageFilter.blur(this); + case _kTypeMatrix: + return _ImageFilter.matrix(this); + default: + throw StateError('Unknown mode $_type for ImageFilter.'); + } + } + + @override + int get hashCode => hashValues(_filterQuality, hashList(_data), _type); + + @override + String toString() { + switch (_type) { + case _kTypeBlur: + return 'ImageFilter.blur(${_data[0]}, ${_data[1]})'; + case _kTypeMatrix: + return 'ImageFilter.matrix($_data, $_filterQuality)'; + default: + return 'Unknown ImageFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.'; + } + } +} + +/// An [ImageFilter] that is backed by a native SkImageFilter. +/// +/// This is a private class, rather than being the implementation of the public +/// ImageFilter, because we want ImageFilter to be efficiently comparable, so that +/// widgets can check for ImageFilter equality to avoid repainting. +class _ImageFilter extends NativeFieldWrapperClass2 { void _constructor() native 'ImageFilter_constructor'; /// Creates an image filter that applies a Gaussian blur. - ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0 }) { + _ImageFilter.blur(this.creator) + : assert(creator != null), + assert(creator._type == ImageFilter._kTypeBlur) { _constructor(); - _initBlur(sigmaX, sigmaY); + _initBlur(creator._data[0], creator._data[1]); } void _initBlur(double sigmaX, double sigmaY) native 'ImageFilter_initBlur'; @@ -2720,14 +2822,19 @@ class ImageFilter extends NativeFieldWrapperClass2 { /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. - ImageFilter.matrix(Float64List matrix4, - { FilterQuality filterQuality = FilterQuality.low }) { - if (matrix4.length != 16) + _ImageFilter.matrix(this.creator) + : assert(creator != null), + assert(creator._type == ImageFilter._kTypeMatrix) { + if (creator._data.length != 16) throw ArgumentError('"matrix4" must have 16 entries.'); _constructor(); - _initMatrix(matrix4, filterQuality.index); + _initMatrix(creator._data, creator._filterQuality.index); } void _initMatrix(Float64List matrix4, int filterQuality) native 'ImageFilter_initMatrix'; + + /// The original Dart object that created the native wrapper, which retains + /// the values used for the filter. + final ImageFilter creator; } /// Base class for objects such as [Gradient] and [ImageShader] which diff --git a/lib/web_ui/lib/src/engine/compositor/image_filter.dart b/lib/web_ui/lib/src/engine/compositor/image_filter.dart index b638ffc13a068..0cd8840ca057e 100644 --- a/lib/web_ui/lib/src/engine/compositor/image_filter.dart +++ b/lib/web_ui/lib/src/engine/compositor/image_filter.dart @@ -10,7 +10,9 @@ part of engine; class SkImageFilter implements ui.ImageFilter { js.JsObject skImageFilter; - SkImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0}) { + SkImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0}) + : _sigmaX = sigmaX, + _sigmaY = sigmaY { skImageFilter = canvasKit['SkImageFilter'].callMethod( 'MakeBlur', [ @@ -21,4 +23,24 @@ class SkImageFilter implements ui.ImageFilter { ], ); } + + final double _sigmaX; + final double _sigmaY; + + @override + bool operator ==(dynamic other) { + if (other is! SkImageFilter) { + return false; + } + final SkImageFilter typedOther = other; + return _sigmaX == typedOther._sigmaX && _sigmaY == typedOther._sigmaY; + } + + @override + int get hashCode => ui.hashValues(_sigmaX, _sigmaY); + + @override + String toString() { + return 'ImageFilter.blur($_sigmaX, $_sigmaY)'; + } } diff --git a/lib/web_ui/lib/src/engine/shader.dart b/lib/web_ui/lib/src/engine/shader.dart index 52799bf5e2995..f1a5856be51ea 100644 --- a/lib/web_ui/lib/src/engine/shader.dart +++ b/lib/web_ui/lib/src/engine/shader.dart @@ -249,4 +249,21 @@ class EngineImageFilter implements ui.ImageFilter { final double sigmaX; final double sigmaY; + + @override + bool operator ==(dynamic other) { + if (other is! EngineImageFilter) { + return false; + } + final EngineImageFilter typedOther = other; + return sigmaX == typedOther.sigmaX && sigmaY == typedOther.sigmaY; + } + + @override + int get hashCode => ui.hashValues(sigmaX, sigmaY); + + @override + String toString() { + return 'ImageFilter.blur($sigmaX, $sigmaY)'; + } } diff --git a/testing/dart/image_filter_test.dart b/testing/dart/image_filter_test.dart new file mode 100644 index 0000000000000..3d6d201018b77 --- /dev/null +++ b/testing/dart/image_filter_test.dart @@ -0,0 +1,126 @@ +// Copyright 2019 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:test/test.dart'; + +const Color green = Color(0xFF00AA00); + +const int greenCenterBlurred = 0x1C001300; +const int greenSideBlurred = 0x15000E00; +const int greenCornerBlurred = 0x10000A00; + +const int greenCenterScaled = 0xFF00AA00; +const int greenSideScaled = 0x80005500; +const int greenCornerScaled = 0x40002B00; + +void main() { + Future getBytesForPaint(Paint paint, {int width = 3, int height = 3}) async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas recorderCanvas = Canvas(recorder); + recorderCanvas.drawRect(const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), paint); + final Picture picture = recorder.endRecording(); + final Image image = await picture.toImage(width, height); + final ByteData bytes = await image.toByteData(); + + expect(bytes.lengthInBytes, equals(width * height * 4)); + return bytes.buffer.asUint32List(); + } + + ImageFilter makeBlur(double sigmaX, double sigmaY) => + ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY); + + ImageFilter makeScale(double scX, double scY, + [double trX = 0.0, double trY = 0.0, + FilterQuality quality = FilterQuality.low]) { + trX *= 1.0 - scX; + trY *= 1.0 - scY; + return ImageFilter.matrix(Float64List.fromList([ + scX, 0.0, 0.0, 0.0, + 0.0, scY, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + trX, trY, 0.0, 1.0, + ]), filterQuality: quality); + } + + List makeList() { + return [ + makeBlur(10.0, 10.0), + makeBlur(10.0, 20.0), + makeBlur(20.0, 20.0), + makeScale(10.0, 10.0), + makeScale(10.0, 20.0), + makeScale(20.0, 10.0), + makeScale(10.0, 10.0, 1.0, 1.0), + makeScale(10.0, 10.0, 0.0, 0.0, FilterQuality.medium), + makeScale(10.0, 10.0, 0.0, 0.0, FilterQuality.high), + makeScale(10.0, 10.0, 0.0, 0.0, FilterQuality.none), + ]; + } + + void checkEquality(List a, List b) { + for (int i = 0; i < a.length; i++) { + for(int j = 0; j < a.length; j++) { + if (i == j) { + expect(a[i], equals(b[j])); + expect(a[i].hashCode, equals(b[j].hashCode)); + expect(a[i].toString(), equals(b[j].toString())); + } else { + expect(a[i], isNot(b[j])); + // No expectations on hashCode if objects are not equal + expect(a[i].toString(), isNot(b[j].toString())); + } + } + } + } + + test('ImageFilter - equals', () async { + final List A = makeList(); + final List B = makeList(); + checkEquality(A, A); + checkEquality(A, B); + checkEquality(B, B); + }); + + test('ImageFilter - nulls', () async { + final Paint paint = Paint()..imageFilter = ImageFilter.blur(sigmaX: null, sigmaY: null); + expect(paint.imageFilter, equals(ImageFilter.blur())); + + expect(() => ImageFilter.matrix(null), throwsNoSuchMethodError); + }); + + void checkBytes(Uint32List bytes, int center, int side, int corner) { + expect(bytes[0], equals(corner)); + expect(bytes[1], equals(side)); + expect(bytes[2], equals(corner)); + + expect(bytes[3], equals(side)); + expect(bytes[4], equals(center)); + expect(bytes[5], equals(side)); + + expect(bytes[6], equals(corner)); + expect(bytes[7], equals(side)); + expect(bytes[8], equals(corner)); + } + + test('ImageFilter - blur', () async { + final Paint paint = Paint() + ..color = green + ..imageFilter = makeBlur(1.0, 1.0); + + final Uint32List bytes = await getBytesForPaint(paint); + checkBytes(bytes, greenCenterBlurred, greenSideBlurred, greenCornerBlurred); + }); + + test('ImageFilter - matrix', () async { + final Paint paint = Paint() + ..color = green + ..imageFilter = makeScale(2.0, 2.0, 1.5, 1.5); + + final Uint32List bytes = await getBytesForPaint(paint); + checkBytes(bytes, greenCenterScaled, greenSideScaled, greenCornerScaled); + }); +} From 176f56329631173cec8e6b1ca6c72f34b2e9a4ed Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 19 Nov 2019 14:49:47 -0800 Subject: [PATCH 193/591] Roll src/third_party/dart eeca3fb1cb..1f933abcee (7 commits) (#13931) dart-lang/sdk@1f933abcee Convert a compile time error into a parse error (issue 39389) dart-lang/sdk@db64f9ecb2 Delete standalone_2/io/process_exit_negative_test.dart. dart-lang/sdk@db166e423e Fix unused elements when setter used dart-lang/sdk@2c5705ad23 Migrate some negative tests to static error tests. dart-lang/sdk@c2584d8046 [kernel] Align TypeParameterType.hashCode with the equality strategy dart-lang/sdk@c6efc78319 Migration: update edge builder tests to use `pointsToNever` where possible. dart-lang/sdk@26f5ea47e8 [co19] Rename status file to co19_2-co19.status --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 2673723225204..9e4d9f186d21d 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'eeca3fb1cbad65db70b317f9b2987b784c2c42b5', + 'dart_revision': '1f933abcee78f205b5231a3064681d0dffe96130', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From df928313a9f60806ca1a9a87e99bae91692f05ec Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 19 Nov 2019 23:02:59 -0500 Subject: [PATCH 194/591] Roll src/third_party/skia e678b79c489d..935a35d972e5 (13 commits) (#13927) https://skia.googlesource.com/skia.git/+log/e678b79c489d..935a35d972e5 git log e678b79c489d..935a35d972e5 --date=short --no-merges --format='%ad %ae %s' 2019-11-19 csmartdalton@google.com Reland "Enable msaa ccpr on vulkan" 2019-11-19 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-19 herb@google.com Remove fallback flag from subrun 2019-11-19 benjaminwagner@google.com Use docker recipe module for Docker Build 2019-11-19 benjaminwagner@google.com Add Debian10 GCC Docker Build 2019-11-19 mtklein@google.com Add basic line table to VTune hooks 2019-11-19 jlavrova@google.com Fixing a memory leak in OneLineShaper 2019-11-19 halcanary@google.com GN tools: copy_git_directory.py better error message. 2019-11-19 herb@google.com Fallback direct glyphs not translating properly 2019-11-19 bungeman@google.com Set fixed pitch bit in DirectWrite port. 2019-11-19 csmartdalton@google.com ccpr: Fix an assumption about weights from the tessellator 2019-11-19 mtklein@google.com add VTune integration to SkVM 2019-11-19 michaelludwig@google.com Detect empty triangles in CropRect Created with: gclient setdep -r src/third_party/skia@935a35d972e5 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 9e4d9f186d21d..67c927a8c7583 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e678b79c489dcc98ce64643d2791c7f5b5835379', + 'skia_revision': '935a35d972e56e1616393a2361ebf005c1e09bfb', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 39b94edb0329a..7b0430688e158 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 33653c8cbb61a7646d184419d7528d56 +Signature: 8d907d762cf18892893f70da0307ee5b UNUSED LICENSES: @@ -1376,6 +1376,7 @@ FILE: ../../../third_party/skia/infra/bots/jobs.json FILE: ../../../third_party/skia/infra/bots/lottie_web.isolate FILE: ../../../third_party/skia/infra/bots/pathkit.isolate FILE: ../../../third_party/skia/infra/bots/perf_skia_bundled.isolate +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json @@ -1421,6 +1422,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/unknown-docker-image.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json @@ -1647,6 +1649,7 @@ FILE: ../../../third_party/skia/infra/canvaskit/docker/canvaskit-emsdk/Dockerfil FILE: ../../../third_party/skia/infra/config/recipes.cfg FILE: ../../../third_party/skia/infra/cross-compile/docker/cross-linux-arm64/Dockerfile FILE: ../../../third_party/skia/infra/cts/whitelist_devices.json +FILE: ../../../third_party/skia/infra/gcc/Debian10/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/gold-lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/project-config/cr-buildbucket.cfg From df55ed4c5b4aa23010d4305b73f32727d5bfe887 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Wed, 20 Nov 2019 06:47:09 -0800 Subject: [PATCH 195/591] [web] Flutter for web autocorrect support (#13922) * support for autocorrect * fixing the unit tests. Note there is no unit tests for the autocorrect since the unit tests do not run on webkit * addressing PR comments * fix typo --- .../src/engine/text_editing/text_editing.dart | 17 ++++++- lib/web_ui/test/text_editing_test.dart | 47 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 8484483f6f46a..c7f75b2a90023 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -179,13 +179,15 @@ class InputConfiguration { @required this.inputType, @required this.inputAction, @required this.obscureText, + @required this.autocorrect, }); InputConfiguration.fromFlutter(Map flutterInputConfiguration) : inputType = EngineInputType.fromName( flutterInputConfiguration['inputType']['name']), inputAction = flutterInputConfiguration['inputAction'], - obscureText = flutterInputConfiguration['obscureText']; + obscureText = flutterInputConfiguration['obscureText'], + autocorrect = flutterInputConfiguration['autocorrect']; /// The type of information being edited in the input control. final EngineInputType inputType; @@ -195,6 +197,15 @@ class InputConfiguration { /// Whether to hide the text being edited. final bool obscureText; + + /// Whether to enable autocorrection. + /// + /// Definition of autocorrect can be found in: + /// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input + /// + /// For future manual tests, note that autocorrect is an attribute only + /// supported by Safari. + final bool autocorrect; } typedef _OnChangeCallback = void Function(EditingState editingState); @@ -372,6 +383,10 @@ class TextEditingElement { if (inputConfig.obscureText) { domElement.setAttribute('type', 'password'); } + + final String autocorrectValue = inputConfig.autocorrect ? 'on' : 'off'; + domElement.setAttribute('autocorrect', autocorrectValue); + _setStaticStyleAttributes(domElement); owner._setDynamicStyleAttributes(domElement); domRenderer.glassPaneElement.append(domElement); diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index 669ff6eb972ef..2e13b9c7d9add 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -26,6 +26,7 @@ final InputConfiguration singlelineConfig = InputConfiguration( inputType: EngineInputType.text, obscureText: false, inputAction: 'TextInputAction.done', + autocorrect: true, ); final Map flutterSinglelineConfig = createFlutterConfig('text'); @@ -34,6 +35,7 @@ final InputConfiguration multilineConfig = InputConfiguration( inputType: EngineInputType.multiline, obscureText: false, inputAction: 'TextInputAction.newline', + autocorrect: true, ); final Map flutterMultilineConfig = createFlutterConfig('multiline'); @@ -105,6 +107,7 @@ void main() { inputType: EngineInputType.text, inputAction: 'TextInputAction.done', obscureText: true, + autocorrect: true, ); editingElement.enable( config, @@ -119,6 +122,46 @@ void main() { editingElement.disable(); }); + test('Knows to turn autocorrect off', () { + final InputConfiguration config = InputConfiguration( + inputType: EngineInputType.text, + inputAction: 'TextInputAction.done', + obscureText: false, + autocorrect: false, + ); + editingElement.enable( + config, + onChange: trackEditingState, + onAction: trackInputAction, + ); + expect(document.getElementsByTagName('input'), hasLength(1)); + final InputElement input = document.getElementsByTagName('input')[0]; + expect(editingElement.domElement, input); + expect(input.getAttribute('autocorrect'), 'off'); + + editingElement.disable(); + }); + + test('Knows to turn autocorrect on', () { + final InputConfiguration config = InputConfiguration( + inputType: EngineInputType.text, + inputAction: 'TextInputAction.done', + obscureText: false, + autocorrect: true, + ); + editingElement.enable( + config, + onChange: trackEditingState, + onAction: trackInputAction, + ); + expect(document.getElementsByTagName('input'), hasLength(1)); + final InputElement input = document.getElementsByTagName('input')[0]; + expect(editingElement.domElement, input); + expect(input.getAttribute('autocorrect'), 'on'); + + editingElement.disable(); + }); + test('Can read editing state correctly', () { editingElement.enable( singlelineConfig, @@ -263,6 +306,7 @@ void main() { inputType: EngineInputType.text, obscureText: false, inputAction: 'TextInputAction.done', + autocorrect: true, ); editingElement.enable( config, @@ -286,6 +330,7 @@ void main() { inputType: EngineInputType.multiline, obscureText: false, inputAction: 'TextInputAction.done', + autocorrect: true, ); editingElement.enable( config, @@ -1129,6 +1174,7 @@ class PlatformMessagesSpy { Map createFlutterConfig( String inputType, { bool obscureText = false, + bool autocorrect = true, String inputAction, }) { return { @@ -1136,6 +1182,7 @@ Map createFlutterConfig( 'name': 'TextInputType.$inputType', }, 'obscureText': obscureText, + 'autocorrect': autocorrect, 'inputAction': inputAction ?? 'TextInputAction.done', }; } From ea4793d80b205076957a19afcac6732c148afa92 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 20 Nov 2019 07:55:50 -0800 Subject: [PATCH 196/591] Roll src/third_party/dart 1f933abcee..8342ec2c71 (12 commits) (#13941) dart-lang/sdk@8342ec2c71 Fix pub.dev URLs throughout SDK repo dart-lang/sdk@6546460461 chore(doc): fix typo dart-lang/sdk@73cca4480c Fix typo dart-lang/sdk@f594720679 Fix typo dart-lang/sdk@11d523dfe1 Add a test suite for "co19", which will be the NNBD-migrated co19 tests. dart-lang/sdk@246b23e5be [cfe] Implement late lowering for local variables. dart-lang/sdk@195dd200ab [cfe] Support statement replacement in inference_visitor dart-lang/sdk@f1adb0b4d8 [analyzer] use the staging url for crash reports dart-lang/sdk@453aca82a7 [dartdevc] Migrating dart:_debugger to nnbd. dart-lang/sdk@28de386937 [dart:core] Update num `operator ==` parameter type in NNBD fork dart-lang/sdk@f31fb0386b Catch exceptions during ranking and disable smart ranking dart-lang/sdk@f414465c45 Analyzer refactoring Workspace.isBazelWorkspace() to a getter: get isBazel, this is follow up on https://dart-review.googlesource.com/c/sdk/+/125564 --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 67c927a8c7583..7a8e848f7b6cd 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1f933abcee78f205b5231a3064681d0dffe96130', + 'dart_revision': '8342ec2c71f5299eadcccb1349b940752b1e7315', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index be5611b2ed44b..71db0b0001022 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 967baee9da0c48803e2e6d2138c25811 +Signature: 9511aa12f950c572fa680441191465b3 UNUSED LICENSES: From ff448c307fc6e57db6c861ee3a4040907caf3c07 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Wed, 20 Nov 2019 08:56:56 -0800 Subject: [PATCH 197/591] Fix edge detection for correct dom_renderer reset (#13940) --- lib/web_ui/lib/src/engine/browser_detection.dart | 6 ++++++ lib/web_ui/lib/src/engine/semantics/text_field.dart | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index 9cdc61edbd49c..fffcadeef6e57 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -16,6 +16,9 @@ enum BrowserEngine { /// The engine that powers Firefox. firefox, + /// The engine that powers Edge. + edge, + /// We were unable to detect the current browser engine. unknown, } @@ -30,10 +33,13 @@ BrowserEngine get browserEngine => _browserEngine ??= _detectBrowserEngine(); BrowserEngine _detectBrowserEngine() { final String vendor = html.window.navigator.vendor; + final String agent = html.window.navigator.userAgent; if (vendor == 'Google Inc.') { return BrowserEngine.blink; } else if (vendor == 'Apple Computer, Inc.') { return BrowserEngine.webkit; + } else if (agent.contains('Edge/')) { + return BrowserEngine.edge; } else if (vendor == '') { // An empty string means firefox: // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 107d9237c822b..8ce6682ed1562 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -60,6 +60,7 @@ class TextField extends RoleManager { switch (browserEngine) { case BrowserEngine.blink: + case BrowserEngine.edge: case BrowserEngine.firefox: case BrowserEngine.unknown: _initializeForBlink(); From e2e1b64fad921f202ba3264e179def270d47519b Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 20 Nov 2019 11:18:00 -0800 Subject: [PATCH 198/591] Roll src/third_party/dart 8342ec2c71..b188632458 (2 commits) (#13942) dart-lang/sdk@b188632458 Fix syntax error in Symbol doc example dart-lang/sdk@61c453f0fe Hide dart:cli in api docs --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7a8e848f7b6cd..878b2f5e67efd 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8342ec2c71f5299eadcccb1349b940752b1e7315', + 'dart_revision': 'b188632458f7c96047ff831c7153015eb63ea515', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 71db0b0001022..1303a25e397ec 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 9511aa12f950c572fa680441191465b3 +Signature: 755817d4207c9a5e7c81d6b3d876fd68 UNUSED LICENSES: From a5673e3a1f7f50ef955a8ada2f117a53b4c61a4c Mon Sep 17 00:00:00 2001 From: Ian McKellar Date: Wed, 20 Nov 2019 12:11:35 -0800 Subject: [PATCH 199/591] [flutter_runner] fix a11y tests (#13947) Instead of running over 400000000 nodes, do something a little quicker. --- .../fuchsia/flutter/accessibility_bridge_unittest.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc b/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc index d432acc7ddaed..d5c773e62daca 100644 --- a/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc +++ b/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc @@ -254,8 +254,8 @@ TEST_F(AccessibilityBridgeTest, BatchesLargeMessages) { flutter::SemanticsNodeUpdates update; - const int32_t child_nodes = fuchsia::accessibility::semantics::MAX_FAN_OUT; - const int32_t leaf_nodes = fuchsia::accessibility::semantics::MAX_FAN_OUT; + const int32_t child_nodes = 100; + const int32_t leaf_nodes = 100; for (int32_t i = 1; i < child_nodes + 1; i++) { flutter::SemanticsNode node; node.id = i; @@ -276,7 +276,7 @@ TEST_F(AccessibilityBridgeTest, BatchesLargeMessages) { RunLoopUntilIdle(); EXPECT_EQ(0, semantics_manager_.DeleteCount()); - EXPECT_EQ(13, semantics_manager_.UpdateCount()); + EXPECT_EQ(4, semantics_manager_.UpdateCount()); EXPECT_EQ(1, semantics_manager_.CommitCount()); EXPECT_FALSE(semantics_manager_.DeleteOverflowed()); EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); @@ -289,7 +289,7 @@ TEST_F(AccessibilityBridgeTest, BatchesLargeMessages) { RunLoopUntilIdle(); EXPECT_EQ(1, semantics_manager_.DeleteCount()); - EXPECT_EQ(14, semantics_manager_.UpdateCount()); + EXPECT_EQ(5, semantics_manager_.UpdateCount()); EXPECT_EQ(2, semantics_manager_.CommitCount()); EXPECT_FALSE(semantics_manager_.DeleteOverflowed()); EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); From 115f57eefc116f8a809d861f28ff39d1f7ce3501 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 20 Nov 2019 12:25:55 -0800 Subject: [PATCH 200/591] Update SwiftShader to 5d1e854. (#13945) Includes better Vulkan conformance (for which I am going to add support for in the test harness). --- DEPS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 878b2f5e67efd..e7dc0534d15ed 100644 --- a/DEPS +++ b/DEPS @@ -137,7 +137,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a985f7f63ac8ec8dae436523ec338516951ec3ff', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'db6869d0d1c3eed7c065c60a566aa063fbcb5b45', # Fuchsia compatibility # @@ -420,7 +420,7 @@ deps = { Var('github_git') + '/KhronosGroup/Vulkan-Docs.git' + '@' + 'v1.1.91', 'src/third_party/swiftshader': - Var('swiftshader_git') + '/SwiftShader.git' + '@' + 'd70129a3d3409dac58e14f819b62620393afb652', + Var('swiftshader_git') + '/SwiftShader.git' + '@' + '5d1e8540407c138f47028d64684f3da599430aa4', 'src/third_party/angle': Var('github_git') + '/google/angle.git' + '@' + '3ea90d609720b7b9b9d05ca094860382f2425294', From 537590377e221fe1fd6dd94c6b6b935bba85493d Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 20 Nov 2019 14:09:27 -0800 Subject: [PATCH 201/591] Ensure we use the base CompositorContext's AcquireFrame method when screenshotting (#13934) This ensures we rasterize into the canvas passed in as subclasses may reimplement AcquireFrame in different ways that don't utilize the canvas object passed in (such as Fuchsia's flutter_runner::CompositorContext). --- ci/licenses_golden/licenses_flutter | 1 + shell/common/BUILD.gn | 2 + .../common/fixtures/shelltest_screenshot.png | Bin 0 -> 319 bytes shell/common/rasterizer.cc | 11 +++- shell/common/shell_unittests.cc | 62 ++++++++++++++++++ 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 shell/common/fixtures/shelltest_screenshot.png diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fe19287614df0..33569a6ced33a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -503,6 +503,7 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/fixtures/shell_test.dart +FILE: ../../../flutter/shell/common/fixtures/shelltest_screenshot.png FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f93bca63478f0..9d57ee5ff5ecf 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -153,6 +153,8 @@ if (current_toolchain == host_toolchain) { test_fixtures("shell_unittests_fixtures") { dart_main = "fixtures/shell_test.dart" + + fixtures = [ "fixtures/shelltest_screenshot.png" ] } shell_host_executable("shell_unittests") { diff --git a/shell/common/fixtures/shelltest_screenshot.png b/shell/common/fixtures/shelltest_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad7d7e01512a3167df9bd041df566959284441a GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^DImT| xJ?`HxE8CWQmQFzxw+RxCNkf9c0`$X!i@XN%Obc=Xn>m3z22WQ%mvv4FO#rLISzQ1C literal 0 HcmV?d00001 diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d61e1ff86ddc3..f40be319848bb 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -399,9 +399,14 @@ static sk_sp ScreenshotLayerTreeAsImage( SkMatrix root_surface_transformation; root_surface_transformation.reset(); - auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr, - root_surface_transformation, - false, nullptr); + // We want to ensure we call the base method for + // CompositorContext::AcquireFrame instead of the platform-specific method. + // Specifically, Fuchsia's CompositorContext handles the rendering surface + // itself which means that we will still continue to render to the onscreen + // surface if we don't call the base method. + auto frame = compositor_context.flutter::CompositorContext::AcquireFrame( + surface_context, canvas, nullptr, root_surface_transformation, false, + nullptr); canvas->clear(SK_ColorTRANSPARENT); frame->Raster(*tree, true); canvas->flush(); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 0146619cce4cf..b69bf226b5c61 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -10,6 +10,7 @@ #include #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/layers/picture_layer.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/fml/command_line.h" #include "flutter/fml/make_copyable.h" @@ -952,5 +953,66 @@ TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) { DestroyShell(std::move(shell), std::move(task_runners)); } +TEST_F(ShellTest, Screenshot) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent firstFrameLatch; + settings.frame_rasterized_callback = + [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); }; + + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + + LayerTreeBuilder builder = [&](std::shared_ptr root) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = + recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80)); + recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80), + SkPaint(SkColor4f::FromColor(SK_ColorRED))); + auto sk_picture = recorder.finishRecordingAsPicture(); + fml::RefPtr queue = fml::MakeRefCounted( + this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(10, 10), + flutter::SkiaGPUObject({sk_picture, queue}), false, false); + root->Add(picture_layer); + }; + + PumpOneFrame(shell.get(), 100, 100, builder); + firstFrameLatch.Wait(); + + std::promise screenshot_promise; + auto screenshot_future = screenshot_promise.get_future(); + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetGPUTaskRunner(), + [&screenshot_promise, &shell]() { + auto rasterizer = shell->GetRasterizer(); + screenshot_promise.set_value(rasterizer->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::CompressedImage, false)); + }); + + auto fixtures_dir = + fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); + + auto reference_png = fml::FileMapping::CreateReadOnly( + fixtures_dir, "shelltest_screenshot.png"); + + // Use MakeWithoutCopy instead of MakeWithCString because we don't want to + // encode the null sentinel + sk_sp reference_data = SkData::MakeWithoutCopy( + reference_png->GetMapping(), reference_png->GetSize()); + + ASSERT_TRUE(reference_data->equals(screenshot_future.get().data.get())); + + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter From 6bf311f1d62cc5a0136456748de9d20c6bd589fd Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 20 Nov 2019 15:15:48 -0800 Subject: [PATCH 202/591] Roll src/third_party/dart b188632458..867a6e0e52 (4 commits) (#13948) dart-lang/sdk@867a6e0e52 [dart:core] Update DartTime.parse to support arbitrary precision fractional seconds. dart-lang/sdk@ae34d6e233 [dartdevc] rename legacy to ddc dart-lang/sdk@733db095ce Move nullabilitySuffix to the public interface for DartType. dart-lang/sdk@635aaa8adf [vm/dart2native] Bundle product-mode vm_platform.dill with the dart-sdk and let dart2native use it --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e7dc0534d15ed..7da30d4d78bc4 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'b188632458f7c96047ff831c7153015eb63ea515', + 'dart_revision': '867a6e0e5288fbc177e4e145287438aebaaa7034', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1303a25e397ec..05c915f00ed0f 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 755817d4207c9a5e7c81d6b3d876fd68 +Signature: a5920f76e5510b35459f38e8498a85cc UNUSED LICENSES: From defb0e5b2ba5ebbed6b8cdf03d1c9296e2de76a2 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 20 Nov 2019 19:05:23 -0500 Subject: [PATCH 203/591] Roll fuchsia/sdk/core/linux-amd64 from 7mmHP... to VHWsK... (#13939) Roll fuchsia/sdk/core/linux-amd64 from 7mmHP... to VHWsK... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 7da30d4d78bc4..59bda73a849d0 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': '7mmHPhROLk7ei9wAP1aDBMeONqeZHKQBDR2J5DtkY0QC' + 'version': 'VHWsKCT23LZe48X50wAxvE-ZbjyazzpKraaB_5YviXwC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index fba9b4572ff39..b6e73c9141485 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 49f79b16acb1fa261c6f3f57173f65d0 +Signature: 133a4e09cdde49c76ccd02af36114e69 UNUSED LICENSES: @@ -1294,6 +1294,7 @@ FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/ssh.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/storage.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/test.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/common.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/drm_fps.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/flutter_frame_stats.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/scenic_frame_stats.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_results.dart @@ -2847,7 +2848,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.auth/account/account.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/agent/agent.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/agent/agent_context.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/agent/agent_controller/agent_controller.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/clipboard/clipboard.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/component/component_context.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/config/config.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/entity/entity.fidl From d6fa188d78ab663a6d9bf50f8684a219839fd513 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 20 Nov 2019 19:07:43 -0500 Subject: [PATCH 204/591] Roll src/third_party/skia 935a35d972e5..f9291b8942d8 (13 commits) (#13938) https://skia.googlesource.com/skia.git/+log/935a35d972e5..f9291b8942d8 git log 935a35d972e5..f9291b8942d8 --date=short --no-merges --format='%ad %ae %s' 2019-11-20 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 e4b3136913c6..99bd10b70422 (13 commits) 2019-11-20 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src b659e40a3998..0db96f99f73d (373 commits) 2019-11-20 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 79afb7620a69..7e11f46ddf8e (1 commits) 2019-11-20 mtklein@google.com drop sse4.1 limit on valgrind bots 2019-11-20 ethannicholas@google.com Fixed some GrSkSLFP limitations. 2019-11-20 mtklein@google.com skip _imm ops on ARM 2019-11-19 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-19 csmartdalton@google.com Block MSAA CCPR on NVIDIA without mixed samples 2019-11-19 csmartdalton@google.com Don't sync unique keys on ccpr clip FP proxies 2019-11-19 mtklein@google.com initialize to avoid Valgrind false positive 2019-11-19 egdaniel@google.com Handle failure to submit semaphores in vulkan backend. 2019-11-19 jvanverth@google.com Metal: Simplify GrFence to use completedHandler in all cases. 2019-11-19 sgilhuly@chromium.org Fix unique_ptr typo in Skia Dawn Created with: gclient setdep -r src/third_party/skia@f9291b8942d8 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DEPS b/DEPS index 59bda73a849d0..1cc44746d5067 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '935a35d972e56e1616393a2361ebf005c1e09bfb', + 'skia_revision': 'f9291b8942d8783f88ce421c1780231c90d365af', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 7b0430688e158..03daca36dbe7f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8d907d762cf18892893f70da0307ee5b +Signature: 890c107c656249e7f696901a85767528 UNUSED LICENSES: @@ -1457,7 +1457,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json @@ -1516,7 +1516,7 @@ FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Debian9-Cl FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json @@ -1592,8 +1592,8 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-C FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json From 7a77e3625dd1db5f4f517a129707cb299c48207a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 20 Nov 2019 19:09:50 -0500 Subject: [PATCH 205/591] Roll fuchsia/sdk/core/mac-amd64 from bC9Qy... to FQdiV... (#13936) Roll fuchsia/sdk/core/mac-amd64 from bC9Qy... to FQdiV... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1cc44746d5067..a84e18c9778de 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'bC9QyoghiSJSFqgRVvUeiVU0MhBMKzsRhvfWmaBc46oC' + 'version': 'FQdiVAvvRBLXIOBI2l5xnfN-_wH-kCvbXL8Pz62qwGoC' } ], 'condition': 'host_os == "mac"', From 9e6d360177580f68f1321b56da7fbffc300d9d3f Mon Sep 17 00:00:00 2001 From: liyuqian Date: Wed, 20 Nov 2019 16:36:23 -0800 Subject: [PATCH 206/591] Fix picture raster cache throttling (#13710) Previously, we're also counting the pictures that are already raster cached. This fixes https://github.com/flutter/flutter/issues/44252 and helps solving the GPU thread issue of https://github.com/flutter/flutter/issues/43083 https://github.com/flutter/flutter/pull/45050 is a performance test in the framework repo to reveal this bug. --- flow/raster_cache.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 0a2e084c96455..7854dac3fbfe9 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -219,8 +219,8 @@ bool RasterCache::Prepare(GrContext* context, if (!entry.image.is_valid()) { entry.image = RasterizePicture(picture, context, transformation_matrix, dst_color_space, checkerboard_images_); + picture_cached_this_frame_++; } - picture_cached_this_frame_++; return true; } From 92b2d011c269220f9124c4d0e60824e5f2f7b588 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Wed, 20 Nov 2019 17:56:00 -0800 Subject: [PATCH 207/591] Made the thread checker print out the thread names on Apple platforms. (#13943) --- fml/memory/thread_checker.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fml/memory/thread_checker.h b/fml/memory/thread_checker.h index c6314ca62128b..cd972955d30c4 100644 --- a/fml/memory/thread_checker.h +++ b/fml/memory/thread_checker.h @@ -47,7 +47,25 @@ class ThreadChecker final { // Returns true if the current thread is the thread this object was created // on and false otherwise. bool IsCreationThreadCurrent() const { - return !!pthread_equal(pthread_self(), self_); + pthread_t current_thread = pthread_self(); + bool is_creation_thread_current = !!pthread_equal(current_thread, self_); +#ifdef __APPLE__ + // TODO(https://github.com/flutter/flutter/issues/45272): Implement for + // other platforms. + if (!is_creation_thread_current) { + static const int buffer_length = 128; + char expected_thread[buffer_length]; + char actual_thread[buffer_length]; + if (0 == pthread_getname_np(current_thread, actual_thread, + buffer_length) && + 0 == pthread_getname_np(self_, actual_thread, buffer_length)) { + FML_DLOG(ERROR) << "IsCreationThreadCurrent expected thread: '" + << expected_thread << "' actual thread:'" + << actual_thread << "'"; + } + } +#endif // __APPLE__ + return is_creation_thread_current; } private: From bc17495c228ff4713d58919b5f10c4d4450a2134 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 21 Nov 2019 09:11:05 -0800 Subject: [PATCH 208/591] Roll src/third_party/dart 867a6e0e52..aa2b3a5eaa (15 commits) (#13955) dart-lang/sdk@aa2b3a5eaa [cfe] Inference constraint solving takes contravariance into account. dart-lang/sdk@5a8c52e06a Add script that performs the tasks of creating and checking the nnbd sdk dart-lang/sdk@4ce204d16a Migration: remove unused variable dart-lang/sdk@97e432a5bc Migration: refactor _checkExpressionNotNull. dart-lang/sdk@8d328738b8 [analyzer] Add variance to analyzer summaries. dart-lang/sdk@3c9e924073 Revamp patch_sdk.dart mainly to use libraries.json. dart-lang/sdk@be519e83b1 Remove XyzElementImpl.forNode() constructors. dart-lang/sdk@a9c1f68135 Oops, meant to commit these changes with: dart-lang/sdk@b431b2fb0d Point analyzer NNBD bot to `language` rather than `language_2` dart-lang/sdk@4ee62c3479 Migrate all of the negative language_2 tests to not be negative tests. dart-lang/sdk@b55ae4e586 Remove TypeSystem.create() dart-lang/sdk@488daa270c [dartdevc] Fix analysis errors in patched dart:core library dart-lang/sdk@ee9a640c9d [vm] Allocate temporaries used by Dart_Invoke etc in new-space. dart-lang/sdk@b3cf5ef327 [observatory] Properly wait for Catapult's iframe to load. Give message while fetching timeline. dart-lang/sdk@b14a4fbf49 Update the comment on parseString to clarify throwIfDiagnostics behavior. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index a84e18c9778de..4f9263b548ca8 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '867a6e0e5288fbc177e4e145287438aebaaa7034', + 'dart_revision': 'aa2b3a5eaa4adb0d11eabdcc15c4056a3dc8602a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 05c915f00ed0f..1baa3bef1da67 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: a5920f76e5510b35459f38e8498a85cc +Signature: 7da493d646e9f99c5b1b71762f1fe64d UNUSED LICENSES: @@ -8059,7 +8059,6 @@ FILE: ../../../third_party/dart/samples/sample_extension/test_sample_synchronous FILE: ../../../third_party/dart/samples/samples.status FILE: ../../../third_party/dart/sdk/lib/_http/crypto.dart FILE: ../../../third_party/dart/sdk/lib/_http/http_date.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/libraries.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart @@ -8185,7 +8184,6 @@ FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/crypto.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/http_date.dart -FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/libraries.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/async_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/core_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/isolate_patch.dart From a38839817cda09745ac64517b155e097e00dc786 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 21 Nov 2019 14:28:36 -0500 Subject: [PATCH 209/591] Roll fuchsia/sdk/core/linux-amd64 from VHWsK... to zgJ75... (#13957) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4f9263b548ca8..2dabe4f3aa119 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'VHWsKCT23LZe48X50wAxvE-ZbjyazzpKraaB_5YviXwC' + 'version': 'zgJ75e5QUIdgVhPAeEnDsHJJLIY_MPiaQajh8EM6Gu4C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index b6e73c9141485..02f9f17e8bc9c 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 133a4e09cdde49c76ccd02af36114e69 +Signature: 55184f5bf77dcda79698058bba784747 UNUSED LICENSES: From f91934073ee11e0ac2a8f9029ab45c2816ab26e8 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 21 Nov 2019 11:52:40 -0800 Subject: [PATCH 210/591] Roll src/third_party/dart aa2b3a5eaa..521b168441 (15 commits) (#13958) dart-lang/sdk@521b168441 [co19] Add tests/co19_2/co19_2-co19.status to test_matrix.json dart-lang/sdk@8295efc541 Revert "Revamp patch_sdk.dart mainly to use libraries.json." dart-lang/sdk@acff31ed26 [cfe] Support null-aware index access dart-lang/sdk@f8d42542dd [vm/compiler] ARM64: Block R22 to hold NullObject(). dart-lang/sdk@afa82d8e98 [co19] Add missing backslash to update script dart-lang/sdk@28a029102b [SDK] Adds --support-disassembler in PRODUCT. dart-lang/sdk@45569e146e [SDK] Fixes C++ undef. behaviour in Library::Add*Metadata. dart-lang/sdk@51f14eb911 [co19] Fork the update script for the co19 NNBD tests dart-lang/sdk@eb6d7f51e2 [dart2js] Add DartTypes for T*, T?, and Never. dart-lang/sdk@4c8c44ec78 [cfe] Implement lowering for constructor initialization of late fields dart-lang/sdk@f692a2fbb8 [cfe] Eliminate Nullability.legacy in transformers dart-lang/sdk@539b0093e1 [dart2js] Remove unnecessary DartType predicates and replace with is-tests. dart-lang/sdk@2e32972929 [pkg/vm] Remove usage of Library.isExternal dart-lang/sdk@54011e5622 [analyzer] Fix format.fbs after summary change for variance. dart-lang/sdk@615928c514 Stop referencing non-API TypeSystem in analyzer. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2dabe4f3aa119..969266c581b0a 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'aa2b3a5eaa4adb0d11eabdcc15c4056a3dc8602a', + 'dart_revision': '521b168441143861d928c9315c9830469617028b', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1baa3bef1da67..e9f5242feacd5 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 7da493d646e9f99c5b1b71762f1fe64d +Signature: f7334fbbdc2435fe4814f8135afa73d6 UNUSED LICENSES: @@ -8059,6 +8059,7 @@ FILE: ../../../third_party/dart/samples/sample_extension/test_sample_synchronous FILE: ../../../third_party/dart/samples/samples.status FILE: ../../../third_party/dart/sdk/lib/_http/crypto.dart FILE: ../../../third_party/dart/sdk/lib/_http/http_date.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/libraries.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart @@ -8184,6 +8185,7 @@ FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/crypto.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/http_date.dart +FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/libraries.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/async_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/core_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/isolate_patch.dart From 5bc410f1a27b8708b396449f8d738603d7617b2a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 21 Nov 2019 15:16:48 -0500 Subject: [PATCH 211/591] Roll src/third_party/skia f9291b8942d8..4e0255508749 (27 commits) (#13954) https://skia.googlesource.com/skia.git/+log/f9291b8942d8..4e0255508749 git log f9291b8942d8..4e0255508749 --date=short --no-merges --format='%ad %ae %s' 2019-11-21 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-21 herb@google.com Remove device paths from SubRun 2019-11-20 mtklein@google.com support 2 immediates 2019-11-20 mtklein@google.com fix SKVM_PERF_DUMPS 2019-11-20 halcanary@google.com Revert "public.bzl: update to replace Gif with Wuffs" 2019-11-20 robertphillips@google.com No longer ref count GrGeometryProcessors 2019-11-20 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms 2914b63d6886..68d3f3a95f1b (1 commits) 2019-11-20 halcanary@google.com Revert "Replace third_party/gif with new DEPS entry." 2019-11-20 halcanary@google.com BUILD: libgifcodec: fix flutter build 2019-11-20 rosasco@google.com Use system-temp storage. 2019-11-20 jsimmons@google.com Find and cache font family lists and the typeface lists that they map to 2019-11-20 mtklein@google.com SkReader32::setMemory() requires 4-byte alignment 2019-11-20 michaelludwig@google.com Specialize vertex writing function for common quad VertexSpecs 2019-11-20 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-20 egdaniel@google.com Various fixes for supporting failures in VkPipeline and cache creation. 2019-11-20 halcanary@google.com Replace third_party/gif with new DEPS entry. 2019-11-20 egdaniel@google.com Update GrVkImageView to handle failure of VkImageView creation. 2019-11-20 jvanverth@google.com Fix for Metal framebufferOnly MSAA texture. 2019-11-20 halcanary@google.com public.bzl: update to replace Gif with Wuffs 2019-11-20 scroggo@google.com Add arm64 Wuffs perf bots 2019-11-20 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms 8d45badce994..2914b63d6886 (1 commits) 2019-11-20 robertphillips@google.com Revert "Reland "Enable msaa ccpr on vulkan"" 2019-11-20 robertphillips@google.com Revert "Block MSAA CCPR on NVIDIA without mixed samples" 2019-11-20 halcanary@google.com SkQP: remove unused script 2019-11-20 borenet@google.com [infra] Use CIPD packages from infra repo 2019-11-20 kjlubick@google.com [canvaskit] Include direction from getRects 2019-11-20 benjaminwagner@google.com Add additional GCC Builds using Docker Created with: gclient setdep -r src/third_party/skia@4e0255508749 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index 969266c581b0a..115b2e795c1a4 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f9291b8942d8783f88ce421c1780231c90d365af', + 'skia_revision': '4e0255508749ce2d595dfdf68b3ba6bcaa672b7e', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 03daca36dbe7f..7c57aab979e9d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 890c107c656249e7f696901a85767528 +Signature: a15ff6749fc2a8cff5e4d7e15f8d5dfb UNUSED LICENSES: @@ -1376,11 +1376,16 @@ FILE: ../../../third_party/skia/infra/bots/jobs.json FILE: ../../../third_party/skia/infra/bots/lottie_web.isolate FILE: ../../../third_party/skia/infra/bots/pathkit.isolate FILE: ../../../third_party/skia/infra/bots/perf_skia_bundled.isolate -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-loongson3a-Release-Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86-Debug-Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Debug-Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Shared_Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm64-Release-Android_Wuffs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86-devrel-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Coverage.json @@ -1416,6 +1421,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-Metal.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Release-MoltenVK_Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Ubuntu18-GCC-x86_64-Release-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json @@ -1649,7 +1655,10 @@ FILE: ../../../third_party/skia/infra/canvaskit/docker/canvaskit-emsdk/Dockerfil FILE: ../../../third_party/skia/infra/config/recipes.cfg FILE: ../../../third_party/skia/infra/cross-compile/docker/cross-linux-arm64/Dockerfile FILE: ../../../third_party/skia/infra/cts/whitelist_devices.json +FILE: ../../../third_party/skia/infra/gcc/Debian10-mips64el/Dockerfile +FILE: ../../../third_party/skia/infra/gcc/Debian10-x86/Dockerfile FILE: ../../../third_party/skia/infra/gcc/Debian10/Dockerfile +FILE: ../../../third_party/skia/infra/gcc/Ubuntu18/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/gold-lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/project-config/cr-buildbucket.cfg @@ -3827,8 +3836,6 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelper.h FILE: ../../../third_party/skia/src/gpu/ops/GrStencilPathOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrTextureOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrTextureOp.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkBufferView.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkBufferView.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkSemaphore.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkSemaphore.h FILE: ../../../third_party/skia/src/opts/SkUtils_opts.h From f37ca31da462cb64c05f58139e06007976a2967d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 21 Nov 2019 15:19:16 -0500 Subject: [PATCH 212/591] Roll fuchsia/sdk/core/mac-amd64 from FQdiV... to l1EAN... (#13956) Roll fuchsia/sdk/core/mac-amd64 from FQdiV... to l1EAN... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 115b2e795c1a4..aec146de6d206 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'FQdiVAvvRBLXIOBI2l5xnfN-_wH-kCvbXL8Pz62qwGoC' + 'version': 'l1EANDzCbwqmrL4BVDrtx0A4u3lI39qi6eTzfQzFu4UC' } ], 'condition': 'host_os == "mac"', From 96fc607b8a543c85da0682465da3a1978f192961 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 21 Nov 2019 12:36:53 -0800 Subject: [PATCH 213/591] [web] Refactor text editing to handle any order of platform messages gracefully (#13741) --- .../src/engine/text_editing/text_editing.dart | 281 +++++++++--------- lib/web_ui/test/text_editing_test.dart | 60 +++- 2 files changed, 194 insertions(+), 147 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index c7f75b2a90023..edba66651a583 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -12,6 +12,16 @@ const int _kReturnKeyCode = 13; void _emptyCallback(dynamic _) {} +/// Indicates whether virtual keyboard shifts the location of input element. +/// +/// Value decided using the operating system and the browser engine. +/// +/// In iOS, the virtual keyboard might shifts the screen up to make input +/// visible depending on the location of the focused input element. +bool get _doesKeyboardShiftInput => + browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs; + /// These style attributes are constant throughout the life time of an input /// element. /// @@ -48,6 +58,7 @@ void _setStaticStyleAttributes(html.HtmlElement domElement) { } /// The current text and selection state of a text field. +@visibleForTesting class EditingState { EditingState({this.text, this.baseOffset = 0, this.extentOffset = 0}); @@ -249,33 +260,30 @@ class TextEditingElement { InputConfiguration _inputConfiguration; EditingState _lastEditingState; + /// Styles associated with the editable text. + _EditingStyle _style; + + /// Size and transform of the editable text on the page. + _GeometricInfo _geometricInfo; + _OnChangeCallback _onChange; _OnActionCallback _onAction; final List> _subscriptions = >[]; - /// On iOS, sets the location of the input element after focusing on it. + /// Whether or not the input element can be positioned at this point in time. /// - /// On iOS, keyboard causes scrolling in the UI. This scrolling does not - /// trigger an event. In order not to trigger a shift on the page, it is - /// important we set it's final location after focusing on it (after keyboard - /// is up). + /// This is currently only used in iOS. It's set to false before focusing the + /// input field, and set back to true after a short timer. We do this because + /// if the input field is positioned before focus, it could be pushed to an + /// incorrect position by the virtual keyboard. /// - /// This method is called after a delay. - /// See [_positionInputElementTimer]. - void configureInputElementForIOS() { - if (browserEngine != BrowserEngine.webkit || - operatingSystem != OperatingSystem.iOs) { - // Only relevant on Safari-based on iOS. - return; - } - - if (domElement != null) { - owner.setStyle(domElement); - owner.inputPositioned = true; - } - } + /// See: + /// + /// * [_delayBeforePositioning] which controls how long to wait before + /// positioning the input field. + bool _canPosition = true; /// Enables the element so it can be used to edit text. /// @@ -315,7 +323,7 @@ class TextEditingElement { })); } - if (owner.doesKeyboardShiftInput) { + if (_doesKeyboardShiftInput) { _preventShiftDuringFocus(); } domElement.focus(); @@ -366,6 +374,8 @@ class TextEditingElement { isEnabled = false; _lastEditingState = null; + _style = null; + _geometricInfo = null; for (int i = 0; i < _subscriptions.length; i++) { _subscriptions[i].cancel(); @@ -373,7 +383,6 @@ class TextEditingElement { _subscriptions.clear(); _positionInputElementTimer?.cancel(); _positionInputElementTimer = null; - owner.inputPositioned = false; _removeDomElement(); } @@ -388,7 +397,7 @@ class TextEditingElement { domElement.setAttribute('autocorrect', autocorrectValue); _setStaticStyleAttributes(domElement); - owner._setDynamicStyleAttributes(domElement); + applyAllStyles(); domRenderer.glassPaneElement.append(domElement); } @@ -401,19 +410,38 @@ class TextEditingElement { domElement.focus(); } + /// Set style to the native DOM element used for text editing. + /// + /// It will be located exactly in the same place with the editable widgets, + /// however it's contents and cursor will be invisible. + /// + /// Users can interact with the element and use the functionalities of the + /// right-click menu. Such as copy,paste, cut, select, translate... + void applyAllStyles() { + _style?.applyToDomElement(domElement); + _positionElement(); + } + + void _positionElement() { + if (_canPosition && _geometricInfo != null) { + _geometricInfo.applyToDomElement(domElement); + } + } + void _preventShiftDuringFocus() { // Position the element outside of the page before focusing on it. // // See [_positionInputElementTimer]. owner.setStyleOutsideOfScreen(domElement); + _canPosition = false; + // TODO(mdebbar): Should we remove this listener after the first invocation? _subscriptions.add(domElement.onFocus.listen((_) { // Cancel previous timer if exists. _positionInputElementTimer?.cancel(); _positionInputElementTimer = Timer(_delayBeforePositioning, () { - if (textEditing.inputElementNeedsToBePositioned) { - configureInputElementForIOS(); - } + _canPosition = true; + _positionElement(); }); // When the virtual keyboard is closed on iOS, onBlur is triggered. @@ -435,15 +463,26 @@ class TextEditingElement { _lastEditingState.applyToDomElement(domElement); - if (owner.inputElementNeedsToBePositioned) { - _preventShiftDuringFocus(); - } - // Re-focuses when setting editing state. domElement.focus(); } + void setGeometricInfo(_GeometricInfo geometricInfo) { + _geometricInfo = geometricInfo; + if (isEnabled) { + _positionElement(); + } + } + + void setStyle(_EditingStyle style) { + _style = style; + if (isEnabled) { + _style.applyToDomElement(domElement); + } + } + void _handleChange(html.Event event) { + assert(isEnabled); assert(domElement != null); EditingState newEditingState = EditingState.fromDomElement(domElement); @@ -587,17 +626,6 @@ class HybridTextEditing { @visibleForTesting bool isEditing = false; - /// Indicates whether the input element needs to be positioned. - /// - /// See [TextEditingElement._delayBeforePositioning]. - bool get inputElementNeedsToBePositioned => - !inputPositioned && isEditing && doesKeyboardShiftInput; - - /// Flag indicating whether the input element's position is set. - /// - /// See [inputElementNeedsToBePositioned]. - bool inputPositioned = false; - InputConfiguration _configuration; /// All "flutter/textinput" platform messages should be sent to this method. @@ -626,11 +654,12 @@ class HybridTextEditing { break; case 'TextInput.setEditableSizeAndTransform': - _setLocation(call.arguments); + editingElement + .setGeometricInfo(_GeometricInfo.fromFlutter(call.arguments)); break; case 'TextInput.setStyle': - _setFontStyle(call.arguments); + editingElement.setStyle(_EditingStyle.fromFlutter(call.arguments)); break; case 'TextInput.clearClient': @@ -658,58 +687,6 @@ class HybridTextEditing { editingElement.disable(); } - _EditingStyle _editingStyle; - _EditingStyle get editingStyle => _editingStyle; - - /// Use the font size received from Flutter if set. - String font() { - assert(_editingStyle != null); - return '${_editingStyle.fontWeight} ${_editingStyle.fontSize}px ${_editingStyle.fontFamily}'; - } - - void _setFontStyle(Map style) { - assert(style.containsKey('fontSize')); - assert(style.containsKey('fontFamily')); - assert(style.containsKey('textAlignIndex')); - assert(style.containsKey('textDirectionIndex')); - - final int textAlignIndex = style['textAlignIndex']; - final int textDirectionIndex = style['textDirectionIndex']; - - /// Converts integer value coming as fontWeightIndex from TextInput.setStyle - /// to its CSS equivalent value. - /// Converts index of TextAlign to enum value. - _editingStyle = _EditingStyle( - textDirection: ui.TextDirection.values[textDirectionIndex], - fontSize: style['fontSize'], - textAlign: ui.TextAlign.values[textAlignIndex], - fontFamily: style['fontFamily'], - fontWeightIndex: style['fontWeightIndex']); - } - - /// Size and transform of the editable text on the page. - _EditableSizeAndTransform _editingLocationAndSize; - _EditableSizeAndTransform get editingLocationAndSize => - _editingLocationAndSize; - - void _setLocation(Map editingLocationAndSize) { - assert(editingLocationAndSize.containsKey('width')); - assert(editingLocationAndSize.containsKey('height')); - assert(editingLocationAndSize.containsKey('transform')); - - final List transformList = - List.from(editingLocationAndSize['transform']); - _editingLocationAndSize = _EditableSizeAndTransform( - width: editingLocationAndSize['width'], - height: editingLocationAndSize['height'], - transform: Float64List.fromList(transformList), - ); - - if (editingElement.domElement != null) { - _setDynamicStyleAttributes(editingElement.domElement); - } - } - void _syncEditingStateToFlutter(EditingState editingState) { ui.window.onPlatformMessage( 'flutter/textinput', @@ -736,52 +713,6 @@ class HybridTextEditing { ); } - /// Positioning of input element is only done if we are not expecting input - /// to be shifted by a virtual keyboard or if the input is already positioned. - /// - /// Otherwise positioning will be done after focusing on the input. - /// See [TextEditingElement._delayBeforePositioning]. - bool get _canPositionInput => inputPositioned || !doesKeyboardShiftInput; - - /// Indicates whether virtual keyboard shifts the location of input element. - /// - /// Value decided using the operating system and the browser engine. - /// - /// In iOS, the virtual keyboard might shifts the screen up to make input - /// visible depending on the location of the focused input element. - bool get doesKeyboardShiftInput => - browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs; - - /// These style attributes are dynamic throughout the life time of an input - /// element. - /// - /// They are changed depending on the messages coming from method calls: - /// "TextInput.setStyle", "TextInput.setEditableSizeAndTransform". - void _setDynamicStyleAttributes(html.HtmlElement domElement) { - if (_editingLocationAndSize != null && _canPositionInput) { - setStyle(domElement); - } - } - - /// Set style to the native DOM element used for text editing. - /// - /// It will be located exactly in the same place with the editable widgets, - /// however it's contents and cursor will be invisible. - /// - /// Users can interact with the element and use the functionalities of the - /// right-click menu. Such as copy,paste, cut, select, translate... - void setStyle(html.HtmlElement domElement) { - final String transformCss = - float64ListToCssTransform(_editingLocationAndSize.transform); - domElement.style - ..width = '${_editingLocationAndSize.width}px' - ..height = '${_editingLocationAndSize.height}px' - ..textAlign = _editingStyle.align - ..font = font() - ..transform = transformCss; - } - // TODO(flutter_web): After the browser closes and re-opens the virtual // shifts the page in iOS. Call this method from visibility change listener // attached to body. @@ -805,10 +736,35 @@ class _EditingStyle { @required this.fontSize, @required this.textAlign, @required this.fontFamily, - @required fontWeightIndex, - }) : this.fontWeight = (fontWeightIndex != null) - ? fontWeightIndexToCss(fontWeightIndex: fontWeightIndex) - : 'normal'; + @required this.fontWeight, + }); + + factory _EditingStyle.fromFlutter(Map flutterStyle) { + assert(flutterStyle.containsKey('fontSize')); + assert(flutterStyle.containsKey('fontFamily')); + assert(flutterStyle.containsKey('textAlignIndex')); + assert(flutterStyle.containsKey('textDirectionIndex')); + + final int textAlignIndex = flutterStyle['textAlignIndex']; + final int textDirectionIndex = flutterStyle['textDirectionIndex']; + final int fontWeightIndex = flutterStyle['fontWeightIndex']; + + // Convert [fontWeightIndex] to its CSS equivalent value. + final String fontWeight = fontWeightIndex != null + ? fontWeightIndexToCss(fontWeightIndex: fontWeightIndex) + : 'normal'; + + // Also convert [textAlignIndex] and [textDirectionIndex] to their + // corresponding enum values in [ui.TextAlign] and [ui.TextDirection] + // respectively. + return _EditingStyle( + fontSize: flutterStyle['fontSize'], + fontFamily: flutterStyle['fontFamily'], + textAlign: ui.TextAlign.values[textAlignIndex], + textDirection: ui.TextDirection.values[textDirectionIndex], + fontWeight: fontWeight, + ); + } /// This information will be used for changing the style of the hidden input /// element, which will match it's size to the size of the editable widget. @@ -819,20 +775,53 @@ class _EditingStyle { final ui.TextDirection textDirection; String get align => textAlignToCssValue(textAlign, textDirection); + + String get cssFont => '${fontWeight} ${fontSize}px ${fontFamily}'; + + void applyToDomElement(html.HtmlElement domElement) { + domElement.style + ..textAlign = align + ..font = cssFont; + } } /// Information on the location and size of the editing element. /// /// This information is received via "TextInput.setEditableSizeAndTransform" /// message. Framework currently sends this information on paint. -class _EditableSizeAndTransform { - _EditableSizeAndTransform({ +class _GeometricInfo { + _GeometricInfo({ @required this.width, @required this.height, @required this.transform, }); + factory _GeometricInfo.fromFlutter( + Map flutterMap, + ) { + assert(flutterMap.containsKey('width')); + assert(flutterMap.containsKey('height')); + assert(flutterMap.containsKey('transform')); + + final List transformList = + List.from(flutterMap['transform']); + return _GeometricInfo( + width: flutterMap['width'], + height: flutterMap['height'], + transform: Float64List.fromList(transformList), + ); + } + final double width; final double height; final Float64List transform; + + String get cssTransform => float64ListToCssTransform(transform); + + void applyToDomElement(html.HtmlElement domElement) { + domElement.style + ..width = '${width}px' + ..height = '${height}px' + ..transform = cssTransform; + } } diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index 2e13b9c7d9add..9a5e32f973fba 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -681,7 +681,7 @@ void main() { }); test( - 'setClient, setLocationSize, setStyle, setEditingState, show, clearClient', + 'setClient, setEditableSizeAndTransform, setStyle, setEditingState, show, clearClient', () { final MethodCall setClient = MethodCall( 'TextInput.setClient', [123, flutterSinglelineConfig]); @@ -728,6 +728,64 @@ void main() { expect(spy.messages, isEmpty); }); + test( + 'setClient, show, setEditableSizeAndTransform, setStyle, setEditingState, clearClient', + () { + final MethodCall setClient = MethodCall( + 'TextInput.setClient', [123, flutterSinglelineConfig]); + textEditing.handleTextInput(codec.encodeMethodCall(setClient)); + + const MethodCall show = MethodCall('TextInput.show'); + textEditing.handleTextInput(codec.encodeMethodCall(show)); + + final MethodCall setSizeAndTransform = + configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues( + 10.0, + 20.0, + 30.0, + ).storage.toList()); + textEditing + .handleTextInput(codec.encodeMethodCall(setSizeAndTransform)); + + final MethodCall setStyle = + configureSetStyleMethodCall(12, 'sans-serif', 4, 4, 1); + textEditing.handleTextInput(codec.encodeMethodCall(setStyle)); + + const MethodCall setEditingState = + MethodCall('TextInput.setEditingState', { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + }); + textEditing.handleTextInput(codec.encodeMethodCall(setEditingState)); + + final HtmlElement domElement = textEditing.editingElement.domElement; + + checkInputEditingState(domElement, 'abcd', 2, 3); + + // Check if the position is correct. + expect( + domElement.getBoundingClientRect(), + Rectangle.fromPoints(const Point(10.0, 20.0), + const Point(160.0, 70.0)), + ); + expect( + domElement.style.transform, + 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)', + ); + expect( + textEditing.editingElement.domElement.style.font, + '500 12px sans-serif', + ); + + const MethodCall clearClient = MethodCall('TextInput.clearClient'); + textEditing.handleTextInput(codec.encodeMethodCall(clearClient)); + }, + ); + test('input font set succesfully with null fontWeightIndex', () { final MethodCall setClient = MethodCall( 'TextInput.setClient', [123, flutterSinglelineConfig]); From f6a3eb9d1938a457f5a4a284949feeae070dae8d Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 21 Nov 2019 12:37:07 -0800 Subject: [PATCH 214/591] [web] Allow users to enable canvas text measurement (#13929) --- lib/web_ui/lib/src/engine/text/measurement.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index 4a067d06f7a8c..a6beff85acc32 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -184,7 +184,7 @@ abstract class TextMeasurementService { /// /// This is only used for testing at the moment. Once the implementation is /// complete and production-ready, we'll get rid of this flag. - static bool enableExperimentalCanvasImplementation = false; + static bool enableExperimentalCanvasImplementation = const bool.fromEnvironment('FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT', defaultValue: false); /// Gets the appropriate [TextMeasurementService] instance for the given /// [paragraph]. From 7e68097cc1d35da8ced4b771d191b6d44c5d9fef Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 21 Nov 2019 13:04:51 -0800 Subject: [PATCH 215/591] [web] Fix line-height for Firefox (#13960) --- lib/web_ui/lib/src/engine/dom_renderer.dart | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 9c91b5c8098a0..4152e5e5752a2 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -220,7 +220,8 @@ class DomRenderer { _styleElement = html.StyleElement(); html.document.head.append(_styleElement); final html.CssStyleSheet sheet = _styleElement.sheet; - + final bool isWebKit = browserEngine == BrowserEngine.webkit; + final bool isFirefox = browserEngine == BrowserEngine.firefox; // TODO(butterfly): use more efficient CSS selectors; descendant selectors // are slow. More info: // @@ -228,10 +229,19 @@ class DomRenderer { // This undoes browser's default layout attributes for paragraphs. We // compute paragraph layout ourselves. - sheet.insertRule(''' -flt-ruler-host p, flt-scene p { - margin: 0; -}''', sheet.cssRules.length); + if (isFirefox) { + // For firefox set line-height, otherwise textx at same font-size will + // measure differently in ruler. + sheet.insertRule( + 'flt-ruler-host p, flt-scene p ' + '{ margin: 0; line-height: 100%;}', + sheet.cssRules.length); + } else { + sheet.insertRule( + 'flt-ruler-host p, flt-scene p ' + '{ margin: 0; }', + sheet.cssRules.length); + } // This undoes browser's default painting and layout attributes of range // input, which is used in semantics. @@ -248,7 +258,7 @@ flt-semantics input[type=range] { left: 0; }''', sheet.cssRules.length); - if (browserEngine == BrowserEngine.webkit) { + if (isWebKit) { sheet.insertRule( 'flt-semantics input[type=range]::-webkit-slider-thumb {' ' -webkit-appearance: none;' @@ -256,7 +266,7 @@ flt-semantics input[type=range] { sheet.cssRules.length); } - if (browserEngine == BrowserEngine.firefox) { + if (isFirefox) { sheet.insertRule( 'input::-moz-selection {' ' background-color: transparent;' @@ -292,7 +302,7 @@ flt-semantics [contentEditable="true"] { // By default on iOS, Safari would highlight the element that's being tapped // on using gray background. This CSS rule disables that. - if (browserEngine == BrowserEngine.webkit) { + if (isWebKit) { sheet.insertRule(''' flt-glass-pane * { -webkit-tap-highlight-color: transparent; @@ -393,8 +403,7 @@ flt-glass-pane * { // is 1.0. window.debugOverrideDevicePixelRatio(1.0); - if (html.window.visualViewport == null && - browserEngine == BrowserEngine.webkit) { + if (html.window.visualViewport == null && isWebKit) { // Safari sometimes gives us bogus innerWidth/innerHeight values when the // page loads. When it changes the values to correct ones it does not // notify of the change via `onResize`. As a workaround, we setup a From c82556bae4be17e46958b7c96d1f1c6a8c99fc4f Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 21 Nov 2019 13:11:52 -0800 Subject: [PATCH 216/591] Roll src/third_party/dart 521b168441..6fa41ae6f9 (1 commits) (#13961) dart-lang/sdk@6fa41ae6f9 Migration: remove _notNull* fields from EdgeBuilder. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index aec146de6d206..350b9c18c76b5 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '521b168441143861d928c9315c9830469617028b', + 'dart_revision': '6fa41ae6f955db8dbf57a9373b7bca44d59f7a87', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From e307b05b7dcb4c4f7985bb2150be10760c165fb7 Mon Sep 17 00:00:00 2001 From: Craig Stout Date: Tue, 19 Nov 2019 12:12:55 -0800 Subject: [PATCH 217/591] [shell][fuchsia] Migrate away from deprecated async loop configs --- shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc index a6fdee43bb5ca..5f00940e475b3 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc +++ b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -53,7 +54,7 @@ TEST_F(VsyncWaiterTest, AwaitVsync) { thread.reset(new flutter_runner::Thread()); } - async::Loop loop(&kAsyncLoopConfigAttachToThread); + async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); const flutter::TaskRunners task_runners( "VsyncWaiterTests", // Dart thread labels From 0185c41e03b638765e7613e43e1936525e6cbfaf Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 21 Nov 2019 14:44:31 -0800 Subject: [PATCH 218/591] Remove the strace debug logging from the Fuchsia gen_package script (#13963) --- tools/fuchsia/gen_package.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tools/fuchsia/gen_package.py b/tools/fuchsia/gen_package.py index c1c8a61848bc0..9dffceb0b79b0 100755 --- a/tools/fuchsia/gen_package.py +++ b/tools/fuchsia/gen_package.py @@ -85,13 +85,7 @@ def main(): else: manifest_file = GenerateManifest(args.package_dir) - strace_out = os.path.join(output_dir, 'strace_out') - pm_command_base = [ - 'strace', - '-f', - '-o', - strace_out, args.pm_bin, '-o', output_dir, @@ -109,9 +103,7 @@ def main(): ['archive', '--output='+ os.path.join(os.path.dirname(output_dir), args.far_name + "-0")], ] for pm_command in pm_commands: - pm_command_args = pm_command_base + pm_command - sys.stderr.write("===== Running %s\n" % pm_command_args) - subprocess.check_output(pm_command_args) + subprocess.check_output(pm_command_base + pm_command) except subprocess.CalledProcessError as e: print('==================== Manifest contents =========================================') with open(manifest_file, 'r') as manifest: @@ -123,10 +115,6 @@ def main(): with open(meta_contents_path, 'r') as meta_contents: sys.stdout.write(meta_contents.read()) print('==================== End meta/contents =========================================') - print('==================== Strace output =============================================') - with open(strace_out, 'r') as strace: - sys.stdout.write(strace.read()) - print('==================== End strace output =========================================') raise return 0 From 7c5a954dd351e1a782f222a9f821db65b66176c2 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 21 Nov 2019 15:00:27 -0800 Subject: [PATCH 219/591] Removed GET_ACTIVITIES flag from all manifest meta-data lookups. (#38891) (#13932) --- .../io/flutter/embedding/android/FlutterActivity.java | 6 +++--- .../flutter/embedding/android/FlutterFragmentActivity.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 47dfe7abd169f..671103f8d3f03 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -491,7 +491,7 @@ private Drawable getSplashScreenFromManifest() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo( getComponentName(), - PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + PackageManager.GET_META_DATA ); Bundle metadata = activityInfo.metaData; int splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : 0; @@ -716,7 +716,7 @@ public String getDartEntrypointFunctionName() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo( getComponentName(), - PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + PackageManager.GET_META_DATA ); Bundle metadata = activityInfo.metaData; String desiredDartEntrypoint = metadata != null ? metadata.getString(DART_ENTRYPOINT_META_DATA_KEY) : null; @@ -754,7 +754,7 @@ public String getInitialRoute() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo( getComponentName(), - PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + PackageManager.GET_META_DATA ); Bundle metadata = activityInfo.metaData; String desiredInitialRoute = metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null; diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 7549a41844ce6..3e275d974b0e7 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -328,7 +328,7 @@ private Drawable getSplashScreenFromManifest() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo( getComponentName(), - PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + PackageManager.GET_META_DATA ); Bundle metadata = activityInfo.metaData; Integer splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : null; @@ -609,7 +609,7 @@ public String getDartEntrypointFunctionName() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo( getComponentName(), - PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + PackageManager.GET_META_DATA ); Bundle metadata = activityInfo.metaData; String desiredDartEntrypoint = metadata != null ? metadata.getString(DART_ENTRYPOINT_META_DATA_KEY) : null; @@ -647,7 +647,7 @@ protected String getInitialRoute() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo( getComponentName(), - PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + PackageManager.GET_META_DATA ); Bundle metadata = activityInfo.metaData; String desiredInitialRoute = metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null; From 654a30e1a35dd59c4e07028920a8ce478e1f8bae Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 21 Nov 2019 19:21:44 -0500 Subject: [PATCH 220/591] Roll src/third_party/skia 4e0255508749..0788481c63dc (24 commits) (#13968) https://skia.googlesource.com/skia.git/+log/4e0255508749..0788481c63dc git log 4e0255508749..0788481c63dc --date=short --no-merges --format='%ad %ae %s' 2019-11-21 benjaminwagner@google.com Remove Build-Ubuntu18-GCC job 2019-11-21 benjaminwagner@google.com [infra] Add service_account_json option to asset utils 2019-11-21 rosasco@google.com Slim down .far by ~45 MB. 2019-11-21 reed@google.com Revert "Use flat version of path-direction enum" 2019-11-21 halcanary@google.com [reland] public.bzl: update to replace Gif with Wuffs 2019-11-21 mtklein@google.com all-constant peepholes 2019-11-21 reed@google.com Use flat version of path-direction enum 2019-11-21 kjlubick@google.com [canvaskit] Try some things to reduce GPU test flakiness 2019-11-21 mtklein@google.com build with Clang for Valgrind bots (take 2) 2019-11-21 mtklein@google.com add some missing bitfield initializers 2019-11-21 ethannicholas@google.com Reland "Fixed some GrSkSLFP limitations." 2019-11-21 michaelludwig@google.com Normalize src coords and domain up front instead of at tessellation time 2019-11-21 robertphillips@google.com Add onPrePrepare to GrFillRRectOp 2019-11-21 herb@google.com Simplify makeOps API 2019-11-21 mtklein@google.com one more stray Valgrind false positive 2019-11-21 halcanary@google.com [minor] mark scripts as executable 2019-11-21 halcanary@google.com Replace third_party/gif with new DEPS entry. 2019-11-21 mtklein@google.com expand float peepholes 2019-11-21 halcanary@google.com Replace third_party/gif: Preflight changes 2019-11-21 ethannicholas@google.com Revert "Fixed some GrSkSLFP limitations." 2019-11-21 egdaniel@google.com Update failure calls in GrVkMemory and shader creation. 2019-11-21 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 99bd10b70422..b8e748be6b94 (17 commits) 2019-11-21 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 0db96f99f73d..137e8082047a (617 commits) 2019-11-21 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 7e11f46ddf8e..f2c9ce1e08ed (9 commits) Created with: gclient setdep -r src/third_party/skia@0788481c63dc If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 38 ++------------------------------ sky/packages/sky_engine/LICENSE | 25 --------------------- 3 files changed, 3 insertions(+), 62 deletions(-) diff --git a/DEPS b/DEPS index 350b9c18c76b5..d2ade0f3c07bd 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4e0255508749ce2d595dfdf68b3ba6bcaa672b7e', + 'skia_revision': '0788481c63dc4c06fdeb0976cc5568ef85d06af0', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 7c57aab979e9d..fed59d401e297 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: a15ff6749fc2a8cff5e4d7e15f8d5dfb +Signature: c8ea729af1431890557e325f934d9a17 UNUSED LICENSES: @@ -1421,7 +1421,6 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-Metal.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Release-MoltenVK_Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Ubuntu18-GCC-x86_64-Release-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json @@ -1658,7 +1657,6 @@ FILE: ../../../third_party/skia/infra/cts/whitelist_devices.json FILE: ../../../third_party/skia/infra/gcc/Debian10-mips64el/Dockerfile FILE: ../../../third_party/skia/infra/gcc/Debian10-x86/Dockerfile FILE: ../../../third_party/skia/infra/gcc/Debian10/Dockerfile -FILE: ../../../third_party/skia/infra/gcc/Ubuntu18/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/gold-lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/project-config/cr-buildbucket.cfg @@ -2759,8 +2757,6 @@ FILE: ../../../third_party/skia/src/codec/SkBmpStandardCodec.h FILE: ../../../third_party/skia/src/codec/SkCodec.cpp FILE: ../../../third_party/skia/src/codec/SkCodecImageGenerator.cpp FILE: ../../../third_party/skia/src/codec/SkCodecImageGenerator.h -FILE: ../../../third_party/skia/src/codec/SkGifCodec.cpp -FILE: ../../../third_party/skia/src/codec/SkGifCodec.h FILE: ../../../third_party/skia/src/codec/SkIcoCodec.cpp FILE: ../../../third_party/skia/src/codec/SkIcoCodec.h FILE: ../../../third_party/skia/src/codec/SkJpegCodec.cpp @@ -6084,36 +6080,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: skia -ORIGIN: ../../../third_party/skia/src/codec/SkGifCodec.cpp -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/src/codec/SkGifCodec.cpp ----------------------------------------------------------------------------------------------------- -Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - ==================================================================================================== LIBRARY: skia ORIGIN: ../../../third_party/skia/src/core/SkBitmapProcState_matrixProcs.cpp + ../../../third_party/skia/LICENSE @@ -6602,4 +6568,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -Total license count: 52 +Total license count: 51 diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index eb1e9774ea029..6cb160d4868d9 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -11160,31 +11160,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------- -skia - Copyright (C) 2014 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without From 44556321e8ea1738a56a77f21c5cc2cc41a35092 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 21 Nov 2019 16:22:28 -0800 Subject: [PATCH 221/591] Roll src/third_party/dart 6fa41ae6f9..09d22a0509 (14 commits) (#13967) dart-lang/sdk@09d22a0509 [vm] Wrap late local var initializers in a closure. dart-lang/sdk@f6cefb35c0 [vm/gardening] Clean up no longer used ClassAndSize class. dart-lang/sdk@4456d01a35 [dartfuzz] Fix ffi check in getMethods dart-lang/sdk@e5ec9e2f6a Migration: improve quality of debug output when finish is called. dart-lang/sdk@1fb118f0e7 Migration: do not call FlowAnalysis.finish from inside a finally block dart-lang/sdk@655ddc90b4 Print a URL that can be used to open the preview tool when a port is specified dart-lang/sdk@6444fba9c7 Implement upper bound computation with NNBD rules. dart-lang/sdk@6dc3762b99 Stop producing a diagnostic for an error that was removed from the spec (issue 39476) dart-lang/sdk@821b3c44ff Add evaluateConstant() to LinterContext. dart-lang/sdk@0b9665fc8b [cfe] Use library's non-nullable modifier when creating new types dart-lang/sdk@3ed0ce2419 [cfe] Reuse nullability modifier when creating a copy of a type dart-lang/sdk@6808c563aa Adjusted front end to allow non-function type aliases dart-lang/sdk@b370346d60 Add 'isNonNullableByDefault' flag to TypeSystemImpl, make all parameters named required. dart-lang/sdk@ad021d02d4 Integrate the latest batch of reviewed diagnostic docs --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d2ade0f3c07bd..6fbb46e775e98 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '6fa41ae6f955db8dbf57a9373b7bca44d59f7a87', + 'dart_revision': '09d22a0509b9bccdcf7dc1e00d1c577ec7eaa79f', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index e9f5242feacd5..86ae03a9ef294 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f7334fbbdc2435fe4814f8135afa73d6 +Signature: d84c3491eae16403a85e0414b4e22c71 UNUSED LICENSES: From af2c53ab135f0e13e3fa9b9339845be335076a02 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Thu, 21 Nov 2019 19:40:06 -0800 Subject: [PATCH 222/591] [fuchsia] Ensure we do not initialize nan RoundedRectangles (#13971) Also initializes physical shape layer vars to empty. --- flow/scene_update_context.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 14190b5b5d631..1c85bb658aacb 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -71,6 +71,13 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, if (rrect.isEmpty()) return; + // isEmpty should account for this, but we are adding these experimental + // checks to validate if this is the root cause for b/144933519. + if (std::isnan(rrect.width()) || std::isnan(rrect.height())) { + FML_LOG(ERROR) << "Invalid RoundedRectangle"; + return; + } + // Add a part which represents the frame's geometry for clipping purposes // and possibly for its texture. // TODO(SCN-137): Need to be able to express the radii as vectors. From 2031bddd9a385e65082baa0214211af49335a2c0 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 21 Nov 2019 20:26:25 -0800 Subject: [PATCH 223/591] Roll src/third_party/dart 09d22a0509..77ac3d0a5c (14 commits) (#13970) dart-lang/sdk@77ac3d0a5c [vm/aot/tfa] Introduce TFA-specific representation of classes (refactoring) dart-lang/sdk@3931d43d42 Issue 39407. Test for ListLiteral inference in presence of null(s). dart-lang/sdk@28d272b3dd Move more types access / creation helpers into ElementsTypesMixin. dart-lang/sdk@aff0915ead Fix BazelWorkspaces in which multiple 'bin' directories exist, e.g. on distributed build systems dart-lang/sdk@fee8c5503c Mark the completion.registerLibraryPaths as deprecated in the Dart Analysis Server. dart-lang/sdk@970c164826 [cfe] Use correct types in transformations and start verifying getStaticType dart-lang/sdk@f979bd9899 Merge RestrictedAnalysisContext into AnalysisContextImpl. dart-lang/sdk@f3455278ff Use 'isNonNullableByDefault' flag in TypeProviderImpl. dart-lang/sdk@dcaf959589 Fix several unused elements, and unnecessary casts. dart-lang/sdk@6646412705 Rewrite DEFERRED_IMPORT_OF_EXTENSION check to using ImportElement namespace. dart-lang/sdk@b7de866bee [vm] Avoid holding canonical hashes at rest. dart-lang/sdk@03676ad489 Remove AnalysisEngine.createAnalysisContext() dart-lang/sdk@535cbf5db9 NNBD preview: Text for return type with nullable return type in subclass dart-lang/sdk@af541cd0dd [VM/nnbd] Ignore nullability of type Never provided by CFE. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6fbb46e775e98..762b0cc12a484 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '09d22a0509b9bccdcf7dc1e00d1c577ec7eaa79f', + 'dart_revision': '77ac3d0a5cadcecb00a68b0eb0001c9ef8fc5add', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 86ae03a9ef294..99316c5772b9c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: d84c3491eae16403a85e0414b4e22c71 +Signature: fe7ad83013a4b930b12500b844661f78 UNUSED LICENSES: From d47e35cb37e2ed6010c6d8f0fcefbb659d5bb371 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 21 Nov 2019 23:51:23 -0500 Subject: [PATCH 224/591] Roll src/third_party/skia 0788481c63dc..7a538b1eeccd (3 commits) (#13973) https://skia.googlesource.com/skia.git/+log/0788481c63dc..7a538b1eeccd git log 0788481c63dc..7a538b1eeccd --date=short --no-merges --format='%ad %ae %s' 2019-11-22 bsalomon@google.com Make FP optimizations helpers use SkAlphaType not GrColorType 2019-11-22 reed@google.com Revert "Revert "Use flat version of path-direction enum"" 2019-11-21 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@7a538b1eeccd If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 762b0cc12a484..8d9238ec991af 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '0788481c63dc4c06fdeb0976cc5568ef85d06af0', + 'skia_revision': '7a538b1eeccdbe940de507af9aeaf454952c4aec', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index fed59d401e297..a56a906d17af8 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c8ea729af1431890557e325f934d9a17 +Signature: 381c27cc50b515f1a409143c19c51f31 UNUSED LICENSES: From 9f4341a2b869ade738bdce64d13075d4fe4a5677 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 21 Nov 2019 22:13:48 -0800 Subject: [PATCH 225/591] Roll src/third_party/dart 77ac3d0a5c..029e06b8d9 (4 commits) (#13974) dart-lang/sdk@029e06b8d9 [analyzer] Allow downwards inference and upper bound heuristic wrt variance. dart-lang/sdk@74a57acfbe [dartdevc] Migrating dart:_foreign_helper to NNBD. dart-lang/sdk@361d96227b Remove support for negative tests from the test runner. dart-lang/sdk@ec81b3edf7 Revert "[vm] Avoid holding canonical hashes at rest." --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 8d9238ec991af..2e3eb0ba9c2b5 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '77ac3d0a5cadcecb00a68b0eb0001c9ef8fc5add', + 'dart_revision': '029e06b8d9eb95f836cd59de6b8ac7fd7cd37934', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 99316c5772b9c..72a278b0b5e58 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: fe7ad83013a4b930b12500b844661f78 +Signature: 0e0f83fa7087cb4e19471dbd2dd2a197 UNUSED LICENSES: From 9f89ef65cdf0c3eb665d3dff3ba44fce208b2e38 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 22 Nov 2019 03:20:09 -0500 Subject: [PATCH 226/591] Roll fuchsia/sdk/core/linux-amd64 from zgJ75... to mvesW... (#13977) Roll fuchsia/sdk/core/linux-amd64 from zgJ75... to mvesW... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2e3eb0ba9c2b5..d23b462be6b9d 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'zgJ75e5QUIdgVhPAeEnDsHJJLIY_MPiaQajh8EM6Gu4C' + 'version': 'mvesWmuGSgtmxJcGYf-5KsasSS56pnl2SEro4s3OmAsC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 02f9f17e8bc9c..8bdc583f91899 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 55184f5bf77dcda79698058bba784747 +Signature: e8cda6b61a13398cffde70b5d65cfd92 UNUSED LICENSES: From ad14574949f5cd0b5eddfd7bc4fd54bb56e00c72 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 22 Nov 2019 11:13:54 -0800 Subject: [PATCH 227/591] Updated googletest to fix fuchsia build. (#13980) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d23b462be6b9d..39fa812fe76a5 100644 --- a/DEPS +++ b/DEPS @@ -152,7 +152,7 @@ deps = { Var('fuchsia_git') + '/third_party/benchmark' + '@' + 'a779ffce872b4c811beef482e18bd0b63626aa42', 'src/third_party/googletest': - Var('fuchsia_git') + '/third_party/googletest' + '@' + 'd8827ca8e397b725a3039b19cc116e309c47815e', + Var('fuchsia_git') + '/third_party/googletest' + '@' + '3fef9015bfe7617d57806cd7c73a653b28559fae', 'src/third_party/rapidjson': Var('fuchsia_git') + '/third_party/rapidjson' + '@' + '32d07c55db1bb6c2ae17cba4033491a667647753', From 11580eb28852a91b2e056f16c5e72ca3ba31ab20 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 22 Nov 2019 15:18:28 -0500 Subject: [PATCH 228/591] Roll src/third_party/skia 7a538b1eeccd..078e8faa26d8 (1 commits) (#13978) https://skia.googlesource.com/skia.git/+log/7a538b1eeccd..078e8faa26d8 git log 7a538b1eeccd..078e8faa26d8 --date=short --no-merges --format='%ad %ae %s' 2019-11-22 bsalomon@google.com Revert "Make FP optimizations helpers use SkAlphaType not GrColorType" Created with: gclient setdep -r src/third_party/skia@078e8faa26d8 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 39fa812fe76a5..641e14c85ced7 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '7a538b1eeccdbe940de507af9aeaf454952c4aec', + 'skia_revision': '078e8faa26d8b1f33a92e57f587be011150d1776', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a56a906d17af8..435eee8cbf4b6 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 381c27cc50b515f1a409143c19c51f31 +Signature: 68d412de34ec10138518fbcb15922cc7 UNUSED LICENSES: From 89e395853c6bcdd3e2727eb086a717938f8fa51c Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 22 Nov 2019 12:20:02 -0800 Subject: [PATCH 229/591] Refactor to passing functions by const ref (#13975) Moved our code to passing functions by const ref --- flow/raster_cache.cc | 3 +- flow/scene_update_context.h | 2 +- flow/view_holder.cc | 8 ++--- flow/view_holder.h | 4 +-- fml/closure.h | 2 +- fml/concurrent_message_loop.cc | 4 +-- fml/concurrent_message_loop.h | 4 +-- fml/delayed_task.cc | 4 +-- fml/delayed_task.h | 4 ++- fml/file.cc | 2 +- fml/file.h | 7 ++-- fml/mapping.cc | 2 +- fml/mapping.h | 2 +- fml/message_loop.cc | 2 +- fml/message_loop.h | 2 +- fml/message_loop_impl.cc | 6 ++-- fml/message_loop_impl.h | 4 +-- fml/message_loop_task_queues.cc | 6 ++-- fml/message_loop_task_queues.h | 4 +-- fml/platform/posix/file_posix.cc | 2 +- fml/platform/win/file_win.cc | 2 +- fml/task_runner.cc | 15 +++++---- fml/task_runner.h | 9 ++--- lib/ui/painting/image_decoder.cc | 3 +- lib/ui/painting/image_decoder.h | 2 +- runtime/dart_isolate.cc | 25 +++++++------- runtime/dart_isolate.h | 18 +++++----- runtime/dart_service_isolate.cc | 2 +- runtime/dart_service_isolate.h | 2 +- runtime/runtime_controller.cc | 12 +++---- runtime/runtime_controller.h | 12 +++---- runtime/skia_concurrent_executor.cc | 2 +- runtime/skia_concurrent_executor.h | 2 +- shell/common/animator.cc | 4 +-- shell/common/animator.h | 2 +- shell/common/engine.cc | 4 +-- shell/common/engine.h | 2 +- shell/common/pipeline.h | 5 +-- shell/common/platform_view.cc | 4 +-- shell/common/platform_view.h | 5 +-- shell/common/pointer_data_dispatcher.h | 3 +- shell/common/rasterizer.cc | 2 +- shell/common/rasterizer.h | 2 +- shell/common/shell.cc | 33 ++++++++++--------- shell/common/shell.h | 16 ++++----- shell/common/surface.cc | 2 +- shell/common/surface.h | 2 +- shell/common/vsync_waiter.cc | 4 +-- shell/common/vsync_waiter.h | 4 +-- .../include/flutter/basic_message_channel.h | 4 +-- .../common/cpp/incoming_message_dispatcher.cc | 5 +-- .../common/cpp/incoming_message_dispatcher.h | 4 +-- shell/platform/embedder/embedder_engine.cc | 2 +- shell/platform/embedder/embedder_engine.h | 2 +- .../embedder/embedder_external_texture_gl.cc | 2 +- .../embedder/embedder_external_texture_gl.h | 2 +- .../embedder_external_view_embedder.cc | 4 +-- .../embedder_external_view_embedder.h | 4 +-- shell/platform/embedder/embedder_layers.cc | 4 ++- shell/platform/embedder/embedder_layers.h | 2 +- .../embedder_platform_message_response.cc | 2 +- .../embedder_platform_message_response.h | 2 +- .../platform/embedder/embedder_task_runner.cc | 6 ++-- .../platform/embedder/embedder_task_runner.h | 7 ++-- .../embedder/tests/embedder_config_builder.cc | 2 +- .../embedder/tests/embedder_config_builder.h | 2 +- .../tests/embedder_test_compositor.cc | 6 ++-- .../embedder/tests/embedder_test_compositor.h | 7 ++-- .../embedder/tests/embedder_test_context.cc | 11 ++++--- .../embedder/tests/embedder_test_context.h | 11 ++++--- .../embedder/vsync_waiter_embedder.cc | 2 +- .../platform/embedder/vsync_waiter_embedder.h | 2 +- .../fuchsia/flutter/platform_view_unittest.cc | 2 +- .../fuchsia/flutter/task_runner_adapter.cc | 14 ++++---- .../fuchsia/flutter/vulkan_surface.cc | 2 +- .../platform/fuchsia/flutter/vulkan_surface.h | 2 +- shell/platform/glfw/glfw_event_loop.cc | 2 +- shell/platform/glfw/glfw_event_loop.h | 2 +- shell/platform/windows/win32_task_runner.cc | 2 +- shell/platform/windows/win32_task_runner.h | 3 +- vulkan/vulkan_handle.h | 2 +- 81 files changed, 213 insertions(+), 187 deletions(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 7854dac3fbfe9..f9388ad98d4b8 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -92,13 +92,14 @@ static bool IsPictureWorthRasterizing(SkPicture* picture, return picture->approximateOpCount() > 5; } +/// @note Procedure doesn't copy all closures. static RasterCacheResult Rasterize( GrContext* context, const SkMatrix& ctm, SkColorSpace* dst_color_space, bool checkerboard, const SkRect& logical_rect, - std::function draw_function) { + const std::function& draw_function) { TRACE_EVENT0("flutter", "RasterCachePopulate"); SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index d4a46055967f3..13d7f666f4107 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -37,7 +37,7 @@ class SceneUpdateContext { virtual SkISize GetSize() const = 0; virtual void SignalWritesFinished( - std::function on_writes_committed) = 0; + const std::function& on_writes_committed) = 0; virtual scenic::Image* GetImage() = 0; diff --git a/flow/view_holder.cc b/flow/view_holder.cc index 688f56d0987a7..b49ca430da40d 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -51,7 +51,7 @@ namespace flutter { void ViewHolder::Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - BindCallback on_bind_callback) { + const BindCallback& on_bind_callback) { // This GPU thread contains at least 1 ViewHolder. Initialize the per-thread // bindings. if (tls_view_holder_bindings.get() == nullptr) { @@ -64,7 +64,7 @@ void ViewHolder::Create(zx_koid_t id, auto view_holder = std::make_unique(std::move(ui_task_runner), std::move(view_holder_token), - std::move(on_bind_callback)); + on_bind_callback); bindings->emplace(id, std::move(view_holder)); } @@ -91,10 +91,10 @@ ViewHolder* ViewHolder::FromId(zx_koid_t id) { ViewHolder::ViewHolder(fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - BindCallback on_bind_callback) + const BindCallback& on_bind_callback) : ui_task_runner_(std::move(ui_task_runner)), pending_view_holder_token_(std::move(view_holder_token)), - pending_bind_callback_(std::move(on_bind_callback)) { + pending_bind_callback_(on_bind_callback) { FML_DCHECK(ui_task_runner_); FML_DCHECK(pending_view_holder_token_.value); } diff --git a/flow/view_holder.h b/flow/view_holder.h index 9466f644aee23..8b49e216c2800 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -34,13 +34,13 @@ class ViewHolder { static void Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - BindCallback on_bind_callback); + const BindCallback& on_bind_callback); static void Destroy(zx_koid_t id); static ViewHolder* FromId(zx_koid_t id); ViewHolder(fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, - BindCallback on_bind_callback); + const BindCallback& on_bind_callback); ~ViewHolder() = default; // Sets the properties/opacity of the child view by issuing a Scenic command. diff --git a/fml/closure.h b/fml/closure.h index c67e4d4554f95..9a8598cdc5273 100644 --- a/fml/closure.h +++ b/fml/closure.h @@ -31,7 +31,7 @@ using closure = std::function; /// class ScopedCleanupClosure { public: - ScopedCleanupClosure(fml::closure closure) : closure_(closure) {} + ScopedCleanupClosure(const fml::closure& closure) : closure_(closure) {} ~ScopedCleanupClosure() { if (closure_) { diff --git a/fml/concurrent_message_loop.cc b/fml/concurrent_message_loop.cc index 482ceb479018d..a116356632856 100644 --- a/fml/concurrent_message_loop.cc +++ b/fml/concurrent_message_loop.cc @@ -43,7 +43,7 @@ std::shared_ptr ConcurrentMessageLoop::GetTaskRunner() { return std::make_shared(weak_from_this()); } -void ConcurrentMessageLoop::PostTask(fml::closure task) { +void ConcurrentMessageLoop::PostTask(const fml::closure& task) { if (!task) { return; } @@ -107,7 +107,7 @@ ConcurrentTaskRunner::ConcurrentTaskRunner( ConcurrentTaskRunner::~ConcurrentTaskRunner() = default; -void ConcurrentTaskRunner::PostTask(fml::closure task) { +void ConcurrentTaskRunner::PostTask(const fml::closure& task) { if (!task) { return; } diff --git a/fml/concurrent_message_loop.h b/fml/concurrent_message_loop.h index 1642c58d3632d..a3487b6c119ee 100644 --- a/fml/concurrent_message_loop.h +++ b/fml/concurrent_message_loop.h @@ -44,7 +44,7 @@ class ConcurrentMessageLoop void WorkerMain(); - void PostTask(fml::closure task); + void PostTask(const fml::closure& task); FML_DISALLOW_COPY_AND_ASSIGN(ConcurrentMessageLoop); }; @@ -55,7 +55,7 @@ class ConcurrentTaskRunner { ~ConcurrentTaskRunner(); - void PostTask(fml::closure task); + void PostTask(const fml::closure& task); private: friend ConcurrentMessageLoop; diff --git a/fml/delayed_task.cc b/fml/delayed_task.cc index 569c1d1798fb0..997176d7694e0 100644 --- a/fml/delayed_task.cc +++ b/fml/delayed_task.cc @@ -9,9 +9,9 @@ namespace fml { DelayedTask::DelayedTask(size_t order, - fml::closure task, + const fml::closure& task, fml::TimePoint target_time) - : order_(order), task_(std::move(task)), target_time_(target_time) {} + : order_(order), task_(task), target_time_(target_time) {} DelayedTask::DelayedTask(const DelayedTask& other) = default; diff --git a/fml/delayed_task.h b/fml/delayed_task.h index 1830fbb6ef0d4..20594c3e8a494 100644 --- a/fml/delayed_task.h +++ b/fml/delayed_task.h @@ -14,7 +14,9 @@ namespace fml { class DelayedTask { public: - DelayedTask(size_t order, fml::closure task, fml::TimePoint target_time); + DelayedTask(size_t order, + const fml::closure& task, + fml::TimePoint target_time); DelayedTask(const DelayedTask& other); diff --git a/fml/file.cc b/fml/file.cc index 2f62e6c6f884b..dc31a8cfd86d4 100644 --- a/fml/file.cc +++ b/fml/file.cc @@ -62,7 +62,7 @@ ScopedTemporaryDirectory::~ScopedTemporaryDirectory() { } bool VisitFilesRecursively(const fml::UniqueFD& directory, - FileVisitor visitor) { + const FileVisitor& visitor) { FileVisitor recursive_visitor = [&recursive_visitor, &visitor]( const UniqueFD& directory, const std::string& filename) { diff --git a/fml/file.h b/fml/file.h index ee54354520695..bc82622036f16 100644 --- a/fml/file.h +++ b/fml/file.h @@ -107,7 +107,8 @@ using FileVisitor = std::function; NonOwnedMapping(const uint8_t* data, size_t size, - ReleaseProc release_proc = nullptr); + const ReleaseProc& release_proc = nullptr); ~NonOwnedMapping() override; diff --git a/fml/message_loop.cc b/fml/message_loop.cc index 635dfbf866873..c1043e664ae0a 100644 --- a/fml/message_loop.cc +++ b/fml/message_loop.cc @@ -61,7 +61,7 @@ fml::RefPtr MessageLoop::GetLoopImpl() const { return loop_; } -void MessageLoop::AddTaskObserver(intptr_t key, fml::closure callback) { +void MessageLoop::AddTaskObserver(intptr_t key, const fml::closure& callback) { loop_->AddTaskObserver(key, callback); } diff --git a/fml/message_loop.h b/fml/message_loop.h index 676bb429cd4c5..025f8059ec1aa 100644 --- a/fml/message_loop.h +++ b/fml/message_loop.h @@ -24,7 +24,7 @@ class MessageLoop { void Terminate(); - void AddTaskObserver(intptr_t key, fml::closure callback); + void AddTaskObserver(intptr_t key, const fml::closure& callback); void RemoveTaskObserver(intptr_t key); diff --git a/fml/message_loop_impl.cc b/fml/message_loop_impl.cc index 8a50c6697c4d6..9e07e11278810 100644 --- a/fml/message_loop_impl.cc +++ b/fml/message_loop_impl.cc @@ -50,7 +50,8 @@ MessageLoopImpl::~MessageLoopImpl() { task_queue_->Dispose(queue_id_); } -void MessageLoopImpl::PostTask(fml::closure task, fml::TimePoint target_time) { +void MessageLoopImpl::PostTask(const fml::closure& task, + fml::TimePoint target_time) { FML_DCHECK(task != nullptr); FML_DCHECK(task != nullptr); if (terminated_) { @@ -61,7 +62,8 @@ void MessageLoopImpl::PostTask(fml::closure task, fml::TimePoint target_time) { task_queue_->RegisterTask(queue_id_, task, target_time); } -void MessageLoopImpl::AddTaskObserver(intptr_t key, fml::closure callback) { +void MessageLoopImpl::AddTaskObserver(intptr_t key, + const fml::closure& callback) { FML_DCHECK(callback != nullptr); FML_DCHECK(MessageLoop::GetCurrent().GetLoopImpl().get() == this) << "Message loop task observer must be added on the same thread as the " diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h index ef98bf5fa4704..055423fad8a85 100644 --- a/fml/message_loop_impl.h +++ b/fml/message_loop_impl.h @@ -34,9 +34,9 @@ class MessageLoopImpl : public Wakeable, virtual void Terminate() = 0; - void PostTask(fml::closure task, fml::TimePoint target_time); + void PostTask(const fml::closure& task, fml::TimePoint target_time); - void AddTaskObserver(intptr_t key, fml::closure callback); + void AddTaskObserver(intptr_t key, const fml::closure& callback); void RemoveTaskObserver(intptr_t key); diff --git a/fml/message_loop_task_queues.cc b/fml/message_loop_task_queues.cc index 5b2c5de20ee5b..0efc33e0414ad 100644 --- a/fml/message_loop_task_queues.cc +++ b/fml/message_loop_task_queues.cc @@ -75,13 +75,13 @@ void MessageLoopTaskQueues::DisposeTasks(TaskQueueId queue_id) { } void MessageLoopTaskQueues::RegisterTask(TaskQueueId queue_id, - fml::closure task, + const fml::closure& task, fml::TimePoint target_time) { std::scoped_lock queue_lock(GetMutex(queue_id)); size_t order = order_++; const auto& queue_entry = queue_entries_[queue_id]; - queue_entry->delayed_tasks.push({order, std::move(task), target_time}); + queue_entry->delayed_tasks.push({order, task, target_time}); TaskQueueId loop_to_wake = queue_id; if (queue_entry->subsumed_by != _kUnmerged) { loop_to_wake = queue_entry->subsumed_by; @@ -157,7 +157,7 @@ size_t MessageLoopTaskQueues::GetNumPendingTasks(TaskQueueId queue_id) const { void MessageLoopTaskQueues::AddTaskObserver(TaskQueueId queue_id, intptr_t key, - fml::closure callback) { + const fml::closure& callback) { std::scoped_lock queue_lock(GetMutex(queue_id)); FML_DCHECK(callback != nullptr) << "Observer callback must be non-null."; diff --git a/fml/message_loop_task_queues.h b/fml/message_loop_task_queues.h index 249e09e685785..67c46991a6f13 100644 --- a/fml/message_loop_task_queues.h +++ b/fml/message_loop_task_queues.h @@ -78,7 +78,7 @@ class MessageLoopTaskQueues // Tasks methods. void RegisterTask(TaskQueueId queue_id, - fml::closure task, + const fml::closure& task, fml::TimePoint target_time); bool HasPendingTasks(TaskQueueId queue_id) const; @@ -93,7 +93,7 @@ class MessageLoopTaskQueues void AddTaskObserver(TaskQueueId queue_id, intptr_t key, - fml::closure callback); + const fml::closure& callback); void RemoveTaskObserver(TaskQueueId queue_id, intptr_t key); diff --git a/fml/platform/posix/file_posix.cc b/fml/platform/posix/file_posix.cc index c275f606d1362..ab64b5cc2de71 100644 --- a/fml/platform/posix/file_posix.cc +++ b/fml/platform/posix/file_posix.cc @@ -222,7 +222,7 @@ bool WriteAtomically(const fml::UniqueFD& base_directory, base_directory.get(), file_name) == 0; } -bool VisitFiles(const fml::UniqueFD& directory, FileVisitor visitor) { +bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) { fml::UniqueFD dup_fd(dup(directory.get())); if (!dup_fd.is_valid()) { FML_DLOG(ERROR) << "Can't dup the directory fd. Error: " << strerror(errno); diff --git a/fml/platform/win/file_win.cc b/fml/platform/win/file_win.cc index 42a5958f4d061..a4c74940c3278 100644 --- a/fml/platform/win/file_win.cc +++ b/fml/platform/win/file_win.cc @@ -399,7 +399,7 @@ bool WriteAtomically(const fml::UniqueFD& base_directory, return true; } -bool VisitFiles(const fml::UniqueFD& directory, FileVisitor visitor) { +bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) { std::string search_pattern = GetFullHandlePath(directory) + "\\*"; WIN32_FIND_DATA find_file_data; HANDLE find_handle = ::FindFirstFile( diff --git a/fml/task_runner.cc b/fml/task_runner.cc index c525332a2aaf9..c50e14a3ca4b9 100644 --- a/fml/task_runner.cc +++ b/fml/task_runner.cc @@ -20,17 +20,18 @@ TaskRunner::TaskRunner(fml::RefPtr loop) TaskRunner::~TaskRunner() = default; -void TaskRunner::PostTask(fml::closure task) { - loop_->PostTask(std::move(task), fml::TimePoint::Now()); +void TaskRunner::PostTask(const fml::closure& task) { + loop_->PostTask(task, fml::TimePoint::Now()); } -void TaskRunner::PostTaskForTime(fml::closure task, +void TaskRunner::PostTaskForTime(const fml::closure& task, fml::TimePoint target_time) { - loop_->PostTask(std::move(task), target_time); + loop_->PostTask(task, target_time); } -void TaskRunner::PostDelayedTask(fml::closure task, fml::TimeDelta delay) { - loop_->PostTask(std::move(task), fml::TimePoint::Now() + delay); +void TaskRunner::PostDelayedTask(const fml::closure& task, + fml::TimeDelta delay) { + loop_->PostTask(task, fml::TimePoint::Now() + delay); } TaskQueueId TaskRunner::GetTaskQueueId() { @@ -62,7 +63,7 @@ bool TaskRunner::RunsTasksOnCurrentThread() { } void TaskRunner::RunNowOrPostTask(fml::RefPtr runner, - fml::closure task) { + const fml::closure& task) { FML_DCHECK(runner); if (runner->RunsTasksOnCurrentThread()) { task(); diff --git a/fml/task_runner.h b/fml/task_runner.h index b7569058af17f..a66f67e13cc12 100644 --- a/fml/task_runner.h +++ b/fml/task_runner.h @@ -20,18 +20,19 @@ class TaskRunner : public fml::RefCountedThreadSafe { public: virtual ~TaskRunner(); - virtual void PostTask(fml::closure task); + virtual void PostTask(const fml::closure& task); - virtual void PostTaskForTime(fml::closure task, fml::TimePoint target_time); + virtual void PostTaskForTime(const fml::closure& task, + fml::TimePoint target_time); - virtual void PostDelayedTask(fml::closure task, fml::TimeDelta delay); + virtual void PostDelayedTask(const fml::closure& task, fml::TimeDelta delay); virtual bool RunsTasksOnCurrentThread(); virtual TaskQueueId GetTaskQueueId(); static void RunNowOrPostTask(fml::RefPtr runner, - fml::closure task); + const fml::closure& task); protected: TaskRunner(fml::RefPtr loop); diff --git a/lib/ui/painting/image_decoder.cc b/lib/ui/painting/image_decoder.cc index c6a35f073b05c..505fce2c5ccdb 100644 --- a/lib/ui/painting/image_decoder.cc +++ b/lib/ui/painting/image_decoder.cc @@ -188,7 +188,8 @@ static SkiaGPUObject UploadRasterImage( return {texture_image, queue}; } -void ImageDecoder::Decode(ImageDescriptor descriptor, ImageResult callback) { +void ImageDecoder::Decode(ImageDescriptor descriptor, + const ImageResult& callback) { TRACE_EVENT0("flutter", __FUNCTION__); fml::tracing::TraceFlow flow(__FUNCTION__); diff --git a/lib/ui/painting/image_decoder.h b/lib/ui/painting/image_decoder.h index 202dff569e1a5..7ffda4ff30017 100644 --- a/lib/ui/painting/image_decoder.h +++ b/lib/ui/painting/image_decoder.h @@ -55,7 +55,7 @@ class ImageDecoder { // concurrently. Texture upload is done on the IO thread and the result // returned back on the UI thread. On error, the texture is null but the // callback is guaranteed to return on the UI thread. - void Decode(ImageDescriptor descriptor, ImageResult result); + void Decode(ImageDescriptor descriptor, const ImageResult& result); fml::WeakPtr GetWeakPtr() const; diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index f6bc1f695ed5f..9fe676aca7b5b 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -41,8 +41,8 @@ std::weak_ptr DartIsolate::CreateRootIsolate( std::string advisory_script_uri, std::string advisory_script_entrypoint, Dart_IsolateFlags* flags, - fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback) { + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback) { TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); Dart_Isolate vm_isolate = nullptr; std::weak_ptr embedder_isolate; @@ -113,9 +113,9 @@ DartIsolate::DartIsolate(const Settings& settings, fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - ChildIsolatePreparer child_isolate_preparer, - fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback, + const ChildIsolatePreparer& child_isolate_preparer, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback, bool is_root_isolate, bool is_group_root_isolate) : UIDartState(std::move(task_runners), @@ -481,10 +481,11 @@ static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function, return true; } +/// @note Procedure doesn't copy all closures. FML_WARN_UNUSED_RESULT bool DartIsolate::Run(const std::string& entrypoint_name, const std::vector& args, - fml::closure on_run) { + const fml::closure& on_run) { TRACE_EVENT0("flutter", "DartIsolate::Run"); if (phase_ != Phase::Ready) { return false; @@ -510,11 +511,12 @@ bool DartIsolate::Run(const std::string& entrypoint_name, return true; } +/// @note Procedure doesn't copy all closures. FML_WARN_UNUSED_RESULT bool DartIsolate::RunFromLibrary(const std::string& library_name, const std::string& entrypoint_name, const std::vector& args, - fml::closure on_run) { + const fml::closure& on_run) { TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary"); if (phase_ != Phase::Ready) { return false; @@ -901,9 +903,8 @@ std::weak_ptr DartIsolate::GetWeakIsolatePtr() { return std::static_pointer_cast(shared_from_this()); } -void DartIsolate::AddIsolateShutdownCallback(fml::closure closure) { - shutdown_callbacks_.emplace_back( - std::make_unique(std::move(closure))); +void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) { + shutdown_callbacks_.emplace_back(std::make_unique(closure)); } void DartIsolate::OnShutdownCallback() { @@ -921,8 +922,8 @@ void DartIsolate::OnShutdownCallback() { } } -DartIsolate::AutoFireClosure::AutoFireClosure(fml::closure closure) - : closure_(std::move(closure)) {} +DartIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure) + : closure_(closure) {} DartIsolate::AutoFireClosure::~AutoFireClosure() { if (closure_) { diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index fa26c071fa764..100a16fea9f6a 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -199,8 +199,8 @@ class DartIsolate : public UIDartState { std::string advisory_script_uri, std::string advisory_script_entrypoint, Dart_IsolateFlags* flags, - fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback); + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback); // |UIDartState| ~DartIsolate() override; @@ -332,7 +332,7 @@ class DartIsolate : public UIDartState { FML_WARN_UNUSED_RESULT bool Run(const std::string& entrypoint, const std::vector& args, - fml::closure on_run = nullptr); + const fml::closure& on_run = nullptr); //---------------------------------------------------------------------------- /// @brief Transition the root isolate to the `Phase::Running` phase and @@ -356,7 +356,7 @@ class DartIsolate : public UIDartState { bool RunFromLibrary(const std::string& library_name, const std::string& entrypoint, const std::vector& args, - fml::closure on_run = nullptr); + const fml::closure& on_run = nullptr); //---------------------------------------------------------------------------- /// @brief Transition the isolate to the `Phase::Shutdown` phase. The @@ -374,7 +374,7 @@ class DartIsolate : public UIDartState { /// /// @param[in] closure The callback to invoke on isolate shutdown. /// - void AddIsolateShutdownCallback(fml::closure closure); + void AddIsolateShutdownCallback(const fml::closure& closure); //---------------------------------------------------------------------------- /// @brief The snapshot used to launch this isolate. This is referenced @@ -413,7 +413,7 @@ class DartIsolate : public UIDartState { class AutoFireClosure { public: - AutoFireClosure(fml::closure closure); + AutoFireClosure(const fml::closure& closure); ~AutoFireClosure(); @@ -444,9 +444,9 @@ class DartIsolate : public UIDartState { fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - ChildIsolatePreparer child_isolate_preparer, - fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback, + const ChildIsolatePreparer& child_isolate_preparer, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback, bool is_root_isolate, bool is_group_root_isolate); FML_WARN_UNUSED_RESULT bool Initialize(Dart_Isolate isolate); diff --git a/runtime/dart_service_isolate.cc b/runtime/dart_service_isolate.cc index 1d747a35ade62..d13666be8fdf2 100644 --- a/runtime/dart_service_isolate.cc +++ b/runtime/dart_service_isolate.cc @@ -81,7 +81,7 @@ void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) { } DartServiceIsolate::CallbackHandle DartServiceIsolate::AddServerStatusCallback( - DartServiceIsolate::ObservatoryServerStateCallback callback) { + const DartServiceIsolate::ObservatoryServerStateCallback& callback) { if (!callback) { return 0; } diff --git a/runtime/dart_service_isolate.h b/runtime/dart_service_isolate.h index dae068870448b..0adbabca9adb8 100644 --- a/runtime/dart_service_isolate.h +++ b/runtime/dart_service_isolate.h @@ -34,7 +34,7 @@ class DartServiceIsolate { // RemoveServerStatusCallback FML_WARN_UNUSED_RESULT static CallbackHandle AddServerStatusCallback( - ObservatoryServerStateCallback callback); + const ObservatoryServerStateCallback& callback); // Accepts the handle returned by AddServerStatusCallback static bool RemoveServerStatusCallback(CallbackHandle handle); diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 370ba3e9130bc..59380508a6e6b 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -25,9 +25,9 @@ RuntimeController::RuntimeController( fml::WeakPtr p_image_decoder, std::string p_advisory_script_uri, std::string p_advisory_script_entrypoint, - std::function p_idle_notification_callback, - fml::closure p_isolate_create_callback, - fml::closure p_isolate_shutdown_callback, + const std::function& p_idle_notification_callback, + const fml::closure& p_isolate_create_callback, + const fml::closure& p_isolate_shutdown_callback, std::shared_ptr p_persistent_isolate_data) : RuntimeController(p_client, p_vm, @@ -56,10 +56,10 @@ RuntimeController::RuntimeController( fml::WeakPtr p_image_decoder, std::string p_advisory_script_uri, std::string p_advisory_script_entrypoint, - std::function idle_notification_callback, + const std::function& idle_notification_callback, WindowData p_window_data, - fml::closure p_isolate_create_callback, - fml::closure p_isolate_shutdown_callback, + const fml::closure& p_isolate_create_callback, + const fml::closure& p_isolate_shutdown_callback, std::shared_ptr p_persistent_isolate_data) : client_(p_client), vm_(p_vm), diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 5ade4672cbece..584f490ad9d36 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -39,9 +39,9 @@ class RuntimeController final : public WindowClient { fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - std::function idle_notification_callback, - fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback, + const std::function& idle_notification_callback, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback, std::shared_ptr persistent_isolate_data); ~RuntimeController() override; @@ -155,10 +155,10 @@ class RuntimeController final : public WindowClient { fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - std::function idle_notification_callback, + const std::function& idle_notification_callback, WindowData data, - fml::closure isolate_create_callback, - fml::closure isolate_shutdown_callback, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback, std::shared_ptr persistent_isolate_data); Window* GetWindowIfAvailable(); diff --git a/runtime/skia_concurrent_executor.cc b/runtime/skia_concurrent_executor.cc index f8769ba8280d0..843b9386425b0 100644 --- a/runtime/skia_concurrent_executor.cc +++ b/runtime/skia_concurrent_executor.cc @@ -8,7 +8,7 @@ namespace flutter { -SkiaConcurrentExecutor::SkiaConcurrentExecutor(OnWorkCallback on_work) +SkiaConcurrentExecutor::SkiaConcurrentExecutor(const OnWorkCallback& on_work) : on_work_(on_work) {} SkiaConcurrentExecutor::~SkiaConcurrentExecutor() = default; diff --git a/runtime/skia_concurrent_executor.h b/runtime/skia_concurrent_executor.h index 5744726d7d464..e1a506c033ab6 100644 --- a/runtime/skia_concurrent_executor.h +++ b/runtime/skia_concurrent_executor.h @@ -14,7 +14,7 @@ namespace flutter { class SkiaConcurrentExecutor : public SkExecutor { public: using OnWorkCallback = std::function; - SkiaConcurrentExecutor(OnWorkCallback on_work); + SkiaConcurrentExecutor(const OnWorkCallback& on_work); ~SkiaConcurrentExecutor() override; diff --git a/shell/common/animator.cc b/shell/common/animator.cc index b0ef6fca098f2..690c47d38394f 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -244,8 +244,8 @@ void Animator::AwaitVSync() { delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_); } -void Animator::ScheduleSecondaryVsyncCallback(fml::closure callback) { - waiter_->ScheduleSecondaryCallback(std::move(callback)); +void Animator::ScheduleSecondaryVsyncCallback(const fml::closure& callback) { + waiter_->ScheduleSecondaryCallback(callback); } } // namespace flutter diff --git a/shell/common/animator.h b/shell/common/animator.h index dddc70b145b46..0bb15bed72a54 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -67,7 +67,7 @@ class Animator final { /// `SmoothPointerDataDispatcher`. /// /// @see `PointerDataDispatcher::ScheduleSecondaryVsyncCallback`. - void ScheduleSecondaryVsyncCallback(fml::closure callback); + void ScheduleSecondaryVsyncCallback(const fml::closure& callback); void Start(); diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 77b6577271511..c0d64b9bfc42d 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -481,8 +481,8 @@ void Engine::DoDispatchPacket(std::unique_ptr packet, } } -void Engine::ScheduleSecondaryVsyncCallback(fml::closure callback) { - animator_->ScheduleSecondaryVsyncCallback(std::move(callback)); +void Engine::ScheduleSecondaryVsyncCallback(const fml::closure& callback) { + animator_->ScheduleSecondaryVsyncCallback(callback); } void Engine::HandleAssetPlatformMessage(fml::RefPtr message) { diff --git a/shell/common/engine.h b/shell/common/engine.h index 52af0510b3e43..7c85d33c584b3 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -714,7 +714,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { uint64_t trace_flow_id) override; // |PointerDataDispatcher::Delegate| - void ScheduleSecondaryVsyncCallback(fml::closure callback) override; + void ScheduleSecondaryVsyncCallback(const fml::closure& callback) override; //---------------------------------------------------------------------------- /// @brief Get the last Entrypoint that was used in the RunConfiguration diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index 29c6c7dd8c522..2bf3b19c4b06f 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -78,7 +78,7 @@ class Pipeline : public fml::RefCountedThreadSafe> { Continuation continuation_; size_t trace_id_; - ProducerContinuation(Continuation continuation, size_t trace_id) + ProducerContinuation(const Continuation& continuation, size_t trace_id) : continuation_(continuation), trace_id_(trace_id) { TRACE_FLOW_BEGIN("flutter", "PipelineItem", trace_id_); TRACE_EVENT_ASYNC_BEGIN0("flutter", "PipelineItem", trace_id_); @@ -127,8 +127,9 @@ class Pipeline : public fml::RefCountedThreadSafe> { using Consumer = std::function; + /// @note Procedure doesn't copy all closures. FML_WARN_UNUSED_RESULT - PipelineConsumeResult Consume(Consumer consumer) { + PipelineConsumeResult Consume(const Consumer& consumer) { if (consumer == nullptr) { return PipelineConsumeResult::NoneAvailable; } diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 3c4ed02998536..f96dc0a945994 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -129,12 +129,12 @@ std::unique_ptr PlatformView::CreateRenderingSurface() { return nullptr; } -void PlatformView::SetNextFrameCallback(fml::closure closure) { +void PlatformView::SetNextFrameCallback(const fml::closure& closure) { if (!closure) { return; } - delegate_.OnPlatformViewSetNextFrameCallback(std::move(closure)); + delegate_.OnPlatformViewSetNextFrameCallback(closure); } } // namespace flutter diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 0ef0b193b9cfa..f80f7519dcddf 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -85,7 +85,8 @@ class PlatformView { /// /// @param[in] closure The callback to execute on the next frame. /// - virtual void OnPlatformViewSetNextFrameCallback(fml::closure closure) = 0; + virtual void OnPlatformViewSetNextFrameCallback( + const fml::closure& closure) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate the viewport metrics of the platform @@ -469,7 +470,7 @@ class PlatformView { /// @param[in] closure The callback to execute on the render thread when the /// next frame gets rendered. /// - void SetNextFrameCallback(fml::closure closure); + void SetNextFrameCallback(const fml::closure& closure); //---------------------------------------------------------------------------- /// @brief Dispatches pointer events from the embedder to the diff --git a/shell/common/pointer_data_dispatcher.h b/shell/common/pointer_data_dispatcher.h index d67a5b6fe7ff3..c222bee8d699e 100644 --- a/shell/common/pointer_data_dispatcher.h +++ b/shell/common/pointer_data_dispatcher.h @@ -60,7 +60,8 @@ class PointerDataDispatcher { /// /// This callback is used to provide the vsync signal needed by /// `SmoothPointerDataDispatcher`. - virtual void ScheduleSecondaryVsyncCallback(fml::closure callback) = 0; + virtual void ScheduleSecondaryVsyncCallback( + const fml::closure& callback) = 0; }; //---------------------------------------------------------------------------- diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f40be319848bb..9fbb6949d6dfb 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -483,7 +483,7 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( return Rasterizer::Screenshot{data, layer_tree->frame_size()}; } -void Rasterizer::SetNextFrameCallback(fml::closure callback) { +void Rasterizer::SetNextFrameCallback(const fml::closure& callback) { next_frame_callback_ = callback; } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 097d93fd4c92a..44c7c7a759a89 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -354,7 +354,7 @@ class Rasterizer final : public SnapshotDelegate { /// @param[in] callback The callback to execute when the next layer tree is /// rendered on-screen. /// - void SetNextFrameCallback(fml::closure callback); + void SetNextFrameCallback(const fml::closure& callback); //---------------------------------------------------------------------------- /// @brief Returns a pointer to the compositor context used by this diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 8443178a8e0d2..5155cd6b426cc 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -44,8 +44,8 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( TaskRunners task_runners, Settings settings, fml::RefPtr isolate_snapshot, - Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer) { + const Shell::CreateCallback& on_create_platform_view, + const Shell::CreateCallback& on_create_rasterizer) { if (!task_runners.IsValid()) { FML_LOG(ERROR) << "Task runners to run the shell were invalid."; return nullptr; @@ -221,8 +221,8 @@ static void PerformInitializationTasks(const Settings& settings) { std::unique_ptr Shell::Create( TaskRunners task_runners, Settings settings, - Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer) { + const Shell::CreateCallback& on_create_platform_view, + const Shell::CreateCallback& on_create_rasterizer) { PerformInitializationTasks(settings); PersistentCache::SetCacheSkSL(settings.cache_sksl); @@ -233,12 +233,12 @@ std::unique_ptr Shell::Create( auto vm_data = vm->GetVMData(); - return Shell::Create(std::move(task_runners), // - std::move(settings), // - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(on_create_platform_view), // - std::move(on_create_rasterizer), // - std::move(vm) // + return Shell::Create(std::move(task_runners), // + std::move(settings), // + vm_data->GetIsolateSnapshot(), // isolate snapshot + on_create_platform_view, // + on_create_rasterizer, // + std::move(vm) // ); } @@ -246,8 +246,8 @@ std::unique_ptr Shell::Create( TaskRunners task_runners, Settings settings, fml::RefPtr isolate_snapshot, - Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer, + const Shell::CreateCallback& on_create_platform_view, + const Shell::CreateCallback& on_create_rasterizer, DartVMRef vm) { PerformInitializationTasks(settings); PersistentCache::SetCacheSkSL(settings.cache_sksl); @@ -404,8 +404,9 @@ void Shell::RunEngine(RunConfiguration run_configuration) { RunEngine(std::move(run_configuration), nullptr); } -void Shell::RunEngine(RunConfiguration run_configuration, - std::function result_callback) { +void Shell::RunEngine( + RunConfiguration run_configuration, + const std::function& result_callback) { auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(), result_callback](Engine::RunStatus run_result) { if (!result_callback) { @@ -862,12 +863,12 @@ void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) { } // |PlatformView::Delegate| -void Shell::OnPlatformViewSetNextFrameCallback(fml::closure closure) { +void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetGPUTaskRunner()->PostTask( - [rasterizer = rasterizer_->GetWeakPtr(), closure = std::move(closure)]() { + [rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() { if (rasterizer) { rasterizer->SetNextFrameCallback(std::move(closure)); } diff --git a/shell/common/shell.h b/shell/common/shell.h index c392f08fbb9d4..5a065bb2234ee 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -122,8 +122,8 @@ class Shell final : public PlatformView::Delegate, static std::unique_ptr Create( TaskRunners task_runners, Settings settings, - CreateCallback on_create_platform_view, - CreateCallback on_create_rasterizer); + const CreateCallback& on_create_platform_view, + const CreateCallback& on_create_rasterizer); //---------------------------------------------------------------------------- /// @brief Creates a shell instance using the provided settings. The @@ -161,8 +161,8 @@ class Shell final : public PlatformView::Delegate, TaskRunners task_runners, Settings settings, fml::RefPtr isolate_snapshot, - CreateCallback on_create_platform_view, - CreateCallback on_create_rasterizer, + const CreateCallback& on_create_platform_view, + const CreateCallback& on_create_rasterizer, DartVMRef vm); //---------------------------------------------------------------------------- @@ -184,7 +184,7 @@ class Shell final : public PlatformView::Delegate, /// operation. /// void RunEngine(RunConfiguration run_configuration, - std::function result_callback); + const std::function& result_callback); //------------------------------------------------------------------------------ /// @return The settings used to launch this shell. @@ -367,8 +367,8 @@ class Shell final : public PlatformView::Delegate, TaskRunners task_runners, Settings settings, fml::RefPtr isolate_snapshot, - Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer); + const Shell::CreateCallback& on_create_platform_view, + const Shell::CreateCallback& on_create_rasterizer); bool Setup(std::unique_ptr platform_view, std::unique_ptr engine, @@ -420,7 +420,7 @@ class Shell final : public PlatformView::Delegate, void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override; // |PlatformView::Delegate| - void OnPlatformViewSetNextFrameCallback(fml::closure closure) override; + void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override; // |Animator::Delegate| void OnAnimatorBeginFrame(fml::TimePoint frame_time) override; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index b8a77ca1811d4..af889885017fc 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -10,7 +10,7 @@ namespace flutter { SurfaceFrame::SurfaceFrame(sk_sp surface, - SubmitCallback submit_callback) + const SubmitCallback& submit_callback) : submitted_(false), surface_(surface), submit_callback_(submit_callback) { FML_DCHECK(submit_callback_); } diff --git a/shell/common/surface.h b/shell/common/surface.h index 7bbc16e24c690..14f898fad3fe1 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -21,7 +21,7 @@ class SurfaceFrame { using SubmitCallback = std::function; - SurfaceFrame(sk_sp surface, SubmitCallback submit_callback); + SurfaceFrame(sk_sp surface, const SubmitCallback& submit_callback); ~SurfaceFrame(); diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index c191a13490006..feba5b647f074 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -29,7 +29,7 @@ VsyncWaiter::VsyncWaiter(TaskRunners task_runners) VsyncWaiter::~VsyncWaiter() = default; // Public method invoked by the animator. -void VsyncWaiter::AsyncWaitForVsync(Callback callback) { +void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) { if (!callback) { return; } @@ -55,7 +55,7 @@ void VsyncWaiter::AsyncWaitForVsync(Callback callback) { AwaitVSync(); } -void VsyncWaiter::ScheduleSecondaryCallback(fml::closure callback) { +void VsyncWaiter::ScheduleSecondaryCallback(const fml::closure& callback) { FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!callback) { diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index 342ca0e536338..ab2d857415652 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -23,12 +23,12 @@ class VsyncWaiter : public std::enable_shared_from_this { virtual ~VsyncWaiter(); - void AsyncWaitForVsync(Callback callback); + void AsyncWaitForVsync(const Callback& callback); /// Add a secondary callback for the next vsync. /// /// See also |PointerDataDispatcher::ScheduleSecondaryVsyncCallback|. - void ScheduleSecondaryCallback(fml::closure callback); + void ScheduleSecondaryCallback(const fml::closure& callback); static constexpr float kUnknownRefreshRateFPS = 0.0; diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h b/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h index eda3de879ff63..ec5f0e9d6c2b4 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h @@ -25,7 +25,7 @@ using MessageReply = std::function; // to the message. template using MessageHandler = - std::function reply)>; + std::function& reply)>; // A channel for communicating with the Flutter engine by sending asynchronous // messages. @@ -61,7 +61,7 @@ class BasicMessageChannel { // Registers a handler that should be called any time a message is // received on this channel. - void SetMessageHandler(MessageHandler handler) const { + void SetMessageHandler(const MessageHandler& handler) const { const auto* codec = codec_; std::string channel_name = name_; BinaryMessageHandler binary_handler = [handler, codec, channel_name]( diff --git a/shell/platform/common/cpp/incoming_message_dispatcher.cc b/shell/platform/common/cpp/incoming_message_dispatcher.cc index 31eed6b559818..6e10d2faaa046 100644 --- a/shell/platform/common/cpp/incoming_message_dispatcher.cc +++ b/shell/platform/common/cpp/incoming_message_dispatcher.cc @@ -12,10 +12,11 @@ IncomingMessageDispatcher::IncomingMessageDispatcher( IncomingMessageDispatcher::~IncomingMessageDispatcher() = default; +/// @note Procedure doesn't copy all closures. void IncomingMessageDispatcher::HandleMessage( const FlutterDesktopMessage& message, - std::function input_block_cb, - std::function input_unblock_cb) { + const std::function& input_block_cb, + const std::function& input_unblock_cb) { std::string channel(message.channel); // Find the handler for the channel; if there isn't one, report the failure. diff --git a/shell/platform/common/cpp/incoming_message_dispatcher.h b/shell/platform/common/cpp/incoming_message_dispatcher.h index 3eb874bc733e8..192552be19403 100644 --- a/shell/platform/common/cpp/incoming_message_dispatcher.h +++ b/shell/platform/common/cpp/incoming_message_dispatcher.h @@ -39,8 +39,8 @@ class IncomingMessageDispatcher { // NotImplemented response to the engine. void HandleMessage( const FlutterDesktopMessage& message, - std::function input_block_cb = [] {}, - std::function input_unblock_cb = [] {}); + const std::function& input_block_cb = [] {}, + const std::function& input_unblock_cb = [] {}); // Registers a message callback for incoming messages from the Flutter // side on the specified channel. |callback| will be called with the message diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 631ef6f23a6a4..3b260834ea33c 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -227,7 +227,7 @@ bool EmbedderEngine::ReloadSystemFonts() { return shell_->ReloadSystemFonts(); } -bool EmbedderEngine::PostRenderThreadTask(fml::closure task) { +bool EmbedderEngine::PostRenderThreadTask(const fml::closure& task) { if (!IsValid()) { return false; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 94026d9f9646f..16525d9dc14fd 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -76,7 +76,7 @@ class EmbedderEngine { bool ReloadSystemFonts(); - bool PostRenderThreadTask(fml::closure task); + bool PostRenderThreadTask(const fml::closure& task); bool RunTask(const FlutterTask* task); diff --git a/shell/platform/embedder/embedder_external_texture_gl.cc b/shell/platform/embedder/embedder_external_texture_gl.cc index d3aedf7a46719..83b26be729a86 100644 --- a/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/shell/platform/embedder/embedder_external_texture_gl.cc @@ -10,7 +10,7 @@ namespace flutter { EmbedderExternalTextureGL::EmbedderExternalTextureGL( int64_t texture_identifier, - ExternalTextureCallback callback) + const ExternalTextureCallback& callback) : Texture(texture_identifier), external_texture_callback_(callback) { FML_DCHECK(external_texture_callback_); } diff --git a/shell/platform/embedder/embedder_external_texture_gl.h b/shell/platform/embedder/embedder_external_texture_gl.h index e696ebf9eb08e..df2927e0f6e51 100644 --- a/shell/platform/embedder/embedder_external_texture_gl.h +++ b/shell/platform/embedder/embedder_external_texture_gl.h @@ -18,7 +18,7 @@ class EmbedderExternalTextureGL : public flutter::Texture { sk_sp(int64_t texture_identifier, GrContext*, const SkISize&)>; EmbedderExternalTextureGL(int64_t texture_identifier, - ExternalTextureCallback callback); + const ExternalTextureCallback& callback); ~EmbedderExternalTextureGL(); diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index ac20b010a191d..99a0398450fca 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -12,8 +12,8 @@ namespace flutter { EmbedderExternalViewEmbedder::EmbedderExternalViewEmbedder( - CreateRenderTargetCallback create_render_target_callback, - PresentCallback present_callback) + const CreateRenderTargetCallback& create_render_target_callback, + const PresentCallback& present_callback) : create_render_target_callback_(create_render_target_callback), present_callback_(present_callback) { FML_DCHECK(create_render_target_callback_); diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 1ab13670a28cd..16f4a5893843e 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -49,8 +49,8 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { /// embedder for presentation. /// EmbedderExternalViewEmbedder( - CreateRenderTargetCallback create_render_target_callback, - PresentCallback present_callback); + const CreateRenderTargetCallback& create_render_target_callback, + const PresentCallback& present_callback); //---------------------------------------------------------------------------- /// @brief Collects the external view embedder. diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc index 6b26edbcb1019..90ba77b6428e0 100644 --- a/shell/platform/embedder/embedder_layers.cc +++ b/shell/platform/embedder/embedder_layers.cc @@ -203,7 +203,9 @@ void EmbedderLayers::PushPlatformViewLayer( presented_layers_.push_back(layer); } // namespace flutter -void EmbedderLayers::InvokePresentCallback(PresentCallback callback) const { +/// @note Procedure doesn't copy all closures. +void EmbedderLayers::InvokePresentCallback( + const PresentCallback& callback) const { std::vector presented_layers_pointers; presented_layers_pointers.reserve(presented_layers_.size()); for (const auto& layer : presented_layers_) { diff --git a/shell/platform/embedder/embedder_layers.h b/shell/platform/embedder/embedder_layers.h index 8f179f3c7e32e..c1cb2907588c9 100644 --- a/shell/platform/embedder/embedder_layers.h +++ b/shell/platform/embedder/embedder_layers.h @@ -31,7 +31,7 @@ class EmbedderLayers { using PresentCallback = std::function& layers)>; - void InvokePresentCallback(PresentCallback callback) const; + void InvokePresentCallback(const PresentCallback& callback) const; private: const SkISize frame_size_; diff --git a/shell/platform/embedder/embedder_platform_message_response.cc b/shell/platform/embedder/embedder_platform_message_response.cc index 11c03d1f7431b..633e1c4b0971f 100644 --- a/shell/platform/embedder/embedder_platform_message_response.cc +++ b/shell/platform/embedder/embedder_platform_message_response.cc @@ -10,7 +10,7 @@ namespace flutter { EmbedderPlatformMessageResponse::EmbedderPlatformMessageResponse( fml::RefPtr runner, - Callback callback) + const Callback& callback) : runner_(std::move(runner)), callback_(callback) {} EmbedderPlatformMessageResponse::~EmbedderPlatformMessageResponse() = default; diff --git a/shell/platform/embedder/embedder_platform_message_response.h b/shell/platform/embedder/embedder_platform_message_response.h index c93dfc21653f3..1a5067d815048 100644 --- a/shell/platform/embedder/embedder_platform_message_response.h +++ b/shell/platform/embedder/embedder_platform_message_response.h @@ -27,7 +27,7 @@ class EmbedderPlatformMessageResponse : public PlatformMessageResponse { /// contents of the response sent by the framework back /// to the emebder. EmbedderPlatformMessageResponse(fml::RefPtr runner, - Callback callback); + const Callback& callback); //---------------------------------------------------------------------------- /// @brief Destroys the message response. Can be called on any thread. diff --git a/shell/platform/embedder/embedder_task_runner.cc b/shell/platform/embedder/embedder_task_runner.cc index ae350171dc2fb..abbc340db0faf 100644 --- a/shell/platform/embedder/embedder_task_runner.cc +++ b/shell/platform/embedder/embedder_task_runner.cc @@ -26,11 +26,11 @@ size_t EmbedderTaskRunner::GetEmbedderIdentifier() const { return embedder_identifier_; } -void EmbedderTaskRunner::PostTask(fml::closure task) { +void EmbedderTaskRunner::PostTask(const fml::closure& task) { PostTaskForTime(task, fml::TimePoint::Now()); } -void EmbedderTaskRunner::PostTaskForTime(fml::closure task, +void EmbedderTaskRunner::PostTaskForTime(const fml::closure& task, fml::TimePoint target_time) { if (!task) { return; @@ -48,7 +48,7 @@ void EmbedderTaskRunner::PostTaskForTime(fml::closure task, dispatch_table_.post_task_callback(this, baton, target_time); } -void EmbedderTaskRunner::PostDelayedTask(fml::closure task, +void EmbedderTaskRunner::PostDelayedTask(const fml::closure& task, fml::TimeDelta delay) { PostTaskForTime(task, fml::TimePoint::Now() + delay); } diff --git a/shell/platform/embedder/embedder_task_runner.h b/shell/platform/embedder/embedder_task_runner.h index 79cc52cfe8d55..464b79482c3ed 100644 --- a/shell/platform/embedder/embedder_task_runner.h +++ b/shell/platform/embedder/embedder_task_runner.h @@ -80,13 +80,14 @@ class EmbedderTaskRunner final : public fml::TaskRunner { fml::TaskQueueId placeholder_id_; // |fml::TaskRunner| - void PostTask(fml::closure task) override; + void PostTask(const fml::closure& task) override; // |fml::TaskRunner| - void PostTaskForTime(fml::closure task, fml::TimePoint target_time) override; + void PostTaskForTime(const fml::closure& task, + fml::TimePoint target_time) override; // |fml::TaskRunner| - void PostDelayedTask(fml::closure task, fml::TimeDelta delay) override; + void PostDelayedTask(const fml::closure& task, fml::TimeDelta delay) override; // |fml::TaskRunner| bool RunsTasksOnCurrentThread() override; diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index 10f9cdff9579e..567729a2147cd 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -181,7 +181,7 @@ void EmbedderConfigBuilder::SetRenderTaskRunner( } void EmbedderConfigBuilder::SetPlatformMessageCallback( - std::function callback) { + const std::function& callback) { context_.SetPlatformMessageCallback(callback); } diff --git a/shell/platform/embedder/tests/embedder_config_builder.h b/shell/platform/embedder/tests/embedder_config_builder.h index b96b8ba918802..1dfdd7f070a4c 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.h +++ b/shell/platform/embedder/tests/embedder_config_builder.h @@ -64,7 +64,7 @@ class EmbedderConfigBuilder { void SetRenderTaskRunner(const FlutterTaskRunnerDescription* runner); void SetPlatformMessageCallback( - std::function callback); + const std::function& callback); void SetCompositor(); diff --git a/shell/platform/embedder/tests/embedder_test_compositor.cc b/shell/platform/embedder/tests/embedder_test_compositor.cc index 9f68fd3cc8289..153e5df4d6f80 100644 --- a/shell/platform/embedder/tests/embedder_test_compositor.cc +++ b/shell/platform/embedder/tests/embedder_test_compositor.cc @@ -306,19 +306,19 @@ bool EmbedderTestCompositor::CreateSoftwareRenderSurface( } void EmbedderTestCompositor::SetNextPresentCallback( - PresentCallback next_present_callback) { + const PresentCallback& next_present_callback) { FML_CHECK(!next_present_callback_); next_present_callback_ = next_present_callback; } void EmbedderTestCompositor::SetNextSceneCallback( - NextSceneCallback next_scene_callback) { + const NextSceneCallback& next_scene_callback) { FML_CHECK(!next_scene_callback_); next_scene_callback_ = next_scene_callback; } void EmbedderTestCompositor::SetPlatformViewRendererCallback( - PlatformViewRendererCallback callback) { + const PlatformViewRendererCallback& callback) { platform_view_renderer_callback_ = callback; } diff --git a/shell/platform/embedder/tests/embedder_test_compositor.h b/shell/platform/embedder/tests/embedder_test_compositor.h index ae6c768973a1c..418b179e18e9e 100644 --- a/shell/platform/embedder/tests/embedder_test_compositor.h +++ b/shell/platform/embedder/tests/embedder_test_compositor.h @@ -38,7 +38,8 @@ class EmbedderTestCompositor { using PlatformViewRendererCallback = std::function(const FlutterLayer& layer, GrContext* context)>; - void SetPlatformViewRendererCallback(PlatformViewRendererCallback callback); + void SetPlatformViewRendererCallback( + const PlatformViewRendererCallback& callback); using PresentCallback = std::function; @@ -49,10 +50,10 @@ class EmbedderTestCompositor { /// /// @param[in] next_present_callback The next present callback /// - void SetNextPresentCallback(PresentCallback next_present_callback); + void SetNextPresentCallback(const PresentCallback& next_present_callback); using NextSceneCallback = std::function image)>; - void SetNextSceneCallback(NextSceneCallback next_scene_callback); + void SetNextSceneCallback(const NextSceneCallback& next_scene_callback); sk_sp GetLastComposition(); diff --git a/shell/platform/embedder/tests/embedder_test_context.cc b/shell/platform/embedder/tests/embedder_test_context.cc index 8ca4148b53507..e1de9acaaedd7 100644 --- a/shell/platform/embedder/tests/embedder_test_context.cc +++ b/shell/platform/embedder/tests/embedder_test_context.cc @@ -111,18 +111,18 @@ void EmbedderTestContext::AddNativeCallback(const char* name, } void EmbedderTestContext::SetSemanticsNodeCallback( - SemanticsNodeCallback update_semantics_node_callback) { + const SemanticsNodeCallback& update_semantics_node_callback) { update_semantics_node_callback_ = update_semantics_node_callback; } void EmbedderTestContext::SetSemanticsCustomActionCallback( - SemanticsActionCallback update_semantics_custom_action_callback) { + const SemanticsActionCallback& update_semantics_custom_action_callback) { update_semantics_custom_action_callback_ = update_semantics_custom_action_callback; } void EmbedderTestContext::SetPlatformMessageCallback( - std::function callback) { + const std::function& callback) { platform_message_callback_ = callback; } @@ -217,7 +217,7 @@ EmbedderTestCompositor& EmbedderTestContext::GetCompositor() { } void EmbedderTestContext::SetNextSceneCallback( - NextSceneCallback next_scene_callback) { + const NextSceneCallback& next_scene_callback) { if (compositor_) { compositor_->SetNextSceneCallback(next_scene_callback); return; @@ -241,8 +241,9 @@ size_t EmbedderTestContext::GetSoftwareSurfacePresentCount() const { return software_surface_present_count_; } +/// @note Procedure doesn't copy all closures. void EmbedderTestContext::FireRootSurfacePresentCallbackIfPresent( - std::function(void)> image_callback) { + const std::function(void)>& image_callback) { if (!next_scene_callback_) { return; } diff --git a/shell/platform/embedder/tests/embedder_test_context.h b/shell/platform/embedder/tests/embedder_test_context.h index ea5e0fd652746..69b2665da1852 100644 --- a/shell/platform/embedder/tests/embedder_test_context.h +++ b/shell/platform/embedder/tests/embedder_test_context.h @@ -49,18 +49,19 @@ class EmbedderTestContext { void AddNativeCallback(const char* name, Dart_NativeFunction function); - void SetSemanticsNodeCallback(SemanticsNodeCallback update_semantics_node); + void SetSemanticsNodeCallback( + const SemanticsNodeCallback& update_semantics_node); void SetSemanticsCustomActionCallback( - SemanticsActionCallback semantics_custom_action); + const SemanticsActionCallback& semantics_custom_action); void SetPlatformMessageCallback( - std::function callback); + const std::function& callback); EmbedderTestCompositor& GetCompositor(); using NextSceneCallback = std::function image)>; - void SetNextSceneCallback(NextSceneCallback next_scene_callback); + void SetNextSceneCallback(const NextSceneCallback& next_scene_callback); size_t GetGLSurfacePresentCount() const; @@ -126,7 +127,7 @@ class EmbedderTestContext { bool SofwarePresent(sk_sp image); void FireRootSurfacePresentCallbackIfPresent( - std::function(void)> image_callback); + const std::function(void)>& image_callback); FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestContext); }; diff --git a/shell/platform/embedder/vsync_waiter_embedder.cc b/shell/platform/embedder/vsync_waiter_embedder.cc index 64f1cf08e1e1a..a3d483f51b41f 100644 --- a/shell/platform/embedder/vsync_waiter_embedder.cc +++ b/shell/platform/embedder/vsync_waiter_embedder.cc @@ -6,7 +6,7 @@ namespace flutter { -VsyncWaiterEmbedder::VsyncWaiterEmbedder(VsyncCallback vsync_callback, +VsyncWaiterEmbedder::VsyncWaiterEmbedder(const VsyncCallback& vsync_callback, flutter::TaskRunners task_runners) : VsyncWaiter(std::move(task_runners)), vsync_callback_(vsync_callback) { FML_DCHECK(vsync_callback_); diff --git a/shell/platform/embedder/vsync_waiter_embedder.h b/shell/platform/embedder/vsync_waiter_embedder.h index 9c361e158a061..c1b06327c8fc3 100644 --- a/shell/platform/embedder/vsync_waiter_embedder.h +++ b/shell/platform/embedder/vsync_waiter_embedder.h @@ -14,7 +14,7 @@ class VsyncWaiterEmbedder final : public VsyncWaiter { public: using VsyncCallback = std::function; - VsyncWaiterEmbedder(VsyncCallback callback, + VsyncWaiterEmbedder(const VsyncCallback& callback, flutter::TaskRunners task_runners); ~VsyncWaiterEmbedder() override; diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 5bbca9e4718ff..8524df10f08e2 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -45,7 +45,7 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { // |flutter::PlatformView::Delegate| void OnPlatformViewDestroyed() {} // |flutter::PlatformView::Delegate| - void OnPlatformViewSetNextFrameCallback(fml::closure closure) {} + void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {} // |flutter::PlatformView::Delegate| void OnPlatformViewSetViewportMetrics( const flutter::ViewportMetrics& metrics) {} diff --git a/shell/platform/fuchsia/flutter/task_runner_adapter.cc b/shell/platform/fuchsia/flutter/task_runner_adapter.cc index 1fd65e66f6e87..e75cc16e3914e 100644 --- a/shell/platform/fuchsia/flutter/task_runner_adapter.cc +++ b/shell/platform/fuchsia/flutter/task_runner_adapter.cc @@ -19,18 +19,20 @@ class CompatTaskRunner : public fml::TaskRunner { FML_DCHECK(forwarding_target_); } - void PostTask(fml::closure task) override { - async::PostTask(forwarding_target_, std::move(task)); + void PostTask(const fml::closure& task) override { + async::PostTask(forwarding_target_, task); } - void PostTaskForTime(fml::closure task, fml::TimePoint target_time) override { + void PostTaskForTime(const fml::closure& task, + fml::TimePoint target_time) override { async::PostTaskForTime( - forwarding_target_, std::move(task), + forwarding_target_, task, zx::time(target_time.ToEpochDelta().ToNanoseconds())); } - void PostDelayedTask(fml::closure task, fml::TimeDelta delay) override { - async::PostDelayedTask(forwarding_target_, std::move(task), + void PostDelayedTask(const fml::closure& task, + fml::TimeDelta delay) override { + async::PostDelayedTask(forwarding_target_, task, zx::duration(delay.ToNanoseconds())); } diff --git a/shell/platform/fuchsia/flutter/vulkan_surface.cc b/shell/platform/fuchsia/flutter/vulkan_surface.cc index e81813198da7c..00a470dcefb4f 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface.cc @@ -451,7 +451,7 @@ bool VulkanSurface::FlushSessionAcquireAndReleaseEvents() { } void VulkanSurface::SignalWritesFinished( - std::function on_writes_committed) { + const std::function& on_writes_committed) { FML_DCHECK(on_writes_committed); if (!valid_) { diff --git a/shell/platform/fuchsia/flutter/vulkan_surface.h b/shell/platform/fuchsia/flutter/vulkan_surface.h index 2da9f459ff083..4c67c9d5538c2 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface.h @@ -69,7 +69,7 @@ class VulkanSurface final // Note: It is safe for the caller to collect the surface in the // |on_writes_committed| callback. void SignalWritesFinished( - std::function on_writes_committed) override; + const std::function& on_writes_committed) override; // |flutter::SceneUpdateContext::SurfaceProducerSurface| scenic::Image* GetImage() override; diff --git a/shell/platform/glfw/glfw_event_loop.cc b/shell/platform/glfw/glfw_event_loop.cc index f1fc08c77ce03..613895e8e4a7f 100644 --- a/shell/platform/glfw/glfw_event_loop.cc +++ b/shell/platform/glfw/glfw_event_loop.cc @@ -12,7 +12,7 @@ namespace flutter { GLFWEventLoop::GLFWEventLoop(std::thread::id main_thread_id, - TaskExpiredCallback on_task_expired) + const TaskExpiredCallback& on_task_expired) : main_thread_id_(main_thread_id), on_task_expired_(std::move(on_task_expired)) {} diff --git a/shell/platform/glfw/glfw_event_loop.h b/shell/platform/glfw/glfw_event_loop.h index 4c9185a60fc3d..b6bf5849a18bd 100644 --- a/shell/platform/glfw/glfw_event_loop.h +++ b/shell/platform/glfw/glfw_event_loop.h @@ -21,7 +21,7 @@ class GLFWEventLoop { public: using TaskExpiredCallback = std::function; GLFWEventLoop(std::thread::id main_thread_id, - TaskExpiredCallback on_task_expired); + const TaskExpiredCallback& on_task_expired); ~GLFWEventLoop(); diff --git a/shell/platform/windows/win32_task_runner.cc b/shell/platform/windows/win32_task_runner.cc index b2f5d473e5363..e8d66b9fab6f1 100644 --- a/shell/platform/windows/win32_task_runner.cc +++ b/shell/platform/windows/win32_task_runner.cc @@ -10,7 +10,7 @@ namespace flutter { Win32TaskRunner::Win32TaskRunner(DWORD main_thread_id, - TaskExpiredCallback on_task_expired) + const TaskExpiredCallback& on_task_expired) : main_thread_id_(main_thread_id), on_task_expired_(std::move(on_task_expired)) {} diff --git a/shell/platform/windows/win32_task_runner.h b/shell/platform/windows/win32_task_runner.h index 08be94941f8ac..ce71655e15c2d 100644 --- a/shell/platform/windows/win32_task_runner.h +++ b/shell/platform/windows/win32_task_runner.h @@ -23,7 +23,8 @@ namespace flutter { class Win32TaskRunner { public: using TaskExpiredCallback = std::function; - Win32TaskRunner(DWORD main_thread_id, TaskExpiredCallback on_task_expired); + Win32TaskRunner(DWORD main_thread_id, + const TaskExpiredCallback& on_task_expired); ~Win32TaskRunner(); diff --git a/vulkan/vulkan_handle.h b/vulkan/vulkan_handle.h index a829d0d10f6b7..d011493d6f19c 100644 --- a/vulkan/vulkan_handle.h +++ b/vulkan/vulkan_handle.h @@ -21,7 +21,7 @@ class VulkanHandle { VulkanHandle() : handle_(VK_NULL_HANDLE) {} - VulkanHandle(Handle handle, Disposer disposer = nullptr) + VulkanHandle(Handle handle, const Disposer& disposer = nullptr) : handle_(handle), disposer_(disposer) {} VulkanHandle(VulkanHandle&& other) From 72e167e47d96312a95e2bc4d799ef5c9a9d45ba4 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 22 Nov 2019 12:20:16 -0800 Subject: [PATCH 230/591] [web] Fix bug in rich text layout (#13981) --- lib/web_ui/lib/src/engine/text/ruler.dart | 18 +++- lib/web_ui/test/paragraph_test.dart | 102 ++++++++++++++-------- 2 files changed, 84 insertions(+), 36 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index ff90efa14518b..d03ab80773075 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -205,7 +205,7 @@ class TextDimensions { // Rich text: deeply copy contents. This is the slow case that should be // avoided if fast layout performance is desired. final html.Element copy = from._paragraphElement.clone(true); - _element.children.addAll(copy.children); + _element.nodes.addAll(copy.nodes); } } @@ -405,6 +405,10 @@ class ParagraphRuler { ..border = '0' ..padding = '0'; + if (assertionsEnabled) { + _singleLineHost.setAttribute('data-ruler', 'single-line'); + } + singleLineDimensions.applyStyle(style); // Force single-line (even if wider than screen) and preserve whitespaces. @@ -427,6 +431,10 @@ class ParagraphRuler { ..border = '0' ..padding = '0'; + if (assertionsEnabled) { + _minIntrinsicHost.setAttribute('data-ruler', 'min-intrinsic'); + } + minIntrinsicDimensions.applyStyle(style); // "flex: 0" causes the paragraph element to shrink horizontally, exposing @@ -456,6 +464,10 @@ class ParagraphRuler { ..border = '0' ..padding = '0'; + if (assertionsEnabled) { + _constrainedHost.setAttribute('data-ruler', 'constrained'); + } + constrainedDimensions.applyStyle(style); final html.CssStyleDeclaration elementStyle = constrainedDimensions._element.style; @@ -495,6 +507,10 @@ class ParagraphRuler { ..border = '0' ..padding = '0'; + if (assertionsEnabled) { + _lineHeightHost.setAttribute('data-ruler', 'line-height'); + } + lineHeightDimensions.applyStyle(style); // Force single-line (even if wider than screen) and preserve whitespaces. diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart index 4d5c0a92c86b2..b53010600258b 100644 --- a/lib/web_ui/test/paragraph_test.dart +++ b/lib/web_ui/test/paragraph_test.dart @@ -2,17 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; import 'package:test/test.dart'; +void testEachMeasurement(String description, VoidCallback body) { + test(description, () async { + try { + TextMeasurementService.initialize(rulerCacheCapacity: 2); + return body(); + } finally { + TextMeasurementService.clearCache(); + } + }); + test('$description (canvas measurement)', () async { + try { + TextMeasurementService.initialize(rulerCacheCapacity: 2); + TextMeasurementService.enableExperimentalCanvasImplementation = true; + return body(); + } finally { + TextMeasurementService.enableExperimentalCanvasImplementation = false; + TextMeasurementService.clearCache(); + } + }); +} + void main() async { await webOnlyInitializeTestDomRenderer(); // Ahem font uses a constant ideographic/alphabetic baseline ratio. const double kAhemBaselineRatio = 1.25; - test('predictably lays out a single-line paragraph', () { + testEachMeasurement('predictably lays out a single-line paragraph', () { for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( fontFamily: 'Ahem', @@ -36,7 +58,7 @@ void main() async { } }); - test('predictably lays out a multi-line paragraph', () { + testEachMeasurement('predictably lays out a multi-line paragraph', () { for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( fontFamily: 'Ahem', @@ -63,41 +85,50 @@ void main() async { } }); - // Regression test for https://github.com/flutter/flutter/issues/37744 - test('measures heights of multiple multi-span paragraphs', () { - const double fontSize = 20.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); - builder.addText('1234567890 1234567890 1234567890 1234567890 1234567890'); - builder.addText('1234567890 1234567890 1234567890 1234567890 1234567890'); - builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); - builder.addText('span0'); - final Paragraph paragraph = builder.build(); - paragraph.layout(ParagraphConstraints(width: fontSize * 5.0)); - expect( - paragraph.height, closeTo(fontSize * 3.0, 0.001)); // because it wraps + testEachMeasurement('predictably lays out a single-line rich paragraph', () { + for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + )); + builder.addText('span1'); + builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); + builder.addText('span2'); + final Paragraph paragraph = builder.build(); + paragraph.layout(ParagraphConstraints(width: fontSize * 10.0)); - // Now create another builder with just a single line of text so - // it tries to reuse ruler cache but misses. - final ParagraphBuilder builder2 = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); - builder2.addText('span1'); - builder2.pushStyle(TextStyle(fontWeight: FontWeight.bold)); - builder2.addText('span2'); - final Paragraph paragraph2 = builder2.build(); - paragraph2.layout(ParagraphConstraints(width: fontSize * 5.0)); - expect(paragraph2.height, closeTo(fontSize, 0.001)); // because it wraps + expect(paragraph.height, fontSize); + expect(paragraph.width, fontSize * 10.0); + expect(paragraph.minIntrinsicWidth, fontSize * 10.0); + expect(paragraph.maxIntrinsicWidth, fontSize * 10.0); + } + }); + + testEachMeasurement('predictably lays out a multi-line rich paragraph', () { + for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + )); + builder.addText('12345 '); + builder.addText('67890 '); + builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); + builder.addText('bold'); + final Paragraph paragraph = builder.build(); + paragraph.layout(ParagraphConstraints(width: fontSize * 5.0)); + + expect(paragraph.height, fontSize * 3.0); // because it wraps + expect(paragraph.width, fontSize * 5.0); + expect(paragraph.minIntrinsicWidth, fontSize * 5.0); + expect(paragraph.maxIntrinsicWidth, fontSize * 16.0); + } }); - test('getBoxesForRange returns a box', () { + testEachMeasurement('getBoxesForRange returns a box', () { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( fontFamily: 'Ahem', fontStyle: FontStyle.normal, @@ -120,7 +151,8 @@ void main() async { ); }); - test('getBoxesForRange return empty list for zero-length range', () { + testEachMeasurement( + 'getBoxesForRange return empty list for zero-length range', () { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( fontFamily: 'Ahem', fontStyle: FontStyle.normal, From e08e8ea5276f6e5ece8b52777f722644a2773b4b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 22 Nov 2019 15:22:28 -0500 Subject: [PATCH 231/591] Roll fuchsia/sdk/core/mac-amd64 from l1EAN... to Q9HKI... (#13979) Roll fuchsia/sdk/core/mac-amd64 from l1EAN... to Q9HKI... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 641e14c85ced7..1d81902fde535 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'l1EANDzCbwqmrL4BVDrtx0A4u3lI39qi6eTzfQzFu4UC' + 'version': 'Q9HKIe4uSBsOGG_MNyOuqsVV6ZLQl3IuwiI4ThsH4JQC' } ], 'condition': 'host_os == "mac"', From 8df3eb80574ebe7d57e05d64e3b614a0e4bcd0c4 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 22 Nov 2019 12:27:06 -0800 Subject: [PATCH 232/591] Add dev_compiler and frontend_server to package uploading rule (#13926) --- flutter_frontend_server/BUILD.gn | 1 + flutter_frontend_server/package_incremental.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/flutter_frontend_server/BUILD.gn b/flutter_frontend_server/BUILD.gn index dc9291521ecb1..06aeb3f73453d 100644 --- a/flutter_frontend_server/BUILD.gn +++ b/flutter_frontend_server/BUILD.gn @@ -95,6 +95,7 @@ action("package_incremental_compiler") { "$root_gen_dir/dart-pkg/front_end/pubspec.yaml", "$root_gen_dir/dart-pkg/kernel/pubspec.yaml", "$root_gen_dir/dart-pkg/dev_compiler/pubspec.yaml", + "$root_gen_dir/dart-pkg/frontend_server/pubspec.yaml", ] args = [ diff --git a/flutter_frontend_server/package_incremental.py b/flutter_frontend_server/package_incremental.py index a8b42426023b5..8c54f31780bfd 100755 --- a/flutter_frontend_server/package_incremental.py +++ b/flutter_frontend_server/package_incremental.py @@ -16,6 +16,8 @@ "front_end", "dev_compiler", "flutter_frontend_server", + "frontend_server", + "dev_compiler", ] VM_PUBSPEC = r'''name: vm @@ -84,6 +86,19 @@ source_maps: any ''' +FRONTEND_SERVER_PUBSPEC = r'''name: frontend_server +version: 0.0.1 +environment: + sdk: '>=2.2.2 < 3.0.0' +dependencies: + build_integration: any + vm: any + dev_compiler: any + front_end: any + kernel: any + args: any +''' + PUBSPECS = { 'vm': VM_PUBSPEC, 'build_integration': BUILD_INTEGRATION_PUBSPEC, @@ -91,6 +106,7 @@ 'kernel': KERNEL_PUBSPEC, 'front_end': FRONT_END_PUBSPEC, 'dev_compiler': DEV_COMPILER_PUBSPEC, + 'frontend_server': FRONTEND_SERVER_PUBSPEC, } def main(): From 69f94a50103e65dff4e9d26142edc86ec7c873ef Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 22 Nov 2019 13:05:17 -0800 Subject: [PATCH 233/591] Roll src/third_party/dart 029e06b8d9..5b5f34f2e6 (10 commits) (#13982) dart-lang/sdk@5b5f34f2e6 [vm] Cleanup try-catch blocks around Expression.getStaticType dart-lang/sdk@ecaa3e643a [cfe] Move inference visitor to its own library and rename kernel_shadow_ast to internal_ast dart-lang/sdk@f2b7af83e7 Migration: eliminate some explicit uses of always/never nodes from node_builder.dart. dart-lang/sdk@e78a2fd0df [cfe] Remove optional parameter to toString dart-lang/sdk@82c9950e9c Reland "Fix completion in Analysis Server LSP mode" dart-lang/sdk@1f382890a7 [cfe] Make inference NNBD-aware dart-lang/sdk@bd06f1bf51 [cfe/infra] Unit test suites: print re-run command when a test fails dart-lang/sdk@a2bf8153af Revert "Fix completion in Analysis Server LSP mode" dart-lang/sdk@ec8fc01d3b Fix completion in Analysis Server LSP mode dart-lang/sdk@63e8213721 Expose 'typeProvider' and 'typeSystem' from LibraryElement. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1d81902fde535..952ac106b1959 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '029e06b8d9eb95f836cd59de6b8ac7fd7cd37934', + 'dart_revision': '5b5f34f2e6e047edbd4280a5d9c906c6399280e8', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From a5f42905b5833e81bb8994bad5cfd633141beda4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 22 Nov 2019 16:18:27 -0500 Subject: [PATCH 234/591] Roll fuchsia/sdk/core/linux-amd64 from mvesW... to _7JyV... (#13983) Roll fuchsia/sdk/core/linux-amd64 from mvesW... to _7JyV... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 952ac106b1959..f2bec7fb5697e 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'mvesWmuGSgtmxJcGYf-5KsasSS56pnl2SEro4s3OmAsC' + 'version': '_7JyV941rE763PwZ7OoX0N79LxUFM0v_4DLGTdctL98C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 8bdc583f91899..8a435f8a47e6c 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: e8cda6b61a13398cffde70b5d65cfd92 +Signature: 9a9cbde37b9b7c7eb2d83a5194692dd2 UNUSED LICENSES: From 9c683668027901f2c1d330b4d9bbe28ccfbc53a2 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 22 Nov 2019 13:30:18 -0800 Subject: [PATCH 235/591] Added auto-reviewer config file (#13962) --- .github/auto_assign.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/auto_assign.yml diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml new file mode 100644 index 0000000000000..ef0e0d89db31b --- /dev/null +++ b/.github/auto_assign.yml @@ -0,0 +1,40 @@ +# This is the config file for `auto-assign` bot. +# https://github.com/kentaro-m/auto-assign/ + +# Set to true to add reviewers to pull requests +addReviewers: true + +# Set to true to add assignees to pull requests +addAssignees: false + +# A list of reviewers to be added to pull requests (GitHub user name) +# Note: Add new engine contributors here when joining the team. +reviewers: + - gaaclarke + - liyuqian + - gw280 + - chinmaygarde + - GaryQian + - jason-simmons + - iskakaushik + - franciscojma86 + - cbracken + - flar + - stuartmorgan + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 1 + +# A list of assignees, overrides reviewers if set +# assignees: +# - assigneeA + +# A number of assignees to add to the pull request +# Set to 0 to add all of the assignees. +# Uses numberOfReviewers if unset. +# numberOfAssignees: 2 + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +# skipKeywords: +# - wip From 0d60e1a324c8f17617855f4cfe7287368e15f3dd Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 22 Nov 2019 14:02:50 -0800 Subject: [PATCH 236/591] Do not default to downstream affinity on iOS insertText (#13852) --- .../darwin/ios/framework/Source/FlutterTextInputPlugin.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index ad39e710eeb50..f22fb51b42dfb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -621,7 +621,9 @@ - (BOOL)hasText { } - (void)insertText:(NSString*)text { - _selectionAffinity = _kTextAffinityDownstream; + // The affinity is unknown here. Set to "" so that Flutter interprets it + // as ambiguous and uses a fallback affinity. + _selectionAffinity = ""; [self replaceRange:_selectedTextRange withText:text]; } From 97a23a80e1b4f744c6d9d9f6d83f5c0df1aeb11f Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 22 Nov 2019 14:08:33 -0800 Subject: [PATCH 237/591] Made a way to turn off the OpenGL operations on the IO thread for backgrounded apps (#13908) --- ci/licenses_golden/licenses_flutter | 3 + flow/skia_gpu_object.h | 4 +- fml/BUILD.gn | 3 + fml/synchronization/sync_switch.cc | 39 +++++++++++ fml/synchronization/sync_switch.h | 67 +++++++++++++++++++ fml/synchronization/sync_switch_unittest.cc | 30 +++++++++ lib/ui/io_manager.h | 3 + lib/ui/painting/image_decoder.cc | 53 +++++++++------ lib/ui/painting/image_decoder_unittests.cc | 17 ++++- shell/common/shell.cc | 23 +++++-- shell/common/shell.h | 6 ++ shell/common/shell_io_manager.cc | 12 +++- shell/common/shell_io_manager.h | 6 ++ .../ios/framework/Headers/FlutterEngine.h | 7 ++ .../ios/framework/Source/FlutterEngine.mm | 30 ++++++++- 15 files changed, 268 insertions(+), 35 deletions(-) create mode 100644 fml/synchronization/sync_switch.cc create mode 100644 fml/synchronization/sync_switch.h create mode 100644 fml/synchronization/sync_switch_unittest.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 33569a6ced33a..6df05a1cc4085 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -203,6 +203,9 @@ FILE: ../../../flutter/fml/synchronization/semaphore_unittest.cc FILE: ../../../flutter/fml/synchronization/shared_mutex.h FILE: ../../../flutter/fml/synchronization/shared_mutex_std.cc FILE: ../../../flutter/fml/synchronization/shared_mutex_std.h +FILE: ../../../flutter/fml/synchronization/sync_switch.cc +FILE: ../../../flutter/fml/synchronization/sync_switch.h +FILE: ../../../flutter/fml/synchronization/sync_switch_unittest.cc FILE: ../../../flutter/fml/synchronization/waitable_event.cc FILE: ../../../flutter/fml/synchronization/waitable_event.h FILE: ../../../flutter/fml/synchronization/waitable_event_unittest.cc diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 4c079af96ee95..4823ec14208c5 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -57,7 +57,7 @@ class SkiaGPUObject { SkiaGPUObject(sk_sp object, fml::RefPtr queue) : object_(std::move(object)), queue_(std::move(queue)) { - FML_DCHECK(queue_ && object_); + FML_DCHECK(object_); } SkiaGPUObject(SkiaGPUObject&&) = default; @@ -69,7 +69,7 @@ class SkiaGPUObject { sk_sp get() const { return object_; } void reset() { - if (object_) { + if (object_ && queue_) { queue_->Unref(object_.release()); } queue_ = nullptr; diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 75ff7aeae8c08..f6eca0f933166 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -64,6 +64,8 @@ source_set("fml") { "synchronization/semaphore.cc", "synchronization/semaphore.h", "synchronization/shared_mutex.h", + "synchronization/sync_switch.cc", + "synchronization/sync_switch.h", "synchronization/waitable_event.cc", "synchronization/waitable_event.h", "task_runner.cc", @@ -210,6 +212,7 @@ executable("fml_unittests") { "paths_unittests.cc", "platform/darwin/string_range_sanitization_unittests.mm", "synchronization/semaphore_unittest.cc", + "synchronization/sync_switch_unittest.cc", "synchronization/waitable_event_unittest.cc", "thread_local_unittests.cc", "time/time_delta_unittest.cc", diff --git a/fml/synchronization/sync_switch.cc b/fml/synchronization/sync_switch.cc new file mode 100644 index 0000000000000..7ab90db9eb58d --- /dev/null +++ b/fml/synchronization/sync_switch.cc @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/synchronization/sync_switch.h" + +namespace fml { + +SyncSwitch::Handlers& SyncSwitch::Handlers::SetIfTrue( + const std::function& handler) { + true_handler = std::move(handler); + return *this; +} + +SyncSwitch::Handlers& SyncSwitch::Handlers::SetIfFalse( + const std::function& handler) { + false_handler = std::move(handler); + return *this; +} + +SyncSwitch::SyncSwitch() : SyncSwitch(false) {} + +SyncSwitch::SyncSwitch(bool value) : value_(value) {} + +void SyncSwitch::Execute(const SyncSwitch::Handlers& handlers) { + std::scoped_lock guard(mutex_); + if (value_) { + handlers.true_handler(); + } else { + handlers.false_handler(); + } +} + +void SyncSwitch::SetSwitch(bool value) { + std::scoped_lock guard(mutex_); + value_ = value; +} + +} // namespace fml diff --git a/fml/synchronization/sync_switch.h b/fml/synchronization/sync_switch.h new file mode 100644 index 0000000000000..4fd7bfdd49c18 --- /dev/null +++ b/fml/synchronization/sync_switch.h @@ -0,0 +1,67 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_ +#define FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_ + +#include +#include +#include + +#include "flutter/fml/macros.h" + +namespace fml { + +/// A threadsafe structure that allows you to switch between 2 different +/// execution paths. +/// +/// Execution and setting the switch is exclusive, i.e. only one will happen +/// at a time. +class SyncSwitch { + public: + /// Represents the 2 code paths available when calling |SyncSwitch::Execute|. + struct Handlers { + /// Sets the handler that will be executed if the |SyncSwitch| is true. + Handlers& SetIfTrue(const std::function& handler); + + /// Sets the handler that will be executed if the |SyncSwitch| is false. + Handlers& SetIfFalse(const std::function& handler); + + std::function true_handler = [] {}; + std::function false_handler = [] {}; + }; + + /// Create a |SyncSwitch| with the false value. + SyncSwitch(); + + /// Create a |SyncSwitch| with the specified value. + /// + /// @param[in] value Default value for the |SyncSwitch|. + SyncSwitch(bool value); + + /// Diverge execution between true and false values of the SyncSwitch. + /// + /// This can be called on any thread. Note that attempting to call + /// |SetSwitch| inside of the handlers will result in a self deadlock. + /// + /// @param[in] handlers Called for the correct value of the |SyncSwitch|. + void Execute(const Handlers& handlers); + + /// Set the value of the SyncSwitch. + /// + /// This can be called on any thread. + /// + /// @param[in] value New value for the |SyncSwitch|. + void SetSwitch(bool value); + + private: + std::mutex mutex_; + bool value_; + + FML_DISALLOW_COPY_AND_ASSIGN(SyncSwitch); +}; + +} // namespace fml + +#endif // FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_ diff --git a/fml/synchronization/sync_switch_unittest.cc b/fml/synchronization/sync_switch_unittest.cc new file mode 100644 index 0000000000000..09994496cf907 --- /dev/null +++ b/fml/synchronization/sync_switch_unittest.cc @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/synchronization/sync_switch.h" + +#include "gtest/gtest.h" + +using fml::SyncSwitch; + +TEST(SyncSwitchTest, Basic) { + SyncSwitch syncSwitch; + bool switchValue = false; + syncSwitch.Execute(SyncSwitch::Handlers() + .SetIfTrue([&] { switchValue = true; }) + .SetIfFalse([&] { switchValue = false; })); + EXPECT_FALSE(switchValue); + syncSwitch.SetSwitch(true); + syncSwitch.Execute(SyncSwitch::Handlers() + .SetIfTrue([&] { switchValue = true; }) + .SetIfFalse([&] { switchValue = false; })); + EXPECT_TRUE(switchValue); +} + +TEST(SyncSwitchTest, NoopIfUndefined) { + SyncSwitch syncSwitch; + bool switchValue = false; + syncSwitch.Execute(SyncSwitch::Handlers()); + EXPECT_FALSE(switchValue); +} diff --git a/lib/ui/io_manager.h b/lib/ui/io_manager.h index 200d732f115ad..dfcee970c745b 100644 --- a/lib/ui/io_manager.h +++ b/lib/ui/io_manager.h @@ -7,6 +7,7 @@ #include "flutter/flow/skia_gpu_object.h" #include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/synchronization/sync_switch.h" #include "third_party/skia/include/gpu/GrContext.h" namespace flutter { @@ -22,6 +23,8 @@ class IOManager { virtual fml::WeakPtr GetResourceContext() const = 0; virtual fml::RefPtr GetSkiaUnrefQueue() const = 0; + + virtual std::shared_ptr GetIsGpuDisabledSyncSwitch() = 0; }; } // namespace flutter diff --git a/lib/ui/painting/image_decoder.cc b/lib/ui/painting/image_decoder.cc index 505fce2c5ccdb..9902d95fbd894 100644 --- a/lib/ui/painting/image_decoder.cc +++ b/lib/ui/painting/image_decoder.cc @@ -151,8 +151,7 @@ static sk_sp ImageFromCompressedData( static SkiaGPUObject UploadRasterImage( sk_sp image, - fml::WeakPtr context, - fml::RefPtr queue, + fml::WeakPtr io_manager, const fml::tracing::TraceFlow& flow) { TRACE_EVENT0("flutter", __FUNCTION__); flow.Step(__FUNCTION__); @@ -161,7 +160,7 @@ static SkiaGPUObject UploadRasterImage( // the this method. FML_DCHECK(!image->isTextureBacked()); - if (!context || !queue) { + if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) { FML_LOG(ERROR) << "Could not acquire context of release queue for texture upload."; return {}; @@ -173,19 +172,36 @@ static SkiaGPUObject UploadRasterImage( return {}; } - auto texture_image = - SkImage::MakeCrossContextFromPixmap(context.get(), // context - pixmap, // pixmap - true, // buildMips, - true // limitToMaxTextureSize - ); - - if (!texture_image) { - FML_LOG(ERROR) << "Could not make x-context image."; - return {}; - } - - return {texture_image, queue}; + SkiaGPUObject result; + io_manager->GetIsGpuDisabledSyncSwitch()->Execute( + fml::SyncSwitch::Handlers() + .SetIfTrue([&result, &pixmap, &image] { + SkSafeRef(image.get()); + sk_sp texture_image = SkImage::MakeFromRaster( + pixmap, + [](const void* pixels, SkImage::ReleaseContext context) { + SkSafeUnref(static_cast(context)); + }, + image.get()); + result = {texture_image, nullptr}; + }) + .SetIfFalse([&result, context = io_manager->GetResourceContext(), + &pixmap, queue = io_manager->GetSkiaUnrefQueue()] { + sk_sp texture_image = SkImage::MakeCrossContextFromPixmap( + context.get(), // context + pixmap, // pixmap + true, // buildMips, + true // limitToMaxTextureSize + ); + if (!texture_image) { + FML_LOG(ERROR) << "Could not make x-context image."; + result = {}; + } else { + result = {texture_image, queue}; + } + })); + + return result; } void ImageDecoder::Decode(ImageDescriptor descriptor, @@ -265,9 +281,8 @@ void ImageDecoder::Decode(ImageDescriptor descriptor, return; } - auto uploaded = UploadRasterImage( - std::move(decompressed), io_manager->GetResourceContext(), - io_manager->GetSkiaUnrefQueue(), flow); + auto uploaded = + UploadRasterImage(std::move(decompressed), io_manager, flow); if (!uploaded.get()) { FML_LOG(ERROR) << "Could not upload image to the GPU."; diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index b4dfece10738a..258da2dbeee18 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc @@ -29,7 +29,8 @@ class TestIOManager final : public IOManager { task_runner, fml::TimeDelta::FromNanoseconds(0))), runner_(task_runner), - weak_factory_(this) { + weak_factory_(this), + is_gpu_disabled_sync_switch_(std::make_shared()) { FML_CHECK(task_runner->RunsTasksOnCurrentThread()) << "The IO manager must be initialized its primary task runner. The " "test harness may not be setup correctly/safely."; @@ -62,6 +63,14 @@ class TestIOManager final : public IOManager { return unref_queue_; } + // |IOManager| + std::shared_ptr GetIsGpuDisabledSyncSwitch() override { + did_access_is_gpu_disabled_sync_switch_ = true; + return is_gpu_disabled_sync_switch_; + } + + bool did_access_is_gpu_disabled_sync_switch_ = false; + private: TestGLSurface gl_surface_; sk_sp gl_context_; @@ -70,6 +79,7 @@ class TestIOManager final : public IOManager { fml::WeakPtr weak_prototype_; fml::RefPtr runner_; fml::WeakPtrFactory weak_factory_; + std::shared_ptr is_gpu_disabled_sync_switch_; FML_DISALLOW_COPY_AND_ASSIGN(TestIOManager); }; @@ -167,7 +177,7 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) { fml::AutoResetWaitableEvent latch; - std::unique_ptr io_manager; + std::unique_ptr io_manager; auto release_io_manager = [&]() { io_manager.reset(); @@ -187,8 +197,10 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) { ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); ASSERT_TRUE(image.get()); + EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_); runners.GetIOTaskRunner()->PostTask(release_io_manager); }; + EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_); image_decoder->Decode(std::move(image_descriptor), callback); }; @@ -198,7 +210,6 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) { }; runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode); - latch.Wait(); } diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 5155cd6b426cc..bef72617c2bd6 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -104,15 +104,17 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( // https://github.com/flutter/flutter/issues/42948 fml::TaskRunner::RunNowOrPostTask( io_task_runner, - [&io_manager_promise, // - &weak_io_manager_promise, // - &unref_queue_promise, // - platform_view = platform_view->GetWeakPtr(), // - io_task_runner // + [&io_manager_promise, // + &weak_io_manager_promise, // + &unref_queue_promise, // + platform_view = platform_view->GetWeakPtr(), // + io_task_runner, // + is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() // ]() { TRACE_EVENT0("flutter", "ShellSetupIOSubsystem"); auto io_manager = std::make_unique( - platform_view.getUnsafe()->CreateResourceContext(), io_task_runner); + platform_view.getUnsafe()->CreateResourceContext(), + is_backgrounded_sync_switch, io_task_runner); weak_io_manager_promise.set_value(io_manager->GetWeakPtr()); unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue()); io_manager_promise.set_value(std::move(io_manager)); @@ -289,6 +291,7 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) : task_runners_(std::move(task_runners)), settings_(std::move(settings)), vm_(std::move(vm)), + is_gpu_disabled_sync_switch_(new fml::SyncSwitch()), weak_factory_(this), weak_factory_gpu_(nullptr) { FML_CHECK(vm_) << "Must have access to VM to create a shell."; @@ -647,7 +650,9 @@ void Shell::OnPlatformViewDestroyed() { auto io_task = [io_manager = io_manager_.get(), &latch]() { // Execute any pending Skia object deletions while GPU access is still // allowed. - io_manager->GetSkiaUnrefQueue()->Drain(); + io_manager->GetIsGpuDisabledSyncSwitch()->Execute( + fml::SyncSwitch::Handlers().SetIfFalse( + [&] { io_manager->GetSkiaUnrefQueue()->Drain(); })); // Step 3: All done. Signal the latch that the platform thread is waiting // on. latch.Signal(); @@ -1419,4 +1424,8 @@ bool Shell::ReloadSystemFonts() { return true; } +std::shared_ptr Shell::GetIsGpuDisabledSyncSwitch() const { + return is_gpu_disabled_sync_switch_; +} + } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index 5a065bb2234ee..d306006c92b52 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -18,6 +18,7 @@ #include "flutter/fml/memory/thread_checker.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/status.h" +#include "flutter/fml/synchronization/sync_switch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/thread.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" @@ -305,6 +306,10 @@ class Shell final : public PlatformView::Delegate, /// bool EngineHasLivePorts() const; + //---------------------------------------------------------------------------- + /// @brief Accessor for the disable GPU SyncSwitch + std::shared_ptr GetIsGpuDisabledSyncSwitch() const; + private: using ServiceProtocolHandler = std::function engine_; // on UI task runner std::unique_ptr rasterizer_; // on GPU task runner std::unique_ptr io_manager_; // on IO task runner + std::shared_ptr is_gpu_disabled_sync_switch_; fml::WeakPtr weak_engine_; // to be shared across threads fml::WeakPtr weak_rasterizer_; // to be shared across threads diff --git a/shell/common/shell_io_manager.cc b/shell/common/shell_io_manager.cc index 905c8108cad20..6c23167bf85d2 100644 --- a/shell/common/shell_io_manager.cc +++ b/shell/common/shell_io_manager.cc @@ -53,6 +53,7 @@ sk_sp ShellIOManager::CreateCompatibleResourceLoadingContext( ShellIOManager::ShellIOManager( sk_sp resource_context, + std::shared_ptr is_gpu_disabled_sync_switch, fml::RefPtr unref_queue_task_runner) : resource_context_(std::move(resource_context)), resource_context_weak_factory_( @@ -62,7 +63,8 @@ ShellIOManager::ShellIOManager( unref_queue_(fml::MakeRefCounted( std::move(unref_queue_task_runner), fml::TimeDelta::FromMilliseconds(8))), - weak_factory_(this) { + weak_factory_(this), + is_gpu_disabled_sync_switch_(is_gpu_disabled_sync_switch) { if (!resource_context_) { #ifndef OS_FUCHSIA FML_DLOG(WARNING) << "The IO manager was initialized without a resource " @@ -75,7 +77,8 @@ ShellIOManager::ShellIOManager( ShellIOManager::~ShellIOManager() { // Last chance to drain the IO queue as the platform side reference to the // underlying OpenGL context may be going away. - unref_queue_->Drain(); + is_gpu_disabled_sync_switch_->Execute( + fml::SyncSwitch::Handlers().SetIfFalse([&] { unref_queue_->Drain(); })); } void ShellIOManager::NotifyResourceContextAvailable( @@ -117,4 +120,9 @@ fml::WeakPtr ShellIOManager::GetWeakIOManager() const { return weak_factory_.GetWeakPtr(); } +// |IOManager| +std::shared_ptr ShellIOManager::GetIsGpuDisabledSyncSwitch() { + return is_gpu_disabled_sync_switch_; +} + } // namespace flutter diff --git a/shell/common/shell_io_manager.h b/shell/common/shell_io_manager.h index 066937f53bdaa..c50529fda7553 100644 --- a/shell/common/shell_io_manager.h +++ b/shell/common/shell_io_manager.h @@ -25,6 +25,7 @@ class ShellIOManager final : public IOManager { sk_sp gl_interface); ShellIOManager(sk_sp resource_context, + std::shared_ptr is_gpu_disabled_sync_switch, fml::RefPtr unref_queue_task_runner); ~ShellIOManager() override; @@ -51,6 +52,9 @@ class ShellIOManager final : public IOManager { // |IOManager| fml::RefPtr GetSkiaUnrefQueue() const override; + // |IOManager| + std::shared_ptr GetIsGpuDisabledSyncSwitch() override; + private: // Resource context management. sk_sp resource_context_; @@ -62,6 +66,8 @@ class ShellIOManager final : public IOManager { fml::WeakPtrFactory weak_factory_; + std::shared_ptr is_gpu_disabled_sync_switch_; + FML_DISALLOW_COPY_AND_ASSIGN(ShellIOManager); }; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index 193a9cc8e8be7..344c2b037fc3f 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -302,6 +302,13 @@ FLUTTER_EXPORT */ @property(nonatomic, readonly, copy, nullable) NSString* isolateId; +/** + * Whether or not GPU calls are allowed. + * + * Typically this is set when the app is backgrounded and foregrounded. + */ +@property(nonatomic, assign) BOOL isGpuDisabled; + @end NS_ASSUME_NONNULL_END diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index a9f20708f15b5..c2af9bfd66ace 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -108,6 +108,16 @@ - (instancetype)initWithName:(NSString*)labelPrefix name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [center addObserver:self + selector:@selector(applicationBecameActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + [center addObserver:self + selector:@selector(applicationWillResignActive:) + name:UIApplicationWillResignActiveNotification + object:nil]; + return self; } @@ -117,11 +127,11 @@ - (void)dealloc { [_binaryMessenger release]; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; if (_flutterViewControllerWillDeallocObserver) { [center removeObserver:_flutterViewControllerWillDeallocObserver]; [_flutterViewControllerWillDeallocObserver release]; } + [center removeObserver:self]; [super dealloc]; } @@ -456,6 +466,7 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { } _publisher.reset([[FlutterObservatoryPublisher alloc] init]); [self maybeSetupPlatformViewChannels]; + _shell->GetIsGpuDisabledSyncSwitch()->SetSwitch(_isGpuDisabled ? true : false); } return _shell != nullptr; @@ -639,7 +650,15 @@ - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { return _pluginPublications[pluginKey]; } -#pragma mark - Memory Notifications +#pragma mark - Notifications + +- (void)applicationBecameActive:(NSNotification*)notification { + [self setIsGpuDisabled:NO]; +} + +- (void)applicationWillResignActive:(NSNotification*)notification { + [self setIsGpuDisabled:YES]; +} - (void)onMemoryWarning:(NSNotification*)notification { if (_shell) { @@ -648,6 +667,13 @@ - (void)onMemoryWarning:(NSNotification*)notification { [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}]; } +- (void)setIsGpuDisabled:(BOOL)value { + if (_shell) { + _shell->GetIsGpuDisabledSyncSwitch()->SetSwitch(value ? true : false); + } + _isGpuDisabled = value; +} + @end @implementation FlutterEngineRegistrar { From 0f530a7f2641f98a0b04e6badef32298d1adb147 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 22 Nov 2019 19:13:06 -0500 Subject: [PATCH 238/591] Roll src/third_party/skia 078e8faa26d8..47af12aa8331 (22 commits) (#13987) https://skia.googlesource.com/skia.git/+log/078e8faa26d8..47af12aa8331 git log 078e8faa26d8..47af12aa8331 --date=short --no-merges --format='%ad %ae %s' 2019-11-22 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-22 bsalomon@google.com Test texture domain effect with local matrix. 2019-11-22 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 b8e748be6b94..4c7db77e0185 (21 commits) 2019-11-22 halcanary@google.com LICENSE file: clean up 2019-11-22 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader f2c9ce1e08ed..c8d4d4254ca1 (15 commits) 2019-11-22 jvanverth@google.com Add checks for TextureSampler count in text GeoProcs. 2019-11-22 ethannicholas@google.com SkSL now supports functions defined in sksl_gpu.inc 2019-11-22 reed@google.com Revert "Reland "Revert "Use flat version of path-direction enum""" 2019-11-22 robertphillips@google.com Add utility for creating test-only GrProgramInfos 2019-11-22 kjlubick@google.com [skolo] Remove internal hardware 2 2019-11-22 reed@google.com remove LEGACY_CONVEXITY code -- no more clients 2019-11-22 michaelludwig@google.com GrQuadPerEdgeAA::Tessellator owns GrVertexWriter 2019-11-22 michaelludwig@google.com Move textureop fallback code out of GrRTC and into AddTextureSetOps 2019-11-22 rmistry@google.com [G3 compile bot] Add comment to CL when there is an interesting failure 2019-11-22 mtklein@google.com rough unit test support for FM 2019-11-22 jvanverth@google.com Rename GLRTFBOIDIs0 to WrapsSwapchainSurface and use for Metal. 2019-11-22 nigeltao@google.com Optimize SkWuffsCodec's sk_bzero calls 2019-11-22 robertphillips@google.com Update bezier_*_effects GMs to not use rand 2019-11-22 halcanary@google.com Documentation: Cq-Include-Trybots 2019-11-22 benjaminwagner@google.com Add Docker-based GCC Test tasks 2019-11-22 kjlubick@google.com [canvaskit] Add SkContourMeasure 2019-11-22 reed@google.com Reland "Revert "Use flat version of path-direction enum"" Created with: gclient setdep -r src/third_party/skia@47af12aa8331 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 1577 ++++++++++++++++-------------- sky/packages/sky_engine/LICENSE | 865 +++++++++------- 3 files changed, 1370 insertions(+), 1074 deletions(-) diff --git a/DEPS b/DEPS index f2bec7fb5697e..233e0c96116f8 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '078e8faa26d8b1f33a92e57f587be011150d1776', + 'skia_revision': '47af12aa83318a48ba27f4ac426e94cbffc6a7c3', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 435eee8cbf4b6..36c43676c5e02 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 68d412de34ec10138518fbcb15922cc7 +Signature: f911c0b222e97db1213161265b556a8a UNUSED LICENSES: @@ -806,15 +806,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -831,341 +833,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skcms -LIBRARY: skia LIBRARY: vulkanmemoryallocator -ORIGIN: ../../../third_party/skia/bench/ClearBench.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/include/third_party/skcms/skcms.h + ../../../third_party/skia/include/third_party/skcms/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/bench/ClearBench.cpp -FILE: ../../../third_party/skia/bench/CompositingImagesBench.cpp -FILE: ../../../third_party/skia/bench/CubicMapBench.cpp -FILE: ../../../third_party/skia/bench/GrCCFillGeometryBench.cpp -FILE: ../../../third_party/skia/bench/ImageCycleBench.cpp -FILE: ../../../third_party/skia/bench/JSONBench.cpp -FILE: ../../../third_party/skia/bench/PathOpsBench.cpp -FILE: ../../../third_party/skia/bench/PolyUtilsBench.cpp -FILE: ../../../third_party/skia/bench/ShaderMaskFilterBench.cpp -FILE: ../../../third_party/skia/bench/TypefaceBench.cpp -FILE: ../../../third_party/skia/experimental/pvg/draw_msg.proto -FILE: ../../../third_party/skia/fuzz/FuzzCommon.cpp -FILE: ../../../third_party/skia/fuzz/FuzzPathMeasure.cpp -FILE: ../../../third_party/skia/fuzz/FuzzRegionOp.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzImageFilterDeserialize.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzPathDeserialize.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzRegionDeserialize.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzRegionSetPath.cpp -FILE: ../../../third_party/skia/gm/analytic_gradients.cpp -FILE: ../../../third_party/skia/gm/androidblendmodes.cpp -FILE: ../../../third_party/skia/gm/b_119394958.cpp -FILE: ../../../third_party/skia/gm/clockwise.cpp -FILE: ../../../third_party/skia/gm/crbug_847759.cpp -FILE: ../../../third_party/skia/gm/crbug_884166.cpp -FILE: ../../../third_party/skia/gm/crbug_887103.cpp -FILE: ../../../third_party/skia/gm/crbug_892988.cpp -FILE: ../../../third_party/skia/gm/crbug_899512.cpp -FILE: ../../../third_party/skia/gm/crbug_905548.cpp -FILE: ../../../third_party/skia/gm/daa.cpp -FILE: ../../../third_party/skia/gm/drawimageset.cpp -FILE: ../../../third_party/skia/gm/drawquadset.cpp -FILE: ../../../third_party/skia/gm/fontregen.cpp -FILE: ../../../third_party/skia/gm/fwidth_squircle.cpp -FILE: ../../../third_party/skia/gm/gradients_degenerate.cpp -FILE: ../../../third_party/skia/gm/hugepath.cpp -FILE: ../../../third_party/skia/gm/localmatrixshader.cpp -FILE: ../../../third_party/skia/gm/make_raster_image.cpp -FILE: ../../../third_party/skia/gm/mandoline.cpp -FILE: ../../../third_party/skia/gm/orientation.cpp -FILE: ../../../third_party/skia/gm/p3.cpp -FILE: ../../../third_party/skia/gm/pathmeasure.cpp -FILE: ../../../third_party/skia/gm/perspimages.cpp -FILE: ../../../third_party/skia/gm/polygonoffset.cpp -FILE: ../../../third_party/skia/gm/scaledemoji.cpp -FILE: ../../../third_party/skia/gm/scaledemoji_rendering.cpp -FILE: ../../../third_party/skia/gm/shadermaskfilter.cpp -FILE: ../../../third_party/skia/gm/sharedcorners.cpp -FILE: ../../../third_party/skia/gm/skinning.cpp -FILE: ../../../third_party/skia/gm/trickycubicstrokes.cpp -FILE: ../../../third_party/skia/gm/unpremul.cpp -FILE: ../../../third_party/skia/gm/wacky_yuv_formats.cpp -FILE: ../../../third_party/skia/include/android/SkAnimatedImage.h -FILE: ../../../third_party/skia/include/c/sk_colorspace.h -FILE: ../../../third_party/skia/include/c/sk_imageinfo.h -FILE: ../../../third_party/skia/include/core/SkCanvasVirtualEnforcer.h -FILE: ../../../third_party/skia/include/core/SkContourMeasure.h -FILE: ../../../third_party/skia/include/core/SkCoverageMode.h -FILE: ../../../third_party/skia/include/core/SkCubicMap.h -FILE: ../../../third_party/skia/include/core/SkFontMetrics.h -FILE: ../../../third_party/skia/include/core/SkFontParameters.h -FILE: ../../../third_party/skia/include/core/SkFontTypes.h -FILE: ../../../third_party/skia/include/core/SkYUVAIndex.h -FILE: ../../../third_party/skia/include/effects/SkOpPathEffect.h -FILE: ../../../third_party/skia/include/effects/SkShaderMaskFilter.h -FILE: ../../../third_party/skia/include/effects/SkTrimPathEffect.h -FILE: ../../../third_party/skia/include/gpu/GrBackendDrawableInfo.h -FILE: ../../../third_party/skia/include/gpu/GrDriverBugWorkarounds.h -FILE: ../../../third_party/skia/include/gpu/vk/GrVkMemoryAllocator.h -FILE: ../../../third_party/skia/include/gpu/vk/GrVkVulkan.h -FILE: ../../../third_party/skia/include/ports/SkFontMgr_fuchsia.h -FILE: ../../../third_party/skia/include/private/GrVkTypesPriv.h -FILE: ../../../third_party/skia/include/private/SkMacros.h -FILE: ../../../third_party/skia/include/private/SkSafe32.h -FILE: ../../../third_party/skia/include/private/SkTo.h FILE: ../../../third_party/skia/include/third_party/skcms/skcms.h -FILE: ../../../third_party/skia/include/utils/Sk3D.h -FILE: ../../../third_party/skia/include/utils/SkAnimCodecPlayer.h -FILE: ../../../third_party/skia/include/utils/SkTextUtils.h -FILE: ../../../third_party/skia/infra/cts/run_testlab.go -FILE: ../../../third_party/skia/modules/skottie/gm/3dgm.cpp -FILE: ../../../third_party/skia/modules/skottie/include/SkottieProperty.h -FILE: ../../../third_party/skia/modules/skottie/src/SkottieAdapter.cpp -FILE: ../../../third_party/skia/modules/skottie/src/SkottieAdapter.h -FILE: ../../../third_party/skia/modules/skottie/src/SkottieJson.cpp -FILE: ../../../third_party/skia/modules/skottie/src/SkottieJson.h -FILE: ../../../third_party/skia/modules/skottie/src/SkottiePriv.h -FILE: ../../../third_party/skia/modules/skottie/src/SkottieProperty.cpp -FILE: ../../../third_party/skia/modules/skottie/src/SkottieTest.cpp -FILE: ../../../third_party/skia/modules/skottie/src/SkottieTool.cpp -FILE: ../../../third_party/skia/modules/skottie/src/layers/PrecompLayer.cpp -FILE: ../../../third_party/skia/modules/skottie/src/layers/ShapeLayer.cpp -FILE: ../../../third_party/skia/modules/skottie/src/layers/TextLayer.cpp -FILE: ../../../third_party/skia/modules/skottie/utils/SkottieUtils.cpp -FILE: ../../../third_party/skia/modules/skottie/utils/SkottieUtils.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGClipEffect.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGColorFilter.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGGeometryTransform.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGGradient.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGImage.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGMaskEffect.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGOpacityEffect.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGPlane.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGRoundEffect.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGScene.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGText.h -FILE: ../../../third_party/skia/modules/sksg/src/SkSGClipEffect.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGColorFilter.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGGeometryTransform.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGGradient.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGImage.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGMaskEffect.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGOpacityEffect.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGPlane.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGRoundEffect.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGScene.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGText.cpp -FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper.cpp -FILE: ../../../third_party/skia/samplecode/SampleAnimatedImage.cpp -FILE: ../../../third_party/skia/samplecode/SampleCusp.cpp -FILE: ../../../third_party/skia/samplecode/SampleFlutterAnimate.cpp -FILE: ../../../third_party/skia/samplecode/SampleGlyphTransform.cpp -FILE: ../../../third_party/skia/src/android/SkAnimatedImage.cpp -FILE: ../../../third_party/skia/src/c/sk_imageinfo.cpp -FILE: ../../../third_party/skia/src/codec/SkEncodedInfo.cpp -FILE: ../../../third_party/skia/src/codec/SkParseEncodedOrigin.cpp -FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.cpp -FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.h -FILE: ../../../third_party/skia/src/core/SkBlurPriv.h -FILE: ../../../third_party/skia/src/core/SkCanvasPriv.cpp -FILE: ../../../third_party/skia/src/core/SkColorSpaceXformSteps.cpp -FILE: ../../../third_party/skia/src/core/SkColorSpaceXformSteps.h -FILE: ../../../third_party/skia/src/core/SkContourMeasure.cpp -FILE: ../../../third_party/skia/src/core/SkCoverageModePriv.h -FILE: ../../../third_party/skia/src/core/SkCubicMap.cpp -FILE: ../../../third_party/skia/src/core/SkCubicSolver.h -FILE: ../../../third_party/skia/src/core/SkDeferredDisplayList.cpp -FILE: ../../../third_party/skia/src/core/SkDeferredDisplayListPriv.h -FILE: ../../../third_party/skia/src/core/SkDraw_text.cpp -FILE: ../../../third_party/skia/src/core/SkFontPriv.h -FILE: ../../../third_party/skia/src/core/SkGlyph.cpp -FILE: ../../../third_party/skia/src/core/SkIPoint16.h -FILE: ../../../third_party/skia/src/core/SkMaskFilterBase.h -FILE: ../../../third_party/skia/src/core/SkPath_serial.cpp -FILE: ../../../third_party/skia/src/core/SkPicturePriv.h -FILE: ../../../third_party/skia/src/core/SkRRectPriv.h -FILE: ../../../third_party/skia/src/core/SkRectPriv.h -FILE: ../../../third_party/skia/src/core/SkRemoteGlyphCache.cpp -FILE: ../../../third_party/skia/src/core/SkRemoteGlyphCache.h -FILE: ../../../third_party/skia/src/core/SkSafeRange.h -FILE: ../../../third_party/skia/src/core/SkSpan.h -FILE: ../../../third_party/skia/src/core/SkStrikeCache.cpp -FILE: ../../../third_party/skia/src/core/SkSurfaceCharacterization.cpp -FILE: ../../../third_party/skia/src/core/SkTextBlobPriv.h -FILE: ../../../third_party/skia/src/core/SkTypeface_remote.cpp -FILE: ../../../third_party/skia/src/core/SkTypeface_remote.h -FILE: ../../../third_party/skia/src/core/SkYUVASizeInfo.cpp -FILE: ../../../third_party/skia/src/effects/SkOpPE.h -FILE: ../../../third_party/skia/src/effects/SkOpPathEffect.cpp -FILE: ../../../third_party/skia/src/effects/SkShaderMaskFilter.cpp -FILE: ../../../third_party/skia/src/effects/SkTrimPE.h -FILE: ../../../third_party/skia/src/effects/SkTrimPathEffect.cpp -FILE: ../../../third_party/skia/src/gpu/GrContextThreadSafeProxyPriv.h -FILE: ../../../third_party/skia/src/gpu/GrDDLContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrDriverBugWorkarounds.cpp -FILE: ../../../third_party/skia/src/gpu/GrFPArgs.h -FILE: ../../../third_party/skia/src/gpu/GrLegacyDirectContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.cpp -FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.h -FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxyPriv.h -FILE: ../../../third_party/skia/src/gpu/GrResourceProviderPriv.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCDrawPathsOp.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCDrawPathsOp.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathCache.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathCache.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerFlushResources.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerFlushResources.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerOpsTaskPaths.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCSTLList.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStrokeGeometry.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStrokeGeometry.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStroker.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStroker.h -FILE: ../../../third_party/skia/src/gpu/effects/GrAARectEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrCircleBlurFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrConfigConversionEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrConstColorProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrLumaColorFilterEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrMagnifierEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrPremulInputFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrRRectBlurEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrRectBlurEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrSkSLFP.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrSkSLFP.h -FILE: ../../../third_party/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrYUVtoRGBEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAARectEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAARectEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConfigConversionEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConfigConversionEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConstColorProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConstColorProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrLumaColorFilterEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMagnifierEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMagnifierEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrPremulInputFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrPremulInputFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRRectBlurEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRRectBlurEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRectBlurEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRectBlurEffect.h -FILE: ../../../third_party/skia/src/gpu/geometry/GrQuad.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/GrClampedGradientEffect.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrDualIntervalGradientColorizer.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientBitmapCache.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientBitmapCache.h -FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientShader.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientShader.h -FILE: ../../../third_party/skia/src/gpu/gradients/GrLinearGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrRadialGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrSingleIntervalGradientColorizer.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrSweepGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrTextureGradientColorizer.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrTiledGradientEffect.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrUnrolledBinaryGradientColorizer.fp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrClampedGradientEffect.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrClampedGradientEffect.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrLinearGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrLinearGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrRadialGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrRadialGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSweepGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSweepGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTextureGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTextureGradientColorizer.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTiledGradientEffect.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTiledGradientEffect.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlBuffer.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlBuffer.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCppUtil.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlDepthStencil.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlOpsRenderPass.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineState.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineState.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateBuilder.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateBuilder.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateDataManager.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateDataManager.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlResourceProvider.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlResourceProvider.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSampler.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSampler.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlStencilAttachment.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlStencilAttachment.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlTextureRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlTextureRenderTarget.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUniformHandler.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUniformHandler.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlVaryingHandler.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlVaryingHandler.mm -FILE: ../../../third_party/skia/src/gpu/ops/GrClearStencilClipOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrDebugMarkerOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrDrawableOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrDrawableOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrFillRRectOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrFillRRectOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrFillRectOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrFillRectOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrQuadPerEdgeAA.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrQuadPerEdgeAA.h -FILE: ../../../third_party/skia/src/gpu/ops/GrStrokeRectOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrStrokeRectOp.h -FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.cpp -FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.h -FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.cpp -FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkAMDMemoryAllocator.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkAMDMemoryAllocator.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandPool.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandPool.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageLayout.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkSamplerYcbcrConversion.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkTypesPriv.cpp -FILE: ../../../third_party/skia/src/image/SkImage_GpuBase.cpp -FILE: ../../../third_party/skia/src/image/SkImage_GpuBase.h -FILE: ../../../third_party/skia/src/image/SkImage_GpuYUVA.cpp -FILE: ../../../third_party/skia/src/image/SkImage_GpuYUVA.h -FILE: ../../../third_party/skia/src/image/SkImage_Lazy.h -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts.h -FILE: ../../../third_party/skia/src/opts/SkOpts_hsw.cpp -FILE: ../../../third_party/skia/src/opts/SkRasterPipeline_opts.h -FILE: ../../../third_party/skia/src/pathops/SkPathOpsAsWinding.cpp -FILE: ../../../third_party/skia/src/pathops/SkPathOpsTCurve.h -FILE: ../../../third_party/skia/src/pdf/SkClusterator.cpp -FILE: ../../../third_party/skia/src/pdf/SkClusterator.h -FILE: ../../../third_party/skia/src/pdf/SkPDFTag.cpp -FILE: ../../../third_party/skia/src/pdf/SkPDFTag.h -FILE: ../../../third_party/skia/src/ports/SkFontMgr_fuchsia.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLByteCode.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.h -FILE: ../../../third_party/skia/src/sksl/SkSLPipelineStageCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariableReference.cpp -FILE: ../../../third_party/skia/src/utils/Sk3D.cpp -FILE: ../../../third_party/skia/src/utils/SkAnimCodecPlayer.cpp -FILE: ../../../third_party/skia/src/utils/SkCallableTraits.h -FILE: ../../../third_party/skia/src/utils/SkJSON.cpp -FILE: ../../../third_party/skia/src/utils/SkJSON.h -FILE: ../../../third_party/skia/src/utils/SkTextUtils.cpp -FILE: ../../../third_party/skia/src/utils/mac/SkUniqueCFRef.h -FILE: ../../../third_party/skia/src/utils/win/SkDWriteNTDDI_VERSION.h FILE: ../../../third_party/skia/third_party/skcms/skcms.cc FILE: ../../../third_party/skia/third_party/skcms/skcms_internal.h FILE: ../../../third_party/skia/third_party/skcms/src/Transform_inl.h @@ -1455,6 +1126,8 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json @@ -1576,6 +1249,7 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Cl FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-AcerChromebookR13Convertible-GPU-PowerVRGX6250-arm-Debug-All.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json @@ -1617,7 +1291,6 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_dm.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_get_hashes.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_pull.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_push.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/internal_bot_2.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/internal_bot_5.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json @@ -1667,14 +1340,6 @@ FILE: ../../../third_party/skia/infra/wasm-common/docker/emsdk-base/Dockerfile FILE: ../../../third_party/skia/infra/wasm-common/docker/gold-karma-chrome-tests/Dockerfile FILE: ../../../third_party/skia/infra/wasm-common/docker/karma-chrome-tests/Dockerfile FILE: ../../../third_party/skia/infra/wasm-common/docker/perf-karma-chrome-tests/Dockerfile -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/NotoSerif-Regular.ttf -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/Roboto-Regular.ttf -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/Roboto-Regular.woff -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/example.html -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/extra.html -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/node.example.js -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/package.json -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/test.png FILE: ../../../third_party/skia/modules/canvaskit/cpu.js FILE: ../../../third_party/skia/modules/canvaskit/debug.js FILE: ../../../third_party/skia/modules/canvaskit/externs.js @@ -1714,10 +1379,6 @@ FILE: ../../../third_party/skia/modules/pathkit/externs.js FILE: ../../../third_party/skia/modules/pathkit/helper.js FILE: ../../../third_party/skia/modules/pathkit/karma.bench.conf.js FILE: ../../../third_party/skia/modules/pathkit/karma.conf.js -FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/example.html -FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/package.json -FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/example.html -FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/package.json FILE: ../../../third_party/skia/modules/pathkit/package.json FILE: ../../../third_party/skia/modules/pathkit/perf/effects.bench.js FILE: ../../../third_party/skia/modules/pathkit/perf/path.bench.js @@ -1833,15 +1494,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -2121,15 +1784,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -2366,15 +2031,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -2587,15 +2254,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -2996,15 +2665,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -3228,15 +2899,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -3354,15 +3027,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -3552,21 +3227,389 @@ FILE: ../../../third_party/skia/src/utils/SkShaperJSONWriter.cpp FILE: ../../../third_party/skia/src/utils/SkShaperJSONWriter.h FILE: ../../../third_party/skia/src/utils/win/SkObjBase.h ---------------------------------------------------------------------------------------------------- -Copyright 2019 Google Inc. +Copyright 2019 Google Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + +==================================================================================================== +LIBRARY: skia +ORIGIN: ../../../third_party/skia/bench/ClearBench.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/bench/ClearBench.cpp +FILE: ../../../third_party/skia/bench/CompositingImagesBench.cpp +FILE: ../../../third_party/skia/bench/CubicMapBench.cpp +FILE: ../../../third_party/skia/bench/GrCCFillGeometryBench.cpp +FILE: ../../../third_party/skia/bench/ImageCycleBench.cpp +FILE: ../../../third_party/skia/bench/JSONBench.cpp +FILE: ../../../third_party/skia/bench/PathOpsBench.cpp +FILE: ../../../third_party/skia/bench/PolyUtilsBench.cpp +FILE: ../../../third_party/skia/bench/ShaderMaskFilterBench.cpp +FILE: ../../../third_party/skia/bench/TypefaceBench.cpp +FILE: ../../../third_party/skia/experimental/pvg/draw_msg.proto +FILE: ../../../third_party/skia/fuzz/FuzzCommon.cpp +FILE: ../../../third_party/skia/fuzz/FuzzPathMeasure.cpp +FILE: ../../../third_party/skia/fuzz/FuzzRegionOp.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzImageFilterDeserialize.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzPathDeserialize.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzRegionDeserialize.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzRegionSetPath.cpp +FILE: ../../../third_party/skia/gm/analytic_gradients.cpp +FILE: ../../../third_party/skia/gm/androidblendmodes.cpp +FILE: ../../../third_party/skia/gm/b_119394958.cpp +FILE: ../../../third_party/skia/gm/clockwise.cpp +FILE: ../../../third_party/skia/gm/crbug_847759.cpp +FILE: ../../../third_party/skia/gm/crbug_884166.cpp +FILE: ../../../third_party/skia/gm/crbug_887103.cpp +FILE: ../../../third_party/skia/gm/crbug_892988.cpp +FILE: ../../../third_party/skia/gm/crbug_899512.cpp +FILE: ../../../third_party/skia/gm/crbug_905548.cpp +FILE: ../../../third_party/skia/gm/daa.cpp +FILE: ../../../third_party/skia/gm/drawimageset.cpp +FILE: ../../../third_party/skia/gm/drawquadset.cpp +FILE: ../../../third_party/skia/gm/fontregen.cpp +FILE: ../../../third_party/skia/gm/fwidth_squircle.cpp +FILE: ../../../third_party/skia/gm/gradients_degenerate.cpp +FILE: ../../../third_party/skia/gm/hugepath.cpp +FILE: ../../../third_party/skia/gm/localmatrixshader.cpp +FILE: ../../../third_party/skia/gm/make_raster_image.cpp +FILE: ../../../third_party/skia/gm/mandoline.cpp +FILE: ../../../third_party/skia/gm/orientation.cpp +FILE: ../../../third_party/skia/gm/p3.cpp +FILE: ../../../third_party/skia/gm/pathmeasure.cpp +FILE: ../../../third_party/skia/gm/perspimages.cpp +FILE: ../../../third_party/skia/gm/polygonoffset.cpp +FILE: ../../../third_party/skia/gm/scaledemoji.cpp +FILE: ../../../third_party/skia/gm/scaledemoji_rendering.cpp +FILE: ../../../third_party/skia/gm/shadermaskfilter.cpp +FILE: ../../../third_party/skia/gm/sharedcorners.cpp +FILE: ../../../third_party/skia/gm/skinning.cpp +FILE: ../../../third_party/skia/gm/trickycubicstrokes.cpp +FILE: ../../../third_party/skia/gm/unpremul.cpp +FILE: ../../../third_party/skia/gm/wacky_yuv_formats.cpp +FILE: ../../../third_party/skia/include/android/SkAnimatedImage.h +FILE: ../../../third_party/skia/include/c/sk_colorspace.h +FILE: ../../../third_party/skia/include/c/sk_imageinfo.h +FILE: ../../../third_party/skia/include/core/SkCanvasVirtualEnforcer.h +FILE: ../../../third_party/skia/include/core/SkContourMeasure.h +FILE: ../../../third_party/skia/include/core/SkCoverageMode.h +FILE: ../../../third_party/skia/include/core/SkCubicMap.h +FILE: ../../../third_party/skia/include/core/SkFontMetrics.h +FILE: ../../../third_party/skia/include/core/SkFontParameters.h +FILE: ../../../third_party/skia/include/core/SkFontTypes.h +FILE: ../../../third_party/skia/include/core/SkYUVAIndex.h +FILE: ../../../third_party/skia/include/effects/SkOpPathEffect.h +FILE: ../../../third_party/skia/include/effects/SkShaderMaskFilter.h +FILE: ../../../third_party/skia/include/effects/SkTrimPathEffect.h +FILE: ../../../third_party/skia/include/gpu/GrBackendDrawableInfo.h +FILE: ../../../third_party/skia/include/gpu/GrDriverBugWorkarounds.h +FILE: ../../../third_party/skia/include/gpu/vk/GrVkMemoryAllocator.h +FILE: ../../../third_party/skia/include/gpu/vk/GrVkVulkan.h +FILE: ../../../third_party/skia/include/ports/SkFontMgr_fuchsia.h +FILE: ../../../third_party/skia/include/private/GrVkTypesPriv.h +FILE: ../../../third_party/skia/include/private/SkMacros.h +FILE: ../../../third_party/skia/include/private/SkSafe32.h +FILE: ../../../third_party/skia/include/private/SkTo.h +FILE: ../../../third_party/skia/include/utils/Sk3D.h +FILE: ../../../third_party/skia/include/utils/SkAnimCodecPlayer.h +FILE: ../../../third_party/skia/include/utils/SkTextUtils.h +FILE: ../../../third_party/skia/infra/cts/run_testlab.go +FILE: ../../../third_party/skia/modules/skottie/gm/3dgm.cpp +FILE: ../../../third_party/skia/modules/skottie/include/SkottieProperty.h +FILE: ../../../third_party/skia/modules/skottie/src/SkottieAdapter.cpp +FILE: ../../../third_party/skia/modules/skottie/src/SkottieAdapter.h +FILE: ../../../third_party/skia/modules/skottie/src/SkottieJson.cpp +FILE: ../../../third_party/skia/modules/skottie/src/SkottieJson.h +FILE: ../../../third_party/skia/modules/skottie/src/SkottiePriv.h +FILE: ../../../third_party/skia/modules/skottie/src/SkottieProperty.cpp +FILE: ../../../third_party/skia/modules/skottie/src/SkottieTest.cpp +FILE: ../../../third_party/skia/modules/skottie/src/SkottieTool.cpp +FILE: ../../../third_party/skia/modules/skottie/src/layers/PrecompLayer.cpp +FILE: ../../../third_party/skia/modules/skottie/src/layers/ShapeLayer.cpp +FILE: ../../../third_party/skia/modules/skottie/src/layers/TextLayer.cpp +FILE: ../../../third_party/skia/modules/skottie/utils/SkottieUtils.cpp +FILE: ../../../third_party/skia/modules/skottie/utils/SkottieUtils.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGClipEffect.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGColorFilter.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGGeometryTransform.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGGradient.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGImage.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGMaskEffect.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGOpacityEffect.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGPlane.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGRoundEffect.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGScene.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGText.h +FILE: ../../../third_party/skia/modules/sksg/src/SkSGClipEffect.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGColorFilter.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGGeometryTransform.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGGradient.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGImage.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGMaskEffect.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGOpacityEffect.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGPlane.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGRoundEffect.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGScene.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGText.cpp +FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper.cpp +FILE: ../../../third_party/skia/samplecode/SampleAnimatedImage.cpp +FILE: ../../../third_party/skia/samplecode/SampleCusp.cpp +FILE: ../../../third_party/skia/samplecode/SampleFlutterAnimate.cpp +FILE: ../../../third_party/skia/samplecode/SampleGlyphTransform.cpp +FILE: ../../../third_party/skia/src/android/SkAnimatedImage.cpp +FILE: ../../../third_party/skia/src/c/sk_imageinfo.cpp +FILE: ../../../third_party/skia/src/codec/SkEncodedInfo.cpp +FILE: ../../../third_party/skia/src/codec/SkParseEncodedOrigin.cpp +FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.cpp +FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.h +FILE: ../../../third_party/skia/src/core/SkBlurPriv.h +FILE: ../../../third_party/skia/src/core/SkCanvasPriv.cpp +FILE: ../../../third_party/skia/src/core/SkColorSpaceXformSteps.cpp +FILE: ../../../third_party/skia/src/core/SkColorSpaceXformSteps.h +FILE: ../../../third_party/skia/src/core/SkContourMeasure.cpp +FILE: ../../../third_party/skia/src/core/SkCoverageModePriv.h +FILE: ../../../third_party/skia/src/core/SkCubicMap.cpp +FILE: ../../../third_party/skia/src/core/SkCubicSolver.h +FILE: ../../../third_party/skia/src/core/SkDeferredDisplayList.cpp +FILE: ../../../third_party/skia/src/core/SkDeferredDisplayListPriv.h +FILE: ../../../third_party/skia/src/core/SkDraw_text.cpp +FILE: ../../../third_party/skia/src/core/SkFontPriv.h +FILE: ../../../third_party/skia/src/core/SkGlyph.cpp +FILE: ../../../third_party/skia/src/core/SkIPoint16.h +FILE: ../../../third_party/skia/src/core/SkMaskFilterBase.h +FILE: ../../../third_party/skia/src/core/SkPath_serial.cpp +FILE: ../../../third_party/skia/src/core/SkPicturePriv.h +FILE: ../../../third_party/skia/src/core/SkRRectPriv.h +FILE: ../../../third_party/skia/src/core/SkRectPriv.h +FILE: ../../../third_party/skia/src/core/SkRemoteGlyphCache.cpp +FILE: ../../../third_party/skia/src/core/SkRemoteGlyphCache.h +FILE: ../../../third_party/skia/src/core/SkSafeRange.h +FILE: ../../../third_party/skia/src/core/SkSpan.h +FILE: ../../../third_party/skia/src/core/SkStrikeCache.cpp +FILE: ../../../third_party/skia/src/core/SkSurfaceCharacterization.cpp +FILE: ../../../third_party/skia/src/core/SkTextBlobPriv.h +FILE: ../../../third_party/skia/src/core/SkTypeface_remote.cpp +FILE: ../../../third_party/skia/src/core/SkTypeface_remote.h +FILE: ../../../third_party/skia/src/core/SkYUVASizeInfo.cpp +FILE: ../../../third_party/skia/src/effects/SkOpPE.h +FILE: ../../../third_party/skia/src/effects/SkOpPathEffect.cpp +FILE: ../../../third_party/skia/src/effects/SkShaderMaskFilter.cpp +FILE: ../../../third_party/skia/src/effects/SkTrimPE.h +FILE: ../../../third_party/skia/src/effects/SkTrimPathEffect.cpp +FILE: ../../../third_party/skia/src/gpu/GrContextThreadSafeProxyPriv.h +FILE: ../../../third_party/skia/src/gpu/GrDDLContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrDriverBugWorkarounds.cpp +FILE: ../../../third_party/skia/src/gpu/GrFPArgs.h +FILE: ../../../third_party/skia/src/gpu/GrLegacyDirectContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.cpp +FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.h +FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxyPriv.h +FILE: ../../../third_party/skia/src/gpu/GrResourceProviderPriv.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCDrawPathsOp.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCDrawPathsOp.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathCache.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathCache.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerFlushResources.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerFlushResources.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerOpsTaskPaths.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCSTLList.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStrokeGeometry.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStrokeGeometry.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStroker.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStroker.h +FILE: ../../../third_party/skia/src/gpu/effects/GrAARectEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrCircleBlurFragmentProcessor.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrConfigConversionEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrConstColorProcessor.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrLumaColorFilterEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrMagnifierEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrPremulInputFragmentProcessor.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrRRectBlurEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrRectBlurEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrSkSLFP.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrSkSLFP.h +FILE: ../../../third_party/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrYUVtoRGBEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAARectEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAARectEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConfigConversionEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConfigConversionEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConstColorProcessor.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConstColorProcessor.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrLumaColorFilterEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMagnifierEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMagnifierEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrPremulInputFragmentProcessor.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrPremulInputFragmentProcessor.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRRectBlurEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRRectBlurEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRectBlurEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRectBlurEffect.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrQuad.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/GrClampedGradientEffect.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrDualIntervalGradientColorizer.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientBitmapCache.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientBitmapCache.h +FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientShader.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientShader.h +FILE: ../../../third_party/skia/src/gpu/gradients/GrLinearGradientLayout.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrRadialGradientLayout.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrSingleIntervalGradientColorizer.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrSweepGradientLayout.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrTextureGradientColorizer.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrTiledGradientEffect.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp +FILE: ../../../third_party/skia/src/gpu/gradients/GrUnrolledBinaryGradientColorizer.fp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrClampedGradientEffect.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrClampedGradientEffect.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrLinearGradientLayout.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrLinearGradientLayout.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrRadialGradientLayout.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrRadialGradientLayout.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSweepGradientLayout.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSweepGradientLayout.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTextureGradientColorizer.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTextureGradientColorizer.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTiledGradientEffect.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTiledGradientEffect.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp +FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlBuffer.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlBuffer.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCppUtil.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlDepthStencil.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlOpsRenderPass.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineState.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineState.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateBuilder.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateBuilder.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateDataManager.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineStateDataManager.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlResourceProvider.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlResourceProvider.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSampler.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSampler.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlStencilAttachment.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlStencilAttachment.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlTextureRenderTarget.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlTextureRenderTarget.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUniformHandler.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUniformHandler.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlVaryingHandler.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlVaryingHandler.mm +FILE: ../../../third_party/skia/src/gpu/ops/GrClearStencilClipOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrDebugMarkerOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrDrawableOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrDrawableOp.h +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRRectOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRRectOp.h +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRectOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRectOp.h +FILE: ../../../third_party/skia/src/gpu/ops/GrQuadPerEdgeAA.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrQuadPerEdgeAA.h +FILE: ../../../third_party/skia/src/gpu/ops/GrStrokeRectOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrStrokeRectOp.h +FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.cpp +FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.h +FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.cpp +FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkAMDMemoryAllocator.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkAMDMemoryAllocator.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandPool.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandPool.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageLayout.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSamplerYcbcrConversion.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkTypesPriv.cpp +FILE: ../../../third_party/skia/src/image/SkImage_GpuBase.cpp +FILE: ../../../third_party/skia/src/image/SkImage_GpuBase.h +FILE: ../../../third_party/skia/src/image/SkImage_GpuYUVA.cpp +FILE: ../../../third_party/skia/src/image/SkImage_GpuYUVA.h +FILE: ../../../third_party/skia/src/image/SkImage_Lazy.h +FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts.h +FILE: ../../../third_party/skia/src/opts/SkOpts_hsw.cpp +FILE: ../../../third_party/skia/src/opts/SkRasterPipeline_opts.h +FILE: ../../../third_party/skia/src/pathops/SkPathOpsAsWinding.cpp +FILE: ../../../third_party/skia/src/pathops/SkPathOpsTCurve.h +FILE: ../../../third_party/skia/src/pdf/SkClusterator.cpp +FILE: ../../../third_party/skia/src/pdf/SkClusterator.h +FILE: ../../../third_party/skia/src/pdf/SkPDFTag.cpp +FILE: ../../../third_party/skia/src/pdf/SkPDFTag.h +FILE: ../../../third_party/skia/src/ports/SkFontMgr_fuchsia.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLByteCode.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.h +FILE: ../../../third_party/skia/src/sksl/SkSLPipelineStageCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariableReference.cpp +FILE: ../../../third_party/skia/src/utils/Sk3D.cpp +FILE: ../../../third_party/skia/src/utils/SkAnimCodecPlayer.cpp +FILE: ../../../third_party/skia/src/utils/SkCallableTraits.h +FILE: ../../../third_party/skia/src/utils/SkJSON.cpp +FILE: ../../../third_party/skia/src/utils/SkJSON.h +FILE: ../../../third_party/skia/src/utils/SkTextUtils.cpp +FILE: ../../../third_party/skia/src/utils/mac/SkUniqueCFRef.h +FILE: ../../../third_party/skia/src/utils/win/SkDWriteNTDDI_VERSION.h +---------------------------------------------------------------------------------------------------- +Copyright 2018 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -3892,15 +3935,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5048,15 +5093,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5101,15 +5148,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5136,15 +5185,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5176,15 +5227,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5230,15 +5283,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5271,15 +5326,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5306,15 +5363,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5488,15 +5547,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5530,15 +5591,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5582,15 +5645,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5624,16 +5689,18 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -5659,15 +5726,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5702,15 +5771,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5740,15 +5811,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5796,15 +5869,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5870,15 +5945,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -5966,11 +6043,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/samplecode/SampleTextureUpload.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/modules/canvaskit/canvaskit/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/samplecode/SampleTextureUpload.cpp +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/NotoSerif-Regular.ttf +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/Roboto-Regular.ttf +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/Roboto-Regular.woff +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/example.html +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/extra.html +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/node.example.js +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/package.json +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/test.png +FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/example.html +FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/package.json +FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/example.html +FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/package.json ---------------------------------------------------------------------------------------------------- -Copyright 2019 Google Inc. and Adobe Inc. +Copyright (c) 2011 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -5999,6 +6087,43 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: skia +ORIGIN: ../../../third_party/skia/samplecode/SampleTextureUpload.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/samplecode/SampleTextureUpload.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2019 Google Inc. and Adobe Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + ==================================================================================================== LIBRARY: skia ORIGIN: ../../../third_party/skia/src/codec/SkCodecPriv.h + ../../../third_party/skia/LICENSE @@ -6014,15 +6139,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6057,15 +6184,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6093,15 +6222,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6138,15 +6269,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6176,15 +6309,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6212,15 +6347,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6247,15 +6384,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6282,15 +6421,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6317,15 +6458,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6353,15 +6496,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6389,15 +6534,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6426,15 +6573,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6463,15 +6612,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6499,15 +6650,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -6568,4 +6721,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -Total license count: 51 +Total license count: 53 diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index 6cb160d4868d9..24ea8b29ffdb3 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -9730,15 +9730,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11127,7 +11129,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skcms -skia vulkanmemoryallocator Copyright 2018 Google Inc. @@ -11166,15 +11167,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11220,21 +11223,55 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia +Copyright (c) 2011 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +skia + Copyright (c) 2014 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11270,15 +11307,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11300,15 +11339,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11331,15 +11372,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11361,15 +11404,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11391,15 +11436,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11421,15 +11468,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11451,15 +11500,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11481,15 +11532,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11511,15 +11564,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11541,15 +11596,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11571,15 +11628,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11602,15 +11661,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11632,15 +11693,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11662,15 +11725,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11692,15 +11757,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11722,15 +11789,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11752,15 +11821,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11782,15 +11853,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11812,15 +11885,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11843,15 +11918,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11873,15 +11950,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11903,15 +11982,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11933,15 +12014,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11963,15 +12046,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -11993,15 +12078,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12023,15 +12110,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12053,15 +12142,49 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +skia + +Copyright 2018 Google Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12083,15 +12206,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12113,15 +12238,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12143,15 +12270,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12173,15 +12302,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12203,15 +12334,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12233,15 +12366,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12263,15 +12398,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12293,15 +12430,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12323,15 +12462,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -12353,15 +12494,17 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT From 584d958688752909734d5ad941b16cff9145d87a Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Fri, 22 Nov 2019 16:29:58 -0800 Subject: [PATCH 239/591] [fuchsia] Capture SkRRect in scene_update_context by value (#13989) This was leading to usage of the captured rect after the end of the lifetime in descrutor of Frame. --- flow/scene_update_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 13d7f666f4107..c61f5670ba7f2 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -124,7 +124,7 @@ class SceneUpdateContext { void AddPaintLayer(Layer* layer); private: - const SkRRect& rrect_; + const SkRRect rrect_; SkColor const color_; std::vector paint_layers_; From a6e890666d228d2ec456a54348ca630fc8dd5e64 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 22 Nov 2019 19:14:41 -0800 Subject: [PATCH 240/591] Roll src/third_party/dart 5b5f34f2e6..603fd7881f (11 commits) (#13991) dart-lang/sdk@603fd7881f (ddc) Fix deps tracking for the ddc sdk. dart-lang/sdk@6294c73f3e Deprecate 'typeSystem' parameter for InheritanceManager3. dart-lang/sdk@f196196779 Add modification counts and instructions to all pages; add an index page dart-lang/sdk@5c111a217c Update package:watcher in SDK dart-lang/sdk@f3102ab0cf [vm] Fix late final field edge case dart-lang/sdk@c58d263729 [analysis_server] Handle directory watcher closed on windows. dart-lang/sdk@a87c563ecb [test_runner] Make update_static_error_tests work on Windows dart-lang/sdk@fc47953a1e [dartfuzz] Adding library methods for TypedData types dart-lang/sdk@72f461b116 Update FunctionExpression return type inference to NNBD. dart-lang/sdk@c9165dc3cd Add 'sdkVersion' field to 'server.log' messages. dart-lang/sdk@e304d34cea Use the corresponding TypeProvider(s) to create legacy / nonNullableByDefault TypeSystem. --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 233e0c96116f8..613e112dbe083 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '5b5f34f2e6e047edbd4280a5d9c906c6399280e8', + 'dart_revision': '603fd7881f3ccc4e4ba0f78d6dedc9172025c36a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -99,7 +99,7 @@ vars = { 'dart_tflite_native_rev': '3c777c40608a2a9f1427bfe0028ab48e7116b4c1', 'dart_typed_data_tag': '1.1.6', 'dart_usage_tag': '3.4.0', - 'dart_watcher_rev': '0.9.7+12-pub', + 'dart_watcher_rev': '0.9.7+13', 'dart_web_socket_channel_tag': '1.0.9', 'dart_yaml_tag': '2.2.0', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 72a278b0b5e58..750d8056dc630 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 0e0f83fa7087cb4e19471dbd2dd2a197 +Signature: d63acce5056fe19ded3a36abc2b1ab6e UNUSED LICENSES: From d1cac77d5a4edb64901341253fe9e756cb94dc34 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 22 Nov 2019 23:03:46 -0500 Subject: [PATCH 241/591] Roll src/third_party/skia 47af12aa8331..c3ff97a98b74 (3 commits) (#13993) https://skia.googlesource.com/skia.git/+log/47af12aa8331..c3ff97a98b74 git log 47af12aa8331..c3ff97a98b74 --date=short --no-merges --format='%ad %ae %s' 2019-11-23 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-23 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-23 bsalomon@google.com Revert "Revert "Make FP optimizations helpers use SkAlphaType not GrColorType"" Created with: gclient setdep -r src/third_party/skia@c3ff97a98b74 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 613e112dbe083..7e7d0f890468d 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '47af12aa83318a48ba27f4ac426e94cbffc6a7c3', + 'skia_revision': 'c3ff97a98b74554854b5822797c6b7b431f4d8d2', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 36c43676c5e02..c1f160aa1c92f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f911c0b222e97db1213161265b556a8a +Signature: d41f84acbe245ec2c36d88299851fa7d UNUSED LICENSES: From 591144dfef5943a90188d0b35be219fb195c8090 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 22 Nov 2019 22:12:58 -0800 Subject: [PATCH 242/591] Roll src/third_party/dart 603fd7881f..16cbabec3a (1 commits) (#13994) dart-lang/sdk@16cbabec3a [dartdevc] Migrating dart:_isolate_helper to NNBD. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7e7d0f890468d..feb04ff794d28 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '603fd7881f3ccc4e4ba0f78d6dedc9172025c36a', + 'dart_revision': '16cbabec3a00e715173c68e8f761a8e1296cc9e5', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 750d8056dc630..870c9f3e75e59 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: d63acce5056fe19ded3a36abc2b1ab6e +Signature: 13a82078efea46e09944716f9ea1949f UNUSED LICENSES: From ed30d77ab9d16706829adc8214d1fe1949696d5d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 22 Nov 2019 16:44:21 -0800 Subject: [PATCH 243/591] Setup a Metal test surface and add a new unit-test target that tests the testing utilities. `//flutter/testing` now contains a lot of utilities used by other test targets. This includes stuff like working with render targets that use either OpenGL or Metal, fixtures for interacting with the Dart VM, test assertion predicates, etc.. However, these utilities themselves are not tested as part of a standalone test suite. Instead, only the test targets that include it exercise these utilities. Since these are no longer trivial, a new test target has been added that tests the testing utilities directly. --- BUILD.gn | 1 + testing/BUILD.gn | 54 +++++++++++ testing/run_tests.py | 2 + testing/test_metal_surface.cc | 46 +++++++++ testing/test_metal_surface.h | 47 ++++++++++ testing/test_metal_surface_impl.h | 39 ++++++++ testing/test_metal_surface_impl.mm | 119 ++++++++++++++++++++++++ testing/test_metal_surface_unittests.cc | 34 +++++++ tools/gn | 5 +- 9 files changed, 343 insertions(+), 4 deletions(-) create mode 100644 testing/test_metal_surface.cc create mode 100644 testing/test_metal_surface.h create mode 100644 testing/test_metal_surface_impl.h create mode 100644 testing/test_metal_surface_impl.mm create mode 100644 testing/test_metal_surface_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index 5fd4f5482f601..361a5c6f0e2f6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -73,6 +73,7 @@ group("flutter") { "$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests", "$flutter_root/shell/platform/embedder:embedder_unittests", "$flutter_root/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests", + "$flutter_root/testing:testing_unittests", "$flutter_root/third_party/txt:txt_unittests", ] diff --git a/testing/BUILD.gn b/testing/BUILD.gn index a8db2229e29f5..52032e0cdaee6 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -2,6 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//flutter/shell/config.gni") +import("//flutter/testing/testing.gni") + source_set("testing_lib") { testonly = true @@ -77,4 +80,55 @@ if (current_toolchain == host_toolchain) { "//third_party/swiftshader_flutter:swiftshader", ] } + + # All targets on all platforms should be able to the Metal utilities. On + # platforms where Metal in not available, the tests must be skipped or + # implemented to use another available client rendering API. This is usually + # either OpenGL which is portably implemented via SwiftShader or the software + # backend. This way, all tests compile on all platforms but the Metal backend + # exercised on platforms where Metal itself is available. + source_set("metal") { + testonly = true + + sources = [ + "$flutter_root/testing/test_metal_surface.cc", + "$flutter_root/testing/test_metal_surface.h", + ] + + defines = [] + + if (shell_enable_metal) { + sources += [ "$flutter_root/testing/test_metal_surface_impl.mm" ] + defines += [ "TESTING_ENABLE_METAL" ] + } + + deps = [ + ":skia", + "$flutter_root/fml", + ] + } +} + +test_fixtures("testing_fixtures") { + fixtures = [] +} + +# The //flutter/testing library provides utility methods to other test targets. +# This test target tests the testing utilities. +executable("testing_unittests") { + testonly = true + + sources = [ + "$flutter_root/testing/test_metal_surface_unittests.cc", + ] + + deps = [ + ":dart", + ":metal", + ":opengl", + ":skia", + ":testing", + ":testing_fixtures", + ":testing_lib", + ] } diff --git a/testing/run_tests.py b/testing/run_tests.py index d345bb887c0ac..f32e86398d247 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -108,6 +108,8 @@ def RunCCTests(build_dir, filter): RunEngineExecutable(build_dir, 'ui_unittests', filter, shuffle_flags) + RunEngineExecutable(build_dir, 'testing_unittests', filter, shuffle_flags) + # These unit-tests are Objective-C and can only run on Darwin. if IsMac(): RunEngineExecutable(build_dir, 'flutter_channels_unittests', filter, shuffle_flags) diff --git a/testing/test_metal_surface.cc b/testing/test_metal_surface.cc new file mode 100644 index 0000000000000..d108681e0fcba --- /dev/null +++ b/testing/test_metal_surface.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/test_metal_surface.h" + +#if TESTING_ENABLE_METAL +#include "flutter/testing/test_metal_surface_impl.h" +#endif // TESTING_ENABLE_METAL + +namespace flutter { + +bool TestMetalSurface::PlatformSupportsMetal() { +#if TESTING_ENABLE_METAL + return true; +#else // TESTING_ENABLE_METAL + return false; +#endif // TESTING_ENABLE_METAL +} + +std::unique_ptr TestMetalSurface::Create( + SkISize surface_size) { +#if TESTING_ENABLE_METAL + return std::make_unique(surface_size); +#else // TESTING_ENABLE_METAL + return nullptr; +#endif // TESTING_ENABLE_METAL +} + +TestMetalSurface::TestMetalSurface() = default; + +TestMetalSurface::~TestMetalSurface() = default; + +bool TestMetalSurface::IsValid() const { + return impl_ ? impl_->IsValid() : false; +} + +sk_sp TestMetalSurface::GetGrContext() const { + return impl_ ? impl_->GetGrContext() : nullptr; +} + +sk_sp TestMetalSurface::GetSurface() const { + return impl_ ? impl_->GetSurface() : nullptr; +} + +} // namespace flutter diff --git a/testing/test_metal_surface.h b/testing/test_metal_surface.h new file mode 100644 index 0000000000000..e21ad4601cb10 --- /dev/null +++ b/testing/test_metal_surface.h @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_TESTING_TEST_METAL_SURFACE_H_ +#define FLUTTER_TESTING_TEST_METAL_SURFACE_H_ + +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrContext.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// @brief Creates a MTLTexture backed SkSurface and context that can be +/// used to render to in unit-tests. +/// +class TestMetalSurface { + public: + static bool PlatformSupportsMetal(); + + static std::unique_ptr Create( + SkISize surface_size = SkISize::MakeEmpty()); + + virtual ~TestMetalSurface(); + + virtual bool IsValid() const; + + virtual sk_sp GetGrContext() const; + + virtual sk_sp GetSurface() const; + + protected: + TestMetalSurface(); + + private: + std::unique_ptr impl_; + + TestMetalSurface(std::unique_ptr impl); + + FML_DISALLOW_COPY_AND_ASSIGN(TestMetalSurface); +}; + +} // namespace flutter + +#endif // FLUTTER_TESTING_TEST_METAL_SURFACE_H_ diff --git a/testing/test_metal_surface_impl.h b/testing/test_metal_surface_impl.h new file mode 100644 index 0000000000000..c92eceac291c2 --- /dev/null +++ b/testing/test_metal_surface_impl.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_TESTING_TEST_METAL_SURFACE_IMPL_H_ +#define FLUTTER_TESTING_TEST_METAL_SURFACE_IMPL_H_ + +#include "flutter/fml/macros.h" +#include "flutter/testing/test_metal_surface.h" + +namespace flutter { + +class TestMetalSurfaceImpl : public TestMetalSurface { + public: + TestMetalSurfaceImpl(SkISize surface_size); + + // |TestMetalSurface| + ~TestMetalSurfaceImpl() override; + + private: + bool is_valid_ = false; + sk_sp context_; + sk_sp surface_; + + // |TestMetalSurface| + bool IsValid() const override; + + // |TestMetalSurface| + sk_sp GetGrContext() const override; + + // |TestMetalSurface| + sk_sp GetSurface() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(TestMetalSurfaceImpl); +}; + +} // namespace flutter + +#endif // FLUTTER_TESTING_TEST_METAL_SURFACE_IMPL_H_ diff --git a/testing/test_metal_surface_impl.mm b/testing/test_metal_surface_impl.mm new file mode 100644 index 0000000000000..679a4c0fa36f0 --- /dev/null +++ b/testing/test_metal_surface_impl.mm @@ -0,0 +1,119 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/test_metal_surface_impl.h" + +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "third_party/skia/include/core/SkSurface.h" + +namespace flutter { + +TestMetalSurfaceImpl::TestMetalSurfaceImpl(SkISize surface_size) { + if (surface_size.isEmpty()) { + FML_LOG(ERROR) << "Size of test Metal surface was empty."; + return; + } + + auto device = fml::scoped_nsobject{[MTLCreateSystemDefaultDevice() retain]}; + if (!device) { + FML_LOG(ERROR) << "Could not acquire Metal device."; + return; + } + + auto command_queue = fml::scoped_nsobject{[device.get() newCommandQueue]}; + if (!command_queue) { + FML_LOG(ERROR) << "Could not create the default command queue."; + return; + } + + auto texture_descriptor = fml::scoped_nsobject{[[MTLTextureDescriptor + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:surface_size.width() + height:surface_size.height() + mipmapped:NO] retain]}; + + // The most pessimistic option and disables all optimizations but allows tests + // the most flexible access to the surface. They may read and wrote to the + // surface from shaders or use as a pixel view. + texture_descriptor.get().usage = MTLTextureUsageUnknown; + + if (!texture_descriptor) { + FML_LOG(ERROR) << "Invalid texture descriptor."; + return; + } + + auto texture = fml::scoped_nsobject{ + [device.get() newTextureWithDescriptor:texture_descriptor.get()]}; + + if (!texture) { + FML_LOG(ERROR) << "Could not create texture from texture descriptor."; + return; + } + + auto skia_context = GrContext::MakeMetal(device.get(), command_queue.get()); + + if (skia_context) { + // Skia wants ownership of the device and queue. If a context was created, + // we now no longer own the argument. Release the arguments only on + // successful creation of the context. + FML_ALLOW_UNUSED_LOCAL(device.release()); + FML_ALLOW_UNUSED_LOCAL(command_queue.release()); + } else { + FML_LOG(ERROR) << "Could not create the GrContext from the Metal Device " + "and command queue."; + return; + } + + GrMtlTextureInfo skia_texture_info; + skia_texture_info.fTexture = sk_cf_obj{[texture.get() retain]}; + + auto backend_render_target = GrBackendRenderTarget{ + surface_size.width(), // width + surface_size.height(), // height + 1, // sample count + skia_texture_info // texture info + }; + + auto surface = SkSurface::MakeFromBackendRenderTarget( + skia_context.get(), // context + backend_render_target, // backend render target + kTopLeft_GrSurfaceOrigin, // surface origin + kBGRA_8888_SkColorType, // color type + nullptr, // color space + nullptr, // surface properties + nullptr, // release proc (texture is already ref counted in sk_cf_obj) + nullptr // release context + ); + + if (!surface) { + FML_LOG(ERROR) << "Could not create Skia surface from a Metal texture."; + return; + } + + surface_ = std::move(surface); + context_ = std::move(skia_context); + + is_valid_ = true; +} + +// |TestMetalSurface| +TestMetalSurfaceImpl::~TestMetalSurfaceImpl() = default; + +// |TestMetalSurface| +bool TestMetalSurfaceImpl::IsValid() const { + return is_valid_; +} +// |TestMetalSurface| +sk_sp TestMetalSurfaceImpl::GetGrContext() const { + return IsValid() ? context_ : nullptr; +} +// |TestMetalSurface| +sk_sp TestMetalSurfaceImpl::GetSurface() const { + return IsValid() ? surface_ : nullptr; +} + +} // namespace flutter diff --git a/testing/test_metal_surface_unittests.cc b/testing/test_metal_surface_unittests.cc new file mode 100644 index 0000000000000..5765e6e656cd6 --- /dev/null +++ b/testing/test_metal_surface_unittests.cc @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/test_metal_surface.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +TEST(TestMetalSurface, EmptySurfaceIsInvalid) { + if (!TestMetalSurface::PlatformSupportsMetal()) { + GTEST_SKIP(); + } + + auto surface = TestMetalSurface::Create(); + ASSERT_NE(surface, nullptr); + ASSERT_FALSE(surface->IsValid()); +} + +TEST(TestMetalSurface, CanCreateValidTestMetalSurface) { + if (!TestMetalSurface::PlatformSupportsMetal()) { + GTEST_SKIP(); + } + + auto surface = TestMetalSurface::Create(SkISize::Make(100, 100)); + ASSERT_NE(surface, nullptr); + ASSERT_TRUE(surface->IsValid()); + ASSERT_NE(surface->GetSurface(), nullptr); + ASSERT_NE(surface->GetGrContext(), nullptr); +} + +} // namespace testing +} // namespace flutter diff --git a/tools/gn b/tools/gn index fb19768e18bdd..5975392a8a025 100755 --- a/tools/gn +++ b/tools/gn @@ -44,7 +44,7 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') - if args.enable_metal: + if args.enable_metal and args.target_os == 'ios': target_dir.append('metal') return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) @@ -75,9 +75,6 @@ def to_gn_args(args): if args.target_os != 'android' and args.enable_vulkan: raise Exception('--enable-vulkan is only supported on Android') - if args.target_os != 'ios' and args.enable_metal: - raise Exception('--enable-metal is only supported on iOS') - runtime_mode = args.runtime_mode gn_args = {} From b6b54fd60631a3828c2e2c9b079b5d1d2d8c8c37 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 22 Nov 2019 17:45:38 -0800 Subject: [PATCH 244/591] PR --- testing/BUILD.gn | 46 +++++++++++++++--------------- testing/test_metal_surface_impl.mm | 20 ++++++------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/testing/BUILD.gn b/testing/BUILD.gn index 52032e0cdaee6..a1028b0f18952 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -81,12 +81,12 @@ if (current_toolchain == host_toolchain) { ] } - # All targets on all platforms should be able to the Metal utilities. On - # platforms where Metal in not available, the tests must be skipped or + # All targets on all platforms should be able to use the Metal utilities. On + # platforms where Metal is not available, the tests must be skipped or # implemented to use another available client rendering API. This is usually # either OpenGL which is portably implemented via SwiftShader or the software # backend. This way, all tests compile on all platforms but the Metal backend - # exercised on platforms where Metal itself is available. + # is exercised on platforms where Metal itself is available. source_set("metal") { testonly = true @@ -107,28 +107,28 @@ if (current_toolchain == host_toolchain) { "$flutter_root/fml", ] } -} -test_fixtures("testing_fixtures") { - fixtures = [] -} + test_fixtures("testing_fixtures") { + fixtures = [] + } -# The //flutter/testing library provides utility methods to other test targets. -# This test target tests the testing utilities. -executable("testing_unittests") { - testonly = true + # The //flutter/testing library provides utility methods to other test targets. + # This test target tests the testing utilities. + executable("testing_unittests") { + testonly = true - sources = [ - "$flutter_root/testing/test_metal_surface_unittests.cc", - ] + sources = [ + "$flutter_root/testing/test_metal_surface_unittests.cc", + ] - deps = [ - ":dart", - ":metal", - ":opengl", - ":skia", - ":testing", - ":testing_fixtures", - ":testing_lib", - ] + deps = [ + ":dart", + ":metal", + ":opengl", + ":skia", + ":testing", + ":testing_fixtures", + ":testing_lib", + ] + } } diff --git a/testing/test_metal_surface_impl.mm b/testing/test_metal_surface_impl.mm index 679a4c0fa36f0..9f34530e0ab59 100644 --- a/testing/test_metal_surface_impl.mm +++ b/testing/test_metal_surface_impl.mm @@ -30,14 +30,14 @@ return; } - auto texture_descriptor = fml::scoped_nsobject{[[MTLTextureDescriptor - texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm - width:surface_size.width() - height:surface_size.height() - mipmapped:NO] retain]}; + auto texture_descriptor = fml::scoped_nsobject{ + [[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:surface_size.width() + height:surface_size.height() + mipmapped:NO] retain]}; // The most pessimistic option and disables all optimizations but allows tests - // the most flexible access to the surface. They may read and wrote to the + // the most flexible access to the surface. They may read and write to the // surface from shaders or use as a pixel view. texture_descriptor.get().usage = MTLTextureUsageUnknown; @@ -46,8 +46,8 @@ return; } - auto texture = fml::scoped_nsobject{ - [device.get() newTextureWithDescriptor:texture_descriptor.get()]}; + auto texture = + fml::scoped_nsobject{[device.get() newTextureWithDescriptor:texture_descriptor.get()]}; if (!texture) { FML_LOG(ERROR) << "Could not create texture from texture descriptor."; @@ -85,8 +85,8 @@ kBGRA_8888_SkColorType, // color type nullptr, // color space nullptr, // surface properties - nullptr, // release proc (texture is already ref counted in sk_cf_obj) - nullptr // release context + nullptr, // release proc (texture is already ref counted in sk_cf_obj) + nullptr // release context ); if (!surface) { From 8b29922baee8af8091b4255c36bfd8ce8158d48e Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 25 Nov 2019 09:33:30 -0800 Subject: [PATCH 245/591] Roll src/third_party/dart 16cbabec3a..d5fa596d74 (2 commits) (#14001) dart-lang/sdk@d5fa596d74 [vm/compiler] ARM64: Extend usage of PushPair. dart-lang/sdk@709b2aa33d Adding a tool for using DWARF information to convert stack traces. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index feb04ff794d28..85508c8c4ee64 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '16cbabec3a00e715173c68e8f761a8e1296cc9e5', + 'dart_revision': 'd5fa596d748d31512a91ce07b73a7c8cc61bd098', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 870c9f3e75e59..e9573dd5cd5b6 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 13a82078efea46e09944716f9ea1949f +Signature: 5ccba447cd6246f10cd3a3b519d0ef7d UNUSED LICENSES: From 994ce4519d54da191f4198c1a62b50cf35786611 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 25 Nov 2019 09:38:36 -0800 Subject: [PATCH 246/591] Removed auto roll commits from auto_assign. (#13992) --- .github/auto_assign.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index ef0e0d89db31b..5f1411b07cadd 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -36,5 +36,5 @@ numberOfReviewers: 1 # numberOfAssignees: 2 # A list of keywords to be skipped the process that add reviewers if pull requests include it -# skipKeywords: -# - wip +skipKeywords: + - Roll From b0d2caceab254961e4542aef81a077fecd08b9be Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 25 Nov 2019 13:25:13 -0500 Subject: [PATCH 247/591] Roll fuchsia/sdk/core/mac-amd64 from Q9HKI... to 1nxSz... (#13995) Roll fuchsia/sdk/core/mac-amd64 from Q9HKI... to 1nxSz... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 85508c8c4ee64..5ba39074bcf14 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'Q9HKIe4uSBsOGG_MNyOuqsVV6ZLQl3IuwiI4ThsH4JQC' + 'version': '1nxSz1QqLbQZnQOtHW1T-g5BCbDWuM9lK7AJK92M3vsC' } ], 'condition': 'host_os == "mac"', From c89ac6347b7e88a562ebc826a5ee9a07c7b4cab7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 25 Nov 2019 13:27:53 -0500 Subject: [PATCH 248/591] Roll fuchsia/sdk/core/linux-amd64 from _7JyV... to hK-BD... (#13997) Roll fuchsia/sdk/core/linux-amd64 from _7JyV... to hK-BD... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 5ba39074bcf14..19df31778942c 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': '_7JyV941rE763PwZ7OoX0N79LxUFM0v_4DLGTdctL98C' + 'version': 'hK-BDfkZM0WodvMhp8AgZTZjg71JkcNSeB3zIIVArXQC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 8a435f8a47e6c..ca1ca01d3054d 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 9a9cbde37b9b7c7eb2d83a5194692dd2 +Signature: 7a0361087fcc8ddbe4707acaefe438ea UNUSED LICENSES: From 2e7fb50cf05c115c8af405e67549bdf53d7ad97c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 25 Nov 2019 13:30:54 -0500 Subject: [PATCH 249/591] Roll src/third_party/skia c3ff97a98b74..50299de39976 (1 commits) (#13998) https://skia.googlesource.com/skia.git/+log/c3ff97a98b74..50299de39976 git log c3ff97a98b74..50299de39976 --date=short --no-merges --format='%ad %ae %s' 2019-11-23 robertphillips@google.com Reland "Revert "Make FP optimizations helpers use SkAlphaType not GrColorType"" Created with: gclient setdep -r src/third_party/skia@50299de39976 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 19df31778942c..1338295eec099 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c3ff97a98b74554854b5822797c6b7b431f4d8d2', + 'skia_revision': '50299de39976ab7aff91f5867fe05429135baae6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index c1f160aa1c92f..39303efe9a710 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: d41f84acbe245ec2c36d88299851fa7d +Signature: ec3b6c1721f9bb264779b28ba79af0be UNUSED LICENSES: From 388f814d0e46cf6b3ecbec8581b0813138353ad7 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Mon, 25 Nov 2019 14:03:16 -0500 Subject: [PATCH 250/591] Use new SkPathDirection enum, previous one is deprecated (#14005) --- lib/ui/painting/path.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/ui/painting/path.cc b/lib/ui/painting/path.cc index facd8d1ce3899..a670c79fc5928 100644 --- a/lib/ui/painting/path.cc +++ b/lib/ui/painting/path.cc @@ -156,9 +156,8 @@ void CanvasPath::arcToPoint(float arcEndX, bool isClockwiseDirection) { const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize : SkPath::ArcSize::kSmall_ArcSize; - const auto direction = isClockwiseDirection - ? SkPath::Direction::kCW_Direction - : SkPath::Direction::kCCW_Direction; + const auto direction = + isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW; path_.arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, arcEndX, arcEndY); @@ -173,9 +172,8 @@ void CanvasPath::relativeArcToPoint(float arcEndDeltaX, bool isClockwiseDirection) { const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize : SkPath::ArcSize::kSmall_ArcSize; - const auto direction = isClockwiseDirection - ? SkPath::Direction::kCW_Direction - : SkPath::Direction::kCCW_Direction; + const auto direction = + isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW; path_.rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, arcEndDeltaX, arcEndDeltaY); } From 298e053bdc047ec54f9361c4aae0e17b09b3b730 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 25 Nov 2019 11:07:13 -0800 Subject: [PATCH 251/591] Add support for FontLoader API for the web (#13999) --- .../lib/src/engine/text/font_collection.dart | 20 ++++++++ lib/web_ui/lib/src/ui/text.dart | 5 +- lib/web_ui/test/text/font_loading_test.dart | 49 +++++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 lib/web_ui/test/text/font_loading_test.dart diff --git a/lib/web_ui/lib/src/engine/text/font_collection.dart b/lib/web_ui/lib/src/engine/text/font_collection.dart index e33b9c33858dd..258c1fc253fd0 100644 --- a/lib/web_ui/lib/src/engine/text/font_collection.dart +++ b/lib/web_ui/lib/src/engine/text/font_collection.dart @@ -73,6 +73,10 @@ class FontCollection { } } + Future loadFontFromList(Uint8List list, {String fontFamily}) { + return _assetFontManager._loadFontFaceBytes(fontFamily, list); + } + /// Registers fonts that are used by tests. void debugRegisterTestFonts() { _testFontManager = FontManager(); @@ -189,6 +193,22 @@ class FontManager { } } + // Loads a font from bytes, surfacing errors through the future. + Future _loadFontFaceBytes(String family, Uint8List list) { + // Since these fonts are loaded by user code, surface the error + // through the returned future. + final html.FontFace fontFace = html.FontFace(family, list); + return fontFace.load().then((_) { + html.document.fonts.add(fontFace); + }, onError: (dynamic exception) { + // Failures here will throw an html.DomException which confusingly + // does not implement Exception or Error. Rethrow an Exception so it can + // be caught in user code without depending on dart:html or requiring a + // catch block without "on". + throw Exception(exception.toString()); + }); + } + /// Returns a [Future] that completes when all fonts that have been /// registered with this font manager have been loaded and are ready to use. Future ensureFontsLoaded() { diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index c3dc8edf3137f..b85ef0d14e03b 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1437,8 +1437,5 @@ abstract class ParagraphBuilder { /// * `fontFamily`: The family name used to identify the font in text styles. /// If this is not provided, then the family name will be extracted from the font file. Future loadFontFromList(Uint8List list, {String fontFamily}) { - if (engine.assertionsEnabled) { - throw UnsupportedError('loadFontFromList is not supported.'); - } - return Future.value(null); + return _fontCollection.loadFontFromList(list, fontFamily: fontFamily); } diff --git a/lib/web_ui/test/text/font_loading_test.dart b/lib/web_ui/test/text/font_loading_test.dart new file mode 100644 index 0000000000000..a2d5945c0cde1 --- /dev/null +++ b/lib/web_ui/test/text/font_loading_test.dart @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; + +Future main() async { + await ui.webOnlyInitializeTestDomRenderer(); + group('loadFontFromList', () { + const String _testFontUrl = 'packages/ui/assets/ahem.ttf'; + + tearDown(() { + html.document.fonts.clear(); + }); + + test('surfaces error from invalid font buffer', () async { + await expectLater( + ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'), + throwsA(TypeMatcher())); + }); + + test('loads Blehm font from buffer', () async { + expect(_containsFontFamily('Blehm'), false); + + final html.HttpRequest response = await html.HttpRequest.request( + _testFontUrl, + responseType: 'arraybuffer'); + await ui.loadFontFromList(Uint8List.view(response.response), + fontFamily: 'Blehm'); + + expect(_containsFontFamily('Blehm'), true); + }); + }); +} + +bool _containsFontFamily(String family) { + bool found = false; + html.document.fonts.forEach((html.FontFace fontFace, + html.FontFace fontFaceAgain, html.FontFaceSet fontFaceSet) { + if (fontFace.family == family) { + found = true; + } + }); + return found; +} From 02a2bb829ac65235767403e4cdc218e0fcdc8993 Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Mon, 25 Nov 2019 12:33:23 -0800 Subject: [PATCH 252/591] revert accidental change to MultipePlatformViewsTest (#13481) --- .../ios/Scenarios/ScenariosUITests/PlatformViewUITests.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index 6a8df42ab758e..72c3c460de96f 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -35,9 +35,6 @@ - (instancetype)initWithInvocation:(NSInvocation*)invocation { } - (void)testPlatformView { - //[self checkGolden]; - [[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome]; - [self.application activate]; [self checkGolden]; } From c2d451f660557dea5315aeec7e41651226bc72fd Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 25 Nov 2019 13:14:50 -0800 Subject: [PATCH 253/591] Roll src/third_party/dart d5fa596d74..134e0e28cd (9 commits) (#14006) dart-lang/sdk@134e0e28cd [dartdevc] Break dart:_debugger dependency on dart:html dart-lang/sdk@629d4ce095 [vm] Test for late fields with complicated initializers dart-lang/sdk@7ceceda72c Fix typo in snapshot_profiling.md. heapsnaphsot -> heapsnapshot dart-lang/sdk@d45438e793 Add support for selecting text in completion snippets dart-lang/sdk@18f23124ad [cfe] Print nullability on types in messages for opt-in libraries. dart-lang/sdk@e84af9b761 [cfe] Disallow annotations on type parameters of function types dart-lang/sdk@2b56db6e5d [dart2js,ddc] Enable late lowering in dart2js and ddc dart-lang/sdk@cb6ab6eaa2 [cfe] Handle untyped initializing formals for late fields dart-lang/sdk@4d884229ab [kernel] Add (internal) relink method --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1338295eec099..49d5af6499fc9 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd5fa596d748d31512a91ce07b73a7c8cc61bd098', + 'dart_revision': '134e0e28cda1f8110a69bad34ae70e8123475a80', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index e9573dd5cd5b6..823176fa1a60c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 5ccba447cd6246f10cd3a3b519d0ef7d +Signature: 7f0643ee25a0003b77d518e3bfb5e4df UNUSED LICENSES: From e2aa235ab8f3ceb1d49bbed8ea4db6e48ec60b0d Mon Sep 17 00:00:00 2001 From: David Worsham Date: Mon, 25 Nov 2019 14:16:50 -0800 Subject: [PATCH 254/591] Fix most fml tests on Fuchsia (#14007) * Add fuchsia MessageLoopImpl; fix several tests --- ci/licenses_golden/licenses_flutter | 2 + fml/BUILD.gn | 36 ++++++++++++------ fml/message_loop_impl.cc | 4 ++ fml/platform/fuchsia/message_loop_fuchsia.cc | 38 +++++++++++++++++++ fml/platform/fuchsia/message_loop_fuchsia.h | 36 ++++++++++++++++++ .../flutter/meta/flutter_runner_tests.cmx | 2 +- testing/fuchsia/meta/fuchsia_test.cmx | 2 +- 7 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 fml/platform/fuchsia/message_loop_fuchsia.cc create mode 100644 fml/platform/fuchsia/message_loop_fuchsia.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6df05a1cc4085..cb1e2777b1bd4 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -170,6 +170,8 @@ FILE: ../../../flutter/fml/platform/darwin/scoped_nsobject.mm FILE: ../../../flutter/fml/platform/darwin/string_range_sanitization.h FILE: ../../../flutter/fml/platform/darwin/string_range_sanitization.mm FILE: ../../../flutter/fml/platform/darwin/string_range_sanitization_unittests.mm +FILE: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.cc +FILE: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.h FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc FILE: ../../../flutter/fml/platform/linux/message_loop_linux.cc FILE: ../../../flutter/fml/platform/linux/message_loop_linux.h diff --git a/fml/BUILD.gn b/fml/BUILD.gn index f6eca0f933166..32f58eddf6056 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -164,12 +164,28 @@ source_set("fml") { } if (is_fuchsia) { - sources += [ "platform/fuchsia/paths_fuchsia.cc" ] + sources += [ + "platform/fuchsia/message_loop_fuchsia.cc", + "platform/fuchsia/message_loop_fuchsia.h", + "platform/fuchsia/paths_fuchsia.cc", + ] if (using_fuchsia_sdk) { - public_deps += [ "$fuchsia_sdk_root/pkg:trace" ] + public_deps += [ + "$fuchsia_sdk_root/pkg:async-cpp", + "$fuchsia_sdk_root/pkg:async-loop-cpp", + "$fuchsia_sdk_root/pkg:async-loop-default", + "$fuchsia_sdk_root/pkg:trace", + "$fuchsia_sdk_root/pkg:zx", + ] } else { - public_deps += [ "//zircon/public/lib/trace" ] + public_deps += [ + "//zircon/public/lib/async-cpp", + "//zircon/public/lib/async-loop-cpp", + "//zircon/public/lib/async-loop-default", + "//zircon/public/lib/trace", + "//zircon/public/lib/zx", + ] } } @@ -205,16 +221,21 @@ executable("fml_unittests") { sources = [ "base32_unittest.cc", "command_line_unittest.cc", + "gpu_thread_merger_unittests.cc", "memory/ref_counted_unittest.cc", "memory/weak_ptr_unittest.cc", "message_loop_task_queues_merge_unmerge_unittests.cc", + "message_loop_task_queues_unittests.cc", + "message_loop_unittests.cc", "message_unittests.cc", "paths_unittests.cc", "platform/darwin/string_range_sanitization_unittests.mm", + "synchronization/count_down_latch_unittests.cc", "synchronization/semaphore_unittest.cc", "synchronization/sync_switch_unittest.cc", "synchronization/waitable_event_unittest.cc", "thread_local_unittests.cc", + "thread_unittests.cc", "time/time_delta_unittest.cc", "time/time_point_unittest.cc", "time/time_unittest.cc", @@ -222,14 +243,7 @@ executable("fml_unittests") { # TODO(gw280): Figure out why these tests don't work currently on Fuchsia if (!is_fuchsia) { - sources += [ - "file_unittest.cc", - "gpu_thread_merger_unittests.cc", - "message_loop_task_queues_unittests.cc", - "message_loop_unittests.cc", - "synchronization/count_down_latch_unittests.cc", - "thread_unittests.cc", - ] + sources += [ "file_unittest.cc" ] } deps = [ diff --git a/fml/message_loop_impl.cc b/fml/message_loop_impl.cc index 9e07e11278810..d4c9331e35990 100644 --- a/fml/message_loop_impl.cc +++ b/fml/message_loop_impl.cc @@ -17,6 +17,8 @@ #include "flutter/fml/platform/darwin/message_loop_darwin.h" #elif OS_ANDROID #include "flutter/fml/platform/android/message_loop_android.h" +#elif OS_FUCHSIA +#include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h" #elif OS_LINUX #include "flutter/fml/platform/linux/message_loop_linux.h" #elif OS_WIN @@ -30,6 +32,8 @@ fml::RefPtr MessageLoopImpl::Create() { return fml::MakeRefCounted(); #elif OS_ANDROID return fml::MakeRefCounted(); +#elif OS_FUCHSIA + return fml::MakeRefCounted(); #elif OS_LINUX return fml::MakeRefCounted(); #elif OS_WIN diff --git a/fml/platform/fuchsia/message_loop_fuchsia.cc b/fml/platform/fuchsia/message_loop_fuchsia.cc new file mode 100644 index 0000000000000..506de40ef8974 --- /dev/null +++ b/fml/platform/fuchsia/message_loop_fuchsia.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h" + +#include +#include +#include + +namespace fml { + +MessageLoopFuchsia::MessageLoopFuchsia() + : loop_(&kAsyncLoopConfigAttachToCurrentThread) {} + +MessageLoopFuchsia::~MessageLoopFuchsia() = default; + +void MessageLoopFuchsia::Run() { + loop_.Run(); +} + +void MessageLoopFuchsia::Terminate() { + loop_.Quit(); +} + +void MessageLoopFuchsia::WakeUp(fml::TimePoint time_point) { + fml::TimePoint now = fml::TimePoint::Now(); + zx::duration due_time{0}; + if (time_point > now) { + due_time = zx::nsec((time_point - now).ToNanoseconds()); + } + + FML_DCHECK(async::PostDelayedTask( + loop_.dispatcher(), [this]() { RunExpiredTasksNow(); }, + due_time) == ZX_OK); +} + +} // namespace fml diff --git a/fml/platform/fuchsia/message_loop_fuchsia.h b/fml/platform/fuchsia/message_loop_fuchsia.h new file mode 100644 index 0000000000000..f54c587c4a2b3 --- /dev/null +++ b/fml/platform/fuchsia/message_loop_fuchsia.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_PLATFORM_FUCHSIA_MESSAGE_LOOP_FUCHSIA_H_ +#define FLUTTER_FML_PLATFORM_FUCHSIA_MESSAGE_LOOP_FUCHSIA_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/message_loop_impl.h" + +namespace fml { + +class MessageLoopFuchsia : public MessageLoopImpl { + private: + MessageLoopFuchsia(); + + ~MessageLoopFuchsia() override; + + void Run() override; + + void Terminate() override; + + void WakeUp(fml::TimePoint time_point) override; + + async::Loop loop_; + + FML_FRIEND_MAKE_REF_COUNTED(MessageLoopFuchsia); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(MessageLoopFuchsia); + FML_DISALLOW_COPY_AND_ASSIGN(MessageLoopFuchsia); +}; + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_FUCHSIA_MESSAGE_LOOP_FUCHSIA_H_ diff --git a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx index 015acc94b4e47..ea275dd650b1b 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx @@ -10,7 +10,7 @@ "services": [ "fuchsia.accessibility.semantics.SemanticsManager", "fuchsia.intl.PropertyProvider", - "fuchsia.sys.Launcher" + "fuchsia.process.Launcher" ] } } diff --git a/testing/fuchsia/meta/fuchsia_test.cmx b/testing/fuchsia/meta/fuchsia_test.cmx index fedcb77867acb..1eb7a22e5ef28 100644 --- a/testing/fuchsia/meta/fuchsia_test.cmx +++ b/testing/fuchsia/meta/fuchsia_test.cmx @@ -9,7 +9,7 @@ ], "services": [ "fuchsia.accessibility.semantics.SemanticsManager", - "fuchsia.sys.Launcher" + "fuchsia.process.Launcher" ] } } From c7ec5bbc1cc0fe2521237e923cc6633261d8232b Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 25 Nov 2019 14:44:57 -0800 Subject: [PATCH 255/591] Convert images to raster on the GPU thread for Image.toByteData (#13647) If the image is a cross-context image that might be read from the GPU thread during onscreen rendering, then it is not safe to read it concurrently from the IO thread as part of Image.toByteData. If the GPU thread does not have a graphics context, then fall back to converting the image on the IO thread. Fixes https://github.com/flutter/flutter/issues/30697 --- lib/ui/painting/image_encoding.cc | 161 +++++++++++++++++++----------- lib/ui/snapshot_delegate.h | 2 + shell/common/rasterizer.cc | 38 ++++++- shell/common/rasterizer.h | 7 ++ 4 files changed, 143 insertions(+), 65 deletions(-) diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index 86bb45bfd33be..cef92a7e2451a 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -51,33 +51,18 @@ void InvokeDataCallback(std::unique_ptr callback, } } -sk_sp ConvertToRasterImageIfNecessary(sk_sp image, - GrContext* context) { - SkPixmap pixmap; - if (image->peekPixels(&pixmap)) { - // This is already a raster image. - return image; - } - - if (sk_sp raster_image = image->makeRasterImage()) { - // The image can be converted to a raster image. - return raster_image; - } - - // Cross-context images do not support makeRasterImage. Convert these images - // by drawing them into a surface. - if (context == nullptr) { - return nullptr; +sk_sp ConvertToRasterUsingResourceContext( + sk_sp image, + GrContext* resource_context) { + sk_sp surface; + SkImageInfo surface_info = SkImageInfo::MakeN32Premul(image->dimensions()); + if (resource_context) { + surface = SkSurface::MakeRenderTarget(resource_context, SkBudgeted::kNo, + surface_info); + } else { + surface = SkSurface::MakeRaster(surface_info); } - TRACE_EVENT0("flutter", __FUNCTION__); - - // Create a GPU surface with the context and then do a device to host copy of - // image contents. - auto surface = SkSurface::MakeRenderTarget( - context, SkBudgeted::kNo, - SkImageInfo::MakeN32Premul(image->dimensions())); - if (surface == nullptr || surface->getCanvas() == nullptr) { FML_LOG(ERROR) << "Could not create a surface to copy the texture into."; return nullptr; @@ -96,6 +81,64 @@ sk_sp ConvertToRasterImageIfNecessary(sk_sp image, return snapshot->makeRasterImage(); } +void ConvertImageToRaster(sk_sp image, + std::function)> encode_task, + fml::RefPtr gpu_task_runner, + fml::RefPtr io_task_runner, + GrContext* resource_context, + fml::WeakPtr snapshot_delegate) { + // Check validity of the image. + if (image == nullptr) { + FML_LOG(ERROR) << "Image was null."; + encode_task(nullptr); + return; + } + + auto dimensions = image->dimensions(); + + if (dimensions.isEmpty()) { + FML_LOG(ERROR) << "Image dimensions were empty."; + encode_task(nullptr); + return; + } + + SkPixmap pixmap; + if (image->peekPixels(&pixmap)) { + // This is already a raster image. + encode_task(image); + return; + } + + if (sk_sp raster_image = image->makeRasterImage()) { + // The image can be converted to a raster image. + encode_task(raster_image); + return; + } + + // Cross-context images do not support makeRasterImage. Convert these images + // by drawing them into a surface. This must be done on the GPU thread + // to prevent concurrent usage of the image on both the IO and GPU threads. + gpu_task_runner->PostTask([image, encode_task = std::move(encode_task), + resource_context, snapshot_delegate, + io_task_runner]() { + sk_sp 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 { + 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); + } + encode_task(raster_image); + }); + }); +} + sk_sp CopyImageByteData(sk_sp raster_image, SkColorType color_type) { FML_DCHECK(raster_image); @@ -132,28 +175,10 @@ sk_sp CopyImageByteData(sk_sp raster_image, return SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize()); } -sk_sp EncodeImage(sk_sp p_image, - GrContext* context, - ImageByteFormat format) { +sk_sp EncodeImage(sk_sp raster_image, ImageByteFormat format) { TRACE_EVENT0("flutter", __FUNCTION__); - // Check validity of the image. - if (p_image == nullptr) { - FML_LOG(ERROR) << "Image was null."; - return nullptr; - } - - auto dimensions = p_image->dimensions(); - - if (dimensions.isEmpty()) { - FML_LOG(ERROR) << "Image dimensions were empty."; - return nullptr; - } - - auto raster_image = ConvertToRasterImageIfNecessary(p_image, context); - - if (raster_image == nullptr) { - FML_LOG(ERROR) << "Could not create a raster copy of the image."; + if (!raster_image) { return nullptr; } @@ -165,7 +190,7 @@ sk_sp EncodeImage(sk_sp p_image, if (png_image == nullptr) { FML_LOG(ERROR) << "Could not convert raster image to PNG."; return nullptr; - } + }; return png_image; } break; case kRawRGBA: { @@ -181,17 +206,29 @@ sk_sp EncodeImage(sk_sp p_image, } void EncodeImageAndInvokeDataCallback( - std::unique_ptr callback, sk_sp image, - GrContext* context, + std::unique_ptr callback, + ImageByteFormat format, fml::RefPtr ui_task_runner, - ImageByteFormat format) { - sk_sp encoded = EncodeImage(std::move(image), context, format); - - ui_task_runner->PostTask( - fml::MakeCopyable([callback = std::move(callback), encoded]() mutable { + fml::RefPtr gpu_task_runner, + fml::RefPtr io_task_runner, + GrContext* resource_context, + fml::WeakPtr snapshot_delegate) { + auto callback_task = fml::MakeCopyable( + [callback = std::move(callback)](sk_sp encoded) mutable { InvokeDataCallback(std::move(callback), std::move(encoded)); - })); + }); + + auto encode_task = [callback_task = std::move(callback_task), format, + ui_task_runner](sk_sp raster_image) { + sk_sp encoded = EncodeImage(std::move(raster_image), format); + ui_task_runner->PostTask( + [callback_task = std::move(callback_task), + encoded = std::move(encoded)] { callback_task(encoded); }); + }; + + ConvertImageToRaster(std::move(image), encode_task, gpu_task_runner, + io_task_runner, resource_context, snapshot_delegate); } } // namespace @@ -214,13 +251,17 @@ Dart_Handle EncodeImage(CanvasImage* canvas_image, task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable( [callback = std::move(callback), image = canvas_image->image(), + image_format, ui_task_runner = task_runners.GetUITaskRunner(), + gpu_task_runner = task_runners.GetGPUTaskRunner(), + io_task_runner = task_runners.GetIOTaskRunner(), io_manager = UIDartState::Current()->GetIOManager(), - ui_task_runner = task_runners.GetUITaskRunner(), - image_format]() mutable { - EncodeImageAndInvokeDataCallback(std::move(callback), std::move(image), - io_manager->GetResourceContext().get(), - std::move(ui_task_runner), - image_format); + snapshot_delegate = + UIDartState::Current()->GetSnapshotDelegate()]() mutable { + EncodeImageAndInvokeDataCallback( + std::move(image), std::move(callback), image_format, + std::move(ui_task_runner), std::move(gpu_task_runner), + std::move(io_task_runner), io_manager->GetResourceContext().get(), + std::move(snapshot_delegate)); })); return Dart_Null(); diff --git a/lib/ui/snapshot_delegate.h b/lib/ui/snapshot_delegate.h index 9a771f1219cd8..ad9b8ef1f3612 100644 --- a/lib/ui/snapshot_delegate.h +++ b/lib/ui/snapshot_delegate.h @@ -14,6 +14,8 @@ class SnapshotDelegate { public: virtual sk_sp MakeRasterSnapshot(sk_sp picture, SkISize picture_size) = 0; + + virtual sk_sp ConvertToRasterImage(sk_sp image) = 0; }; } // namespace flutter diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 9fbb6949d6dfb..11fdd3f6ee6ad 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -151,13 +151,14 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { } } -sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, - SkISize picture_size) { +sk_sp Rasterizer::DoMakeRasterSnapshot( + SkISize size, + std::function draw_callback) { TRACE_EVENT0("flutter", __FUNCTION__); sk_sp surface; SkImageInfo image_info = SkImageInfo::MakeN32Premul( - picture_size.width(), picture_size.height(), SkColorSpace::MakeSRGB()); + size.width(), size.height(), SkColorSpace::MakeSRGB()); if (surface_ == nullptr || surface_->GetContext() == nullptr) { // Raster surface is fine if there is no on screen surface. This might // happen in case of software rendering. @@ -179,8 +180,7 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, return nullptr; } - surface->getCanvas()->drawPicture(picture.get()); - + draw_callback(surface->getCanvas()); surface->getCanvas()->flush(); sk_sp device_snapshot; @@ -203,6 +203,34 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, return nullptr; } +sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, + SkISize picture_size) { + return DoMakeRasterSnapshot(picture_size, + [picture = std::move(picture)](SkCanvas* canvas) { + canvas->drawPicture(picture); + }); +} + +sk_sp Rasterizer::ConvertToRasterImage(sk_sp image) { + TRACE_EVENT0("flutter", __FUNCTION__); + + // If the rasterizer does not have a surface with a GrContext, then it will + // be unable to render a cross-context SkImage. The caller will need to + // create the raster image on the IO thread. + if (surface_ == nullptr || surface_->GetContext() == nullptr) { + return nullptr; + } + + if (image == nullptr) { + return nullptr; + } + + return DoMakeRasterSnapshot(image->dimensions(), + [image = std::move(image)](SkCanvas* canvas) { + canvas->drawImage(image, 0, 0); + }); +} + RasterStatus Rasterizer::DoDraw( std::unique_ptr layer_tree) { FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 44c7c7a759a89..d2242648d522b 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -426,6 +426,13 @@ class Rasterizer final : public SnapshotDelegate { sk_sp MakeRasterSnapshot(sk_sp picture, SkISize picture_size) override; + // |SnapshotDelegate| + sk_sp ConvertToRasterImage(sk_sp image) override; + + sk_sp DoMakeRasterSnapshot( + SkISize size, + std::function draw_callback); + RasterStatus DoDraw(std::unique_ptr layer_tree); RasterStatus DrawToSurface(flutter::LayerTree& layer_tree); From 05f8c8b24b923d5c044b0cc409c936be28e4039a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 25 Nov 2019 17:48:37 -0500 Subject: [PATCH 256/591] Roll src/third_party/skia 50299de39976..8fa469d3bcd6 (21 commits) (#14009) https://skia.googlesource.com/skia.git/+log/50299de39976..8fa469d3bcd6 git log 50299de39976..8fa469d3bcd6 --date=short --first-parent --format='%ad %ae %s' 2019-11-25 michaelludwig@google.com Lift TextureSampler's proxy to SurfaceProxy 2019-11-25 csmartdalton@google.com Reland "Reland "Enable msaa ccpr on vulkan"" 2019-11-25 jlavrova@google.com Ellipsis again 2019-11-25 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-25 fmalita@chromium.org [CanvasKit] Add SkResourceCache helpers 2019-11-25 brianosman@google.com Extracted Skottie's resource provider to a separate module 2019-11-25 herb@google.com Fix chrome build bots 2019-11-25 herb@google.com Structured binding for SkZip and SkEnumerate 2019-11-25 herb@google.com Simplify SubRun structure in GrTextBlob 2019-11-25 benjaminwagner@google.com Fix asset_utils when no service_account_json 2019-11-25 bsalomon@google.com Reland "Revert "Revert "Make FP optimizations helpers use SkAlphaType not GrColorType""" 2019-11-25 robertphillips@google.com Add code path that avoids large indexBuffer draws 2019-11-25 fmalita@chromium.org [sksg] Temporarily inline Path fillType accessors 2019-11-25 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms 68d3f3a95f1b..ef3043bd8110 (1 commits) 2019-11-25 michaelludwig@google.com Use just GrSurfaceProxy in pipeline management. 2019-11-25 michaelludwig@google.com Lock down GrQuad ctors, avoid resetting Ws when known to be 1.0 2019-11-25 fmalita@chromium.org [skottie] Use seekFrame in skottie2movie 2019-11-25 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 4c7db77e0185..e9b68f332a30 (5 commits) 2019-11-25 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 137e8082047a..0ec8ef3c9f4f (1040 commits) 2019-11-25 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader c8d4d4254ca1..d44d61514749 (4 commits) 2019-11-24 benjaminwagner@google.com Remove Debian9 GCC jobs and related code Created with: gclient setdep -r src/third_party/skia@8fa469d3bcd6 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/DEPS b/DEPS index 49d5af6499fc9..68f6badf9adec 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '50299de39976ab7aff91f5867fe05429135baae6', + 'skia_revision': '8fa469d3bcd68ec95207558a3e9d7af95991cf4b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 39303efe9a710..56f7d7a4dca7f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ec3b6c1721f9bb264779b28ba79af0be +Signature: 44ae6068dc41be876d6b25233fd384cf UNUSED LICENSES: @@ -1012,7 +1012,6 @@ FILE: ../../../third_party/skia/infra/bots/assets/linux_vulkan_sdk/VERSION FILE: ../../../third_party/skia/infra/bots/assets/lottie-samples/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mesa_intel_driver_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mesa_intel_driver_linux/mesa-driver-builder/Dockerfile -FILE: ../../../third_party/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/moltenvk/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mskp/VERSION FILE: ../../../third_party/skia/infra/bots/assets/node/VERSION @@ -1055,6 +1054,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromecast.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm64-Release-Android_Wuffs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86-devrel-Android_SKQP.json @@ -1067,6 +1067,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-CMake.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json @@ -1080,11 +1081,6 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Debug-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-CanvasKit_CPU.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-Shared.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm-Debug-iOS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-iOS.json @@ -1094,7 +1090,9 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Release-MoltenVK_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Shared.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json @@ -1135,7 +1133,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json @@ -1194,7 +1192,7 @@ FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Debian9-Cl FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json @@ -1228,8 +1226,8 @@ FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10- FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/skqp_test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json -FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-GCC-x86_64-Release-Flutter_Android.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json @@ -1267,12 +1265,11 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-C FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json @@ -2959,6 +2956,8 @@ FILE: ../../../third_party/skia/modules/particles/src/SkParticleBinding.cpp FILE: ../../../third_party/skia/modules/particles/src/SkParticleDrawable.cpp FILE: ../../../third_party/skia/modules/particles/src/SkParticleEffect.cpp FILE: ../../../third_party/skia/modules/particles/src/SkReflected.cpp +FILE: ../../../third_party/skia/modules/skresources/include/SkResources.h +FILE: ../../../third_party/skia/modules/skresources/src/SkResources.cpp FILE: ../../../third_party/skia/samplecode/SampleBackdropBounds.cpp FILE: ../../../third_party/skia/samplecode/SampleImageFilterDAG.cpp FILE: ../../../third_party/skia/src/core/SkColorFilterPriv.h From c0db9aa11ffa737c12f152ec59aecc60741cbf7d Mon Sep 17 00:00:00 2001 From: Francisco Magdaleno Date: Mon, 25 Nov 2019 14:55:12 -0800 Subject: [PATCH 257/591] Remove device independent mask (#14010) --- .../darwin/macos/framework/Source/FlutterViewController.mm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 2ea2070a1f737..8492d902ff793 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -522,14 +522,12 @@ - (void)keyUp:(NSEvent*)event { } - (void)flagsChanged:(NSEvent*)event { - NSUInteger currentlyPressedFlags = - event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; - if (currentlyPressedFlags < _keyboardState.previously_pressed_flags) { + if (event.modifierFlags < _keyboardState.previously_pressed_flags) { [self keyUp:event]; } else { [self keyDown:event]; } - _keyboardState.previously_pressed_flags = currentlyPressedFlags; + _keyboardState.previously_pressed_flags = event.modifierFlags; } - (void)mouseEntered:(NSEvent*)event { From 9f6401362d814eac9b35f88ddcaeac104e68b348 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 25 Nov 2019 15:38:41 -0800 Subject: [PATCH 258/591] Don't crash but warn when the scene builder specifies no layers. (#14008) --- flow/layers/layer_tree.cc | 12 +++++ shell/platform/embedder/fixtures/main.dart | 20 +++++++ .../embedder/tests/embedder_unittests.cc | 52 +++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index b031ebf8cb8bd..f0e37c9bed565 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -27,6 +27,12 @@ void LayerTree::RecordBuildTime(fml::TimePoint start) { void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) { TRACE_EVENT0("flutter", "LayerTree::Preroll"); + + if (!root_layer_) { + FML_LOG(ERROR) << "The scene did not specify any layers."; + return; + } + SkColorSpace* color_space = frame.canvas() ? frame.canvas()->imageInfo().colorSpace() : nullptr; frame.context().raster_cache().SetCheckboardCacheImages( @@ -75,6 +81,12 @@ void LayerTree::UpdateScene(SceneUpdateContext& context, void LayerTree::Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) const { TRACE_EVENT0("flutter", "LayerTree::Paint"); + + if (!root_layer_) { + FML_LOG(ERROR) << "The scene did not specify any layers to paint."; + return; + } + SkISize canvas_size = frame.canvas()->getBaseLayerSize(); SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); internal_nodes_canvas.addCanvas(frame.canvas()); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 5e8b0e2c6dc3f..d7dd20d7295ee 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -600,3 +600,23 @@ void platform_view_mutators_with_pixel_ratio() { }; window.scheduleFrame(); } + +@pragma('vm:entry-point') +void empty_scene() { + window.onBeginFrame = (Duration duration) { + window.render(SceneBuilder().build()); + signalNativeTest(); + }; + window.scheduleFrame(); +} + +@pragma('vm:entry-point') +void scene_with_no_container() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); + window.render(builder.build()); + signalNativeTest(); + }; + window.scheduleFrame(); +} diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index fddca5e997bce..47f86c281e454 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -3499,5 +3499,57 @@ TEST_F(EmbedderTest, latch.Wait(); } +TEST_F(EmbedderTest, EmptySceneIsAcceptable) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("empty_scene"); + fml::AutoResetWaitableEvent latch; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); + + auto engine = builder.LaunchEngine(); + + ASSERT_TRUE(engine.is_valid()); + + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + latch.Wait(); +} + +TEST_F(EmbedderTest, SceneWithNoRootContainerIsAcceptable) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("scene_with_no_container"); + fml::AutoResetWaitableEvent latch; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); + + auto engine = builder.LaunchEngine(); + + ASSERT_TRUE(engine.is_valid()); + + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + latch.Wait(); +} + } // namespace testing } // namespace flutter From 201cfae8c7684a88b6c0b52622d520efd14c1597 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Mon, 25 Nov 2019 16:09:56 -0800 Subject: [PATCH 259/591] [vulkan] Bundle vulkan validation layer so files (#13930) Enable vulkan validation for Fuchsia in debug mode as well. --- shell/platform/fuchsia/dart_runner/BUILD.gn | 2 +- shell/platform/fuchsia/flutter/BUILD.gn | 53 ++++++++++++++-- tools/fuchsia/fuchsia_archive.gni | 2 +- .../{common_libs.gni => fuchsia_libs.gni} | 63 +++++++++++++++++++ tools/gn | 7 +++ vulkan/config.gni | 12 ++++ vulkan/vulkan_utilities.cc | 4 +- 7 files changed, 135 insertions(+), 8 deletions(-) rename tools/fuchsia/{common_libs.gni => fuchsia_libs.gni} (50%) create mode 100644 vulkan/config.gni diff --git a/shell/platform/fuchsia/dart_runner/BUILD.gn b/shell/platform/fuchsia/dart_runner/BUILD.gn index 041ad0a8b48d3..735a5dbfab716 100644 --- a/shell/platform/fuchsia/dart_runner/BUILD.gn +++ b/shell/platform/fuchsia/dart_runner/BUILD.gn @@ -6,9 +6,9 @@ assert(is_fuchsia) import("//build/fuchsia/sdk.gni") import("$flutter_root/common/fuchsia_config.gni") -import("$flutter_root/tools/fuchsia/common_libs.gni") import("$flutter_root/tools/fuchsia/dart.gni") import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") +import("$flutter_root/tools/fuchsia/fuchsia_libs.gni") template("runner") { assert(defined(invoker.product), "The parameter 'product' must be defined") diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 4b3855bba9955..0549a1f75352c 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -8,9 +8,10 @@ import("//build/fuchsia/sdk.gni") import("$flutter_root/common/config.gni") import("$flutter_root/shell/gpu/gpu.gni") import("$flutter_root/testing/testing.gni") -import("$flutter_root/tools/fuchsia/common_libs.gni") import("$flutter_root/tools/fuchsia/dart.gni") import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") +import("$flutter_root/tools/fuchsia/fuchsia_libs.gni") +import("$flutter_root/vulkan/config.gni") import("engine_flutter_runner.gni") shell_gpu_configuration("fuchsia_gpu_configuration") { @@ -38,8 +39,14 @@ if (!using_fuchsia_sdk) { flutter_runner("jit") { output_name = "flutter_jit_runner" product = false + + extra_defines = [] if (flutter_runtime_mode == "profile") { - extra_defines = [ "FLUTTER_PROFILE" ] + extra_defines += [ "FLUTTER_PROFILE" ] + } + + if (enable_vulkan_validation_layers) { + extra_defines += [ "VULKAN_VALIDATION_LAYERS_ENABLED" ] } extra_deps = [ @@ -51,8 +58,13 @@ flutter_runner("jit") { flutter_runner("jit_product") { output_name = "flutter_jit_product_runner" product = true + extra_defines = [ "DART_PRODUCT" ] + if (enable_vulkan_validation_layers) { + extra_defines += [ "VULKAN_VALIDATION_LAYERS_ENABLED" ] + } + extra_deps = [ "//third_party/dart/runtime:libdart_jit_product", "//third_party/dart/runtime/platform:libdart_platform_jit_product", @@ -62,9 +74,16 @@ flutter_runner("jit_product") { flutter_runner("aot") { output_name = "flutter_aot_runner" product = false + + extra_defines = [] if (flutter_runtime_mode == "profile") { - extra_defines = [ "FLUTTER_PROFILE" ] + extra_defines += [ "FLUTTER_PROFILE" ] } + + if (enable_vulkan_validation_layers) { + extra_defines += [ "VULKAN_VALIDATION_LAYERS_ENABLED" ] + } + extra_deps = [ "//third_party/dart/runtime:libdart_precompiled_runtime", "//third_party/dart/runtime/platform:libdart_platform_precompiled_runtime", @@ -74,7 +93,13 @@ flutter_runner("aot") { flutter_runner("aot_product") { output_name = "flutter_aot_product_runner" product = true + extra_defines = [ "DART_PRODUCT" ] + + if (enable_vulkan_validation_layers) { + extra_defines += [ "VULKAN_VALIDATION_LAYERS_ENABLED" ] + } + extra_deps = [ "//third_party/dart/runtime:libdart_precompiled_runtime_product", "//third_party/dart/runtime/platform:libdart_platform_precompiled_runtime_product", @@ -157,7 +182,16 @@ template("jit_runner") { }, ] - libraries = common_libs + _vulkan_icds = [] + _libs = common_libs + if (enable_vulkan_validation_layers) { + _libs += vulkan_validation_libs + _vulkan_icds += vulkan_icds + } + + resources += _vulkan_icds + + libraries = _libs meta = [ { @@ -215,7 +249,16 @@ template("aot_runner") { ] } - libraries = common_libs + _vulkan_icds = [] + _libs = common_libs + if (enable_vulkan_validation_layers) { + _libs += vulkan_validation_libs + _vulkan_icds += vulkan_icds + } + + resources += _vulkan_icds + + libraries = _libs meta = [ { diff --git a/tools/fuchsia/fuchsia_archive.gni b/tools/fuchsia/fuchsia_archive.gni index 3d0f0707fc9e5..3b1da4397894c 100644 --- a/tools/fuchsia/fuchsia_archive.gni +++ b/tools/fuchsia/fuchsia_archive.gni @@ -2,8 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("$flutter_root/tools/fuchsia/common_libs.gni") import("$flutter_root/tools/fuchsia/fuchsia_debug_symbols.gni") +import("$flutter_root/tools/fuchsia/fuchsia_libs.gni") # Creates a Fuchsia archive (.far) file using PM from the Fuchsia SDK. template("fuchsia_archive") { diff --git a/tools/fuchsia/common_libs.gni b/tools/fuchsia/fuchsia_libs.gni similarity index 50% rename from tools/fuchsia/common_libs.gni rename to tools/fuchsia/fuchsia_libs.gni index a210961e37a0e..76d2e80a6c072 100644 --- a/tools/fuchsia/common_libs.gni +++ b/tools/fuchsia/fuchsia_libs.gni @@ -69,3 +69,66 @@ common_libs = [ "$clang_base/${clang_manifest_json.md5_beb70f40d525448b39ea87d9f5811e56}") }, ] + +vulkan_dist = "$fuchsia_sdk_base/dist" + +vulkan_validation_libs = [ + { + name = "VkLayer_core_validation.so" + path = rebase_path("$vulkan_dist") + }, + { + name = "VkLayer_khronos_validation.so" + path = rebase_path("$vulkan_dist") + }, + { + name = "VkLayer_object_lifetimes.so" + path = rebase_path("$vulkan_dist") + }, + { + name = "VkLayer_stateless_validation.so" + path = rebase_path("$vulkan_dist") + }, + { + name = "VkLayer_thread_safety.so" + path = rebase_path("$vulkan_dist") + }, + { + name = "VkLayer_unique_objects.so" + path = rebase_path("$vulkan_dist") + }, +] + +vulkan_data_dir = + "//fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d" + +vulkan_icds = [ + { + path = rebase_path("$vulkan_data_dir/VkLayer_core_validation.json") + dest = "vulkan/explicit_layer.d/VkLayer_core_validation.json" + }, + { + path = rebase_path("$vulkan_data_dir/VkLayer_object_lifetimes.json") + dest = "vulkan/explicit_layer.d/VkLayer_object_lifetimes.json" + }, + { + path = rebase_path("$vulkan_data_dir/VkLayer_thread_safety.json") + dest = "vulkan/explicit_layer.d/VkLayer_thread_safety.json" + }, + { + path = rebase_path("$vulkan_data_dir/VkLayer_stateless_validation.json") + dest = "vulkan/explicit_layer.d/VkLayer_stateless_validation.json" + }, + { + path = rebase_path("$vulkan_data_dir/VkLayer_unique_objects.json") + dest = "vulkan/explicit_layer.d/VkLayer_unique_objects.json" + }, + { + path = rebase_path("$vulkan_data_dir/VkLayer_khronos_validation.json") + dest = "vulkan/explicit_layer.d/VkLayer_khronos_validation.json" + }, + { + path = rebase_path("$vulkan_data_dir/VkLayer_standard_validation.json") + dest = "vulkan/explicit_layer.d/VkLayer_standard_validation.json" + }, +] diff --git a/tools/gn b/tools/gn index 5975392a8a025..a6ee544584242 100755 --- a/tools/gn +++ b/tools/gn @@ -268,6 +268,12 @@ def to_gn_args(args): if args.ubsan: gn_args['is_ubsan'] = True + if args.enable_vulkan_validation_layers: + if args.target_os is not 'fuchsia': + print('Vulkan validation layers are currently only supported on Fuchsia targets.') + sys.exit(1) + gn_args['enable_vulkan_validation_layers'] = True + return gn_args def parse_args(args): @@ -316,6 +322,7 @@ def parse_args(args): parser.add_argument('--enable-fontconfig', action='store_true', default=False) parser.add_argument('--enable-skshaper', action='store_true', default=False) + parser.add_argument('--enable-vulkan-validation-layers', action='store_true', default=False) parser.add_argument('--embedder-for-target', dest='embedder_for_target', action='store_true', default=False) diff --git a/vulkan/config.gni b/vulkan/config.gni new file mode 100644 index 0000000000000..08ccbab720647 --- /dev/null +++ b/vulkan/config.gni @@ -0,0 +1,12 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + # Whether to include vulkan validation layers, if available. + # + # Currently these are only supported on Fuchsia, where by default they are + # disabled, to enable them pass `--enable-vulkan-validation-layers` to your + # gn args. + enable_vulkan_validation_layers = false +} diff --git a/vulkan/vulkan_utilities.cc b/vulkan/vulkan_utilities.cc index 6d952777a6cfd..d02602104e328 100644 --- a/vulkan/vulkan_utilities.cc +++ b/vulkan/vulkan_utilities.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/vulkan/vulkan_utilities.h" +#include "flutter/fml/build_config.h" #include #include @@ -12,6 +13,8 @@ namespace vulkan { bool IsDebuggingEnabled() { #ifndef NDEBUG return true; +#elif defined(VULKAN_VALIDATION_LAYERS_ENABLED) + return true; #else return false; #endif @@ -27,7 +30,6 @@ bool ValidationErrorsFatal() { #if OS_FUCHSIA return false; #endif - return true; } From 7b07be61b911f93a61e5e11f77101d7028716a54 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 25 Nov 2019 18:21:45 -0800 Subject: [PATCH 260/591] Refactor CanvasKit backend in preparation for platform views (#13935) * SkCanvas -> SkLayerCanvas SkRecordingCanvas -> SkCanvas * Move files * WIP on canvas refactor * Refactor CanvasKit backend * fix tests * update licenses * Respond to PR comments --- ci/licenses_golden/licenses_flutter | 3 +- lib/web_ui/lib/src/engine.dart | 3 +- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 3 +- .../lib/src/engine/compositor/canvas.dart | 293 +++++++++--- .../engine/compositor/canvas_kit_canvas.dart | 449 ++++++++++++++++++ .../lib/src/engine/compositor/layer.dart | 50 +- .../compositor/layer_scene_builder.dart | 6 +- .../lib/src/engine/compositor/path.dart | 27 +- .../src/engine/compositor/path_metrics.dart | 5 +- .../lib/src/engine/compositor/picture.dart | 8 +- .../engine/compositor/picture_recorder.dart | 16 +- .../lib/src/engine/compositor/rasterizer.dart | 21 +- .../engine/compositor/recording_canvas.dart | 282 ----------- .../lib/src/engine/compositor/surface.dart | 131 +++-- .../lib/src/engine/compositor/text.dart | 3 + .../lib/src/engine/compositor/util.dart | 31 ++ .../lib/src/engine/compositor/vertices.dart | 37 +- lib/web_ui/lib/src/engine/picture.dart | 70 +++ .../lib/src/engine/surface/picture.dart | 2 +- lib/web_ui/lib/src/engine/window.dart | 11 +- lib/web_ui/lib/src/ui/canvas.dart | 71 +-- lib/web_ui/test/compositing_test.dart | 2 +- .../engine/compositing_golden_test.dart | 4 +- .../engine/picture_golden_test.dart | 2 +- 24 files changed, 1000 insertions(+), 530 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart delete mode 100644 lib/web_ui/lib/src/engine/compositor/recording_canvas.dart create mode 100644 lib/web_ui/lib/src/engine/picture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index cb1e2777b1bd4..16ba00d5d0238 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -365,6 +365,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_detection.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_location.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/fonts.dart @@ -381,7 +382,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/picture_recorder.dar FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/platform_message.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/raster_cache.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/rasterizer.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/runtime_delegate.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/text.dart @@ -398,6 +398,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/onscreen_logging.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/path_to_svg.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_binding.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index c1b40aa0dc857..4aa372e1c91a6 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -25,6 +25,7 @@ part 'engine/browser_detection.dart'; part 'engine/browser_location.dart'; part 'engine/color_filter.dart'; part 'engine/compositor/canvas.dart'; +part 'engine/compositor/canvas_kit_canvas.dart'; part 'engine/compositor/color_filter.dart'; part 'engine/compositor/engine_delegate.dart'; part 'engine/compositor/fonts.dart'; @@ -41,7 +42,6 @@ part 'engine/compositor/picture_recorder.dart'; part 'engine/compositor/platform_message.dart'; part 'engine/compositor/raster_cache.dart'; part 'engine/compositor/rasterizer.dart'; -part 'engine/compositor/recording_canvas.dart'; part 'engine/compositor/runtime_delegate.dart'; part 'engine/compositor/surface.dart'; part 'engine/compositor/text.dart'; @@ -58,6 +58,7 @@ part 'engine/html_image_codec.dart'; part 'engine/keyboard.dart'; part 'engine/onscreen_logging.dart'; part 'engine/path_to_svg.dart'; +part 'engine/picture.dart'; part 'engine/platform_views.dart'; part 'engine/plugins.dart'; part 'engine/pointer_binding.dart'; diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index 9a5b268ebffc7..35d59eed67767 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -743,7 +743,8 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { /// Paints the [picture] into this canvas. void drawPicture(ui.Picture picture) { - picture.recordingCanvas.apply(this); + final EnginePicture enginePicture = picture; + enginePicture.recordingCanvas.apply(this); } /// Draws vertices on a gl context. diff --git a/lib/web_ui/lib/src/engine/compositor/canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas.dart index 81109c047e9e2..1f765793900e2 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas.dart @@ -4,91 +4,174 @@ part of engine; -/// An actual [SkCanvas] which can receive raw drawing commands. -/// -/// In order for the drawing commands to be flushed to the associated HTML -/// canvas, you must call `flush()` on the canvas's `SkSurface`. -/// -/// Although this class is backed by an `SkCanvas` and can in theory perform -/// arbitrary drawing operations, this class is only used in the final -/// compositing by the layers, and arbitrary drawings are done in a -/// [ui.Picture] which uses a Skia recording canvas. This class receives -/// drawing calls from the various `Layer` classes, e.g. [ClipRectLayer] and -/// so only exposes a subset of the drawing operations that can be performed -/// on a canvas. +/// A Dart wrapper around Skia's SKCanvas. class SkCanvas { final js.JsObject skCanvas; - final html.CanvasElement htmlCanvas; - final js.JsObject skSurface; - final ui.Size size; - SkCanvas(this.skCanvas, this.htmlCanvas, this.skSurface, this.size); + SkCanvas(this.skCanvas); - int save() { - return skCanvas.callMethod('save'); + int get saveCount => skCanvas.callMethod('getSaveCount'); + + void clipPath(ui.Path path, bool doAntiAlias) { + final SkPath skPath = path; + final js.JsObject intersectClipOp = canvasKit['ClipOp']['Intersect']; + skCanvas.callMethod('clipPath', [ + skPath._skPath, + intersectClipOp, + doAntiAlias, + ]); } - int saveLayer(ui.Rect bounds, ui.Paint paint) { - return skCanvas.callMethod( - 'saveLayer', [makeSkRect(bounds), makeSkPaint(paint)]); + void clipRRect(ui.RRect rrect, bool doAntiAlias) { + final js.JsObject intersectClipOp = canvasKit['ClipOp']['Intersect']; + skCanvas.callMethod('clipRRect', [ + makeSkRRect(rrect), + intersectClipOp, + doAntiAlias, + ]); } - int saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { - final SkImageFilter skImageFilter = filter; - return skCanvas.callMethod( - 'saveLayer', - [ - null, - skImageFilter.skImageFilter, - 0, - makeSkRect(bounds), - ], - ); + void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) { + js.JsObject skClipOp; + switch (clipOp) { + case ui.ClipOp.difference: + skClipOp = canvasKit['ClipOp']['Difference']; + break; + case ui.ClipOp.intersect: + skClipOp = canvasKit['ClipOp']['Intersect']; + break; + } + + skCanvas.callMethod( + 'clipRect', [makeSkRect(rect), skClipOp, doAntiAlias]); } - void restore() { - skCanvas.callMethod('restore'); + void drawArc( + ui.Rect oval, + double startAngle, + double sweepAngle, + bool useCenter, + ui.Paint paint, + ) { + skCanvas.callMethod('drawArc', [ + makeSkRect(oval), + startAngle, + sweepAngle, + useCenter, + makeSkPaint(paint), + ]); } - void restoreToCount(int count) { - skCanvas.callMethod('restoreToCount', [count]); + void drawAtlasRaw( + ui.Paint paint, + ui.Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List colors, + ui.BlendMode blendMode, + ) { + final SkImage skAtlas = atlas; + skCanvas.callMethod('drawAtlas', [ + skAtlas.skImage, + rects, + rstTransforms, + makeSkPaint(paint), + makeSkBlendMode(blendMode), + colors, + ]); } - void clear() { - skCanvas.callMethod('clear', [0xffffffff]); + void drawCircle(ui.Offset c, double radius, ui.Paint paint) { + skCanvas.callMethod('drawCircle', [ + c.dx, + c.dy, + radius, + makeSkPaint(paint), + ]); } - void translate(double dx, double dy) { - skCanvas.callMethod('translate', [dx, dy]); + void drawColor(ui.Color color, ui.BlendMode blendMode) { + skCanvas.callMethod('drawColor', [ + color.value, + makeSkBlendMode(blendMode), + ]); } - void transform(Float64List matrix) { - skCanvas.callMethod('concat', >[makeSkMatrix(matrix)]); + void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) { + skCanvas.callMethod('drawDRRect', [ + makeSkRRect(outer), + makeSkRRect(inner), + makeSkPaint(paint), + ]); } - void clipPath(ui.Path path, {bool doAntiAlias = true}) { - final SkPath skPath = path; - final js.JsObject intersectClipOp = canvasKit['ClipOp']['Intersect']; - skCanvas.callMethod('clipPath', [ - skPath._skPath, - intersectClipOp, - doAntiAlias, + void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) { + final SkImage skImage = image; + skCanvas.callMethod('drawImage', [ + skImage.skImage, + offset.dx, + offset.dy, + makeSkPaint(paint), ]); } - void clipRect(ui.Rect rect, {bool doAntiAlias = true}) { - final js.JsObject intersectClipOp = canvasKit['ClipOp']['Intersect']; - skCanvas.callMethod('clipRect', [ + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { + final SkImage skImage = image; + skCanvas.callMethod('drawImageRect', [ + skImage.skImage, + makeSkRect(src), + makeSkRect(dst), + makeSkPaint(paint), + false, + ]); + } + + void drawImageNine( + ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { + final SkImage skImage = image; + skCanvas.callMethod('drawImageNine', [ + skImage.skImage, + makeSkRect(center), + makeSkRect(dst), + makeSkPaint(paint), + ]); + } + + void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) { + skCanvas.callMethod('drawLine', [ + p1.dx, + p1.dy, + p2.dx, + p2.dy, + makeSkPaint(paint), + ]); + } + + void drawOval(ui.Rect rect, ui.Paint paint) { + skCanvas.callMethod('drawOval', [ makeSkRect(rect), - intersectClipOp, - doAntiAlias, + makeSkPaint(paint), ]); } - void clipRRect(ui.RRect rrect) { - final SkPath skPath = SkPath(); - skPath.addRRect(rrect); - clipPath(skPath); + void drawPaint(ui.Paint paint) { + skCanvas.callMethod('drawPaint', [makeSkPaint(paint)]); + } + + void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { + final SkParagraph skParagraph = paragraph; + skCanvas.callMethod('drawParagraph', [ + skParagraph.skParagraph, + offset.dx, + offset.dy, + ]); + } + + void drawPath(ui.Path path, ui.Paint paint) { + final js.JsObject skPaint = makeSkPaint(paint); + final SkPath enginePath = path; + final js.JsObject skPath = enginePath._skPath; + skCanvas.callMethod('drawPath', [skPath, skPaint]); } void drawPicture(ui.Picture picture) { @@ -96,14 +179,25 @@ class SkCanvas { skCanvas.callMethod('drawPicture', [skPicture.skPicture]); } - void drawPath(ui.Path path, ui.Paint paint) { - final SkPath skPath = path; - skCanvas.callMethod( - 'drawPath', [skPath._skPath, makeSkPaint(paint)]); + void drawPoints(ui.Paint paint, ui.PointMode pointMode, Float32List points) { + skCanvas.callMethod('drawPoints', [ + makeSkPointMode(pointMode), + points, + makeSkPaint(paint), + ]); } - void drawPaint(ui.Paint paint) { - skCanvas.callMethod('drawPaint', [makeSkPaint(paint)]); + void drawRRect(ui.RRect rrect, ui.Paint paint) { + skCanvas.callMethod('drawRRect', [ + makeSkRRect(rrect), + makeSkPaint(paint), + ]); + } + + void drawRect(ui.Rect rect, ui.Paint paint) { + final js.JsObject skRect = makeSkRect(rect); + final js.JsObject skPaint = makeSkPaint(paint); + skCanvas.callMethod('drawRect', [skRect, skPaint]); } void drawShadow(ui.Path path, ui.Color color, double elevation, @@ -111,4 +205,75 @@ class SkCanvas { drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, ui.window.devicePixelRatio); } + + void drawVertices( + ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { + SkVertices skVertices = vertices; + skCanvas.callMethod('drawVertices', [ + skVertices.skVertices, + makeSkBlendMode(blendMode), + makeSkPaint(paint) + ]); + } + + void restore() { + skCanvas.callMethod('restore'); + } + + void restoreToCount(int count) { + skCanvas.callMethod('restoreToCount', [count]); + } + + void rotate(double radians) { + skCanvas + .callMethod('rotate', [radians * 180.0 / math.pi, 0.0, 0.0]); + } + + int save() { + return skCanvas.callMethod('save'); + } + + void saveLayer(ui.Rect bounds, ui.Paint paint) { + skCanvas.callMethod('saveLayer', [ + makeSkRect(bounds), + makeSkPaint(paint), + ]); + } + + void saveLayerWithoutBounds(ui.Paint paint) { + skCanvas.callMethod('saveLayer', [null, makeSkPaint(paint)]); + } + + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { + final SkImageFilter skImageFilter = filter; + return skCanvas.callMethod( + 'saveLayer', + [ + null, + skImageFilter.skImageFilter, + 0, + makeSkRect(bounds), + ], + ); + } + + void scale(double sx, double sy) { + skCanvas.callMethod('scale', [sx, sy]); + } + + void skew(double sx, double sy) { + skCanvas.callMethod('skew', [sx, sy]); + } + + void transform(Float64List matrix4) { + skCanvas.callMethod('concat', >[makeSkMatrix(matrix4)]); + } + + void translate(double dx, double dy) { + skCanvas.callMethod('translate', [dx, dy]); + } + + void flush() { + skCanvas.callMethod('flush'); + } } diff --git a/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart new file mode 100644 index 0000000000000..4765ca301055d --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart @@ -0,0 +1,449 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// An implementation of [ui.Canvas] that is backed by a CanvasKit canvas. +class CanvasKitCanvas implements ui.Canvas { + final SkCanvas _canvas; + + factory CanvasKitCanvas(ui.PictureRecorder recorder, [ui.Rect cullRect]) { + assert(recorder != null); + if (recorder.isRecording) { + throw ArgumentError( + '"recorder" must not already be associated with another Canvas.'); + } + cullRect ??= ui.Rect.largest; + final SkPictureRecorder skRecorder = recorder; + return CanvasKitCanvas._(skRecorder.beginRecording(cullRect)); + } + + CanvasKitCanvas._(this._canvas); + + @override + void save() { + _canvas.save(); + } + + @override + void saveLayer(ui.Rect bounds, ui.Paint paint) { + assert(paint != null); + if (bounds == null) { + _saveLayerWithoutBounds(paint); + } else { + assert(rectIsValid(bounds)); + _saveLayer(bounds, paint); + } + } + + void _saveLayerWithoutBounds(ui.Paint paint) { + _canvas.saveLayerWithoutBounds(paint); + } + + void _saveLayer(ui.Rect bounds, ui.Paint paint) { + _canvas.saveLayer(bounds, paint); + } + + @override + void restore() { + _canvas.restore(); + } + + @override + int getSaveCount() { + return _canvas.saveCount; + } + + @override + void translate(double dx, double dy) { + _canvas.translate(dx, dy); + } + + @override + void scale(double sx, [double sy]) => _scale(sx, sy ?? sx); + + void _scale(double sx, double sy) { + _canvas.scale(sx, sy); + } + + @override + void rotate(double radians) { + _canvas.rotate(radians); + } + + @override + void skew(double sx, double sy) { + _canvas.skew(sx, sy); + } + + @override + void transform(Float64List matrix4) { + assert(matrix4 != null); + if (matrix4.length != 16) { + throw ArgumentError('"matrix4" must have 16 entries.'); + } + _transform(matrix4); + } + + void _transform(Float64List matrix4) { + _canvas.transform(matrix4); + } + + @override + void clipRect(ui.Rect rect, + {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { + assert(rectIsValid(rect)); + assert(clipOp != null); + assert(doAntiAlias != null); + _clipRect(rect, clipOp, doAntiAlias); + } + + void _clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) { + _canvas.clipRect(rect, clipOp, doAntiAlias); + } + + @override + void clipRRect(ui.RRect rrect, {bool doAntiAlias = true}) { + assert(rrectIsValid(rrect)); + assert(doAntiAlias != null); + _clipRRect(rrect, doAntiAlias); + } + + void _clipRRect(ui.RRect rrect, bool doAntiAlias) { + _canvas.clipRRect(rrect, doAntiAlias); + } + + @override + void clipPath(ui.Path path, {bool doAntiAlias = true}) { + assert(path != null); // path is checked on the engine side + assert(doAntiAlias != null); + _clipPath(path, doAntiAlias); + } + + void _clipPath(ui.Path path, bool doAntiAlias) { + _canvas.clipPath(path, doAntiAlias); + } + + @override + void drawColor(ui.Color color, ui.BlendMode blendMode) { + assert(color != null); + assert(blendMode != null); + _drawColor(color, blendMode); + } + + void _drawColor(ui.Color color, ui.BlendMode blendMode) { + _canvas.drawColor(color, blendMode); + } + + @override + void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) { + assert(_offsetIsValid(p1)); + assert(_offsetIsValid(p2)); + assert(paint != null); + _drawLine(p1, p2, paint); + } + + void _drawLine(ui.Offset p1, ui.Offset p2, paint) { + _canvas.drawLine(p1, p2, paint); + } + + @override + void drawPaint(ui.Paint paint) { + assert(paint != null); + _drawPaint(paint); + } + + void _drawPaint(ui.Paint paint) { + _canvas.drawPaint(paint); + } + + @override + void drawRect(ui.Rect rect, ui.Paint paint) { + assert(rectIsValid(rect)); + assert(paint != null); + _drawRect(rect, paint); + } + + void _drawRect(ui.Rect rect, ui.Paint paint) { + _canvas.drawRect(rect, paint); + } + + @override + void drawRRect(ui.RRect rrect, ui.Paint paint) { + assert(rrectIsValid(rrect)); + assert(paint != null); + _drawRRect(rrect, paint); + } + + void _drawRRect(ui.RRect rrect, ui.Paint paint) { + _canvas.drawRRect(rrect, paint); + } + + @override + void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) { + assert(rrectIsValid(outer)); + assert(rrectIsValid(inner)); + assert(paint != null); + _drawDRRect(outer, inner, paint); + } + + void _drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) { + _canvas.drawDRRect(outer, inner, paint); + } + + @override + void drawOval(ui.Rect rect, ui.Paint paint) { + assert(rectIsValid(rect)); + assert(paint != null); + _drawOval(rect, paint); + } + + void _drawOval(ui.Rect rect, ui.Paint paint) { + _canvas.drawOval(rect, paint); + } + + @override + void drawCircle(ui.Offset c, double radius, ui.Paint paint) { + assert(_offsetIsValid(c)); + assert(paint != null); + _drawCircle(c, radius, paint); + } + + void _drawCircle(ui.Offset c, double radius, ui.Paint paint) { + _canvas.drawCircle(c, radius, paint); + } + + @override + void drawArc(ui.Rect rect, double startAngle, double sweepAngle, + bool useCenter, ui.Paint paint) { + assert(rectIsValid(rect)); + assert(paint != null); + _drawArc(rect, startAngle, sweepAngle, useCenter, paint); + } + + void _drawArc(ui.Rect rect, double startAngle, double sweepAngle, + bool useCenter, ui.Paint paint) { + _canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint); + } + + @override + void drawPath(ui.Path path, ui.Paint paint) { + assert(path != null); // path is checked on the engine side + assert(paint != null); + _drawPath(path, paint); + } + + void _drawPath(ui.Path path, ui.Paint paint) { + _canvas.drawPath(path, paint); + } + + @override + void drawImage(ui.Image image, ui.Offset p, ui.Paint paint) { + assert(image != null); // image is checked on the engine side + assert(_offsetIsValid(p)); + assert(paint != null); + _drawImage(image, p, paint); + } + + void _drawImage(ui.Image image, ui.Offset p, ui.Paint paint) { + _canvas.drawImage(image, p, paint); + } + + @override + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { + assert(image != null); // image is checked on the engine side + assert(rectIsValid(src)); + assert(rectIsValid(dst)); + assert(paint != null); + _drawImageRect(image, src, dst, paint); + } + + void _drawImageRect( + ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { + _canvas.drawImageRect(image, src, dst, paint); + } + + @override + void drawImageNine( + ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { + assert(image != null); // image is checked on the engine side + assert(rectIsValid(center)); + assert(rectIsValid(dst)); + assert(paint != null); + _drawImageNine(image, center, dst, paint); + } + + void _drawImageNine( + ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { + _canvas.drawImageNine(image, center, dst, paint); + } + + @override + void drawPicture(ui.Picture picture) { + assert(picture != null); // picture is checked on the engine side + _drawPicture(picture); + } + + void _drawPicture(ui.Picture picture) { + _canvas.drawPicture(picture); + } + + @override + void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { + assert(paragraph != null); + assert(_offsetIsValid(offset)); + _drawParagraph(paragraph, offset); + } + + void _drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { + _canvas.drawParagraph(paragraph, offset); + } + + @override + void drawPoints( + ui.PointMode pointMode, List points, ui.Paint paint) { + assert(pointMode != null); + assert(points != null); + assert(paint != null); + _drawPoints(paint, pointMode, encodePointList(points)); + } + + @override + void drawRawPoints( + ui.PointMode pointMode, Float32List points, ui.Paint paint) { + assert(pointMode != null); + assert(points != null); + assert(paint != null); + if (points.length % 2 != 0) { + throw ArgumentError('"points" must have an even number of values.'); + } + _drawPoints(paint, pointMode, points); + } + + void _drawPoints(ui.Paint paint, ui.PointMode pointMode, Float32List points) { + _canvas.drawPoints(paint, pointMode, points); + } + + @override + void drawVertices( + ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { + assert(vertices != null); // vertices is checked on the engine side + assert(paint != null); + assert(blendMode != null); + _drawVertices(vertices, blendMode, paint); + } + + void _drawVertices( + ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { + _canvas.drawVertices(vertices, blendMode, paint); + } + + @override + void drawAtlas( + ui.Image atlas, + List transforms, + List rects, + List colors, + ui.BlendMode blendMode, + ui.Rect cullRect, + ui.Paint paint) { + assert(atlas != null); // atlas is checked on the engine side + assert(transforms != null); + assert(rects != null); + assert(colors != null); + assert(blendMode != null); + assert(paint != null); + + final int rectCount = rects.length; + if (transforms.length != rectCount) { + throw ArgumentError('"transforms" and "rects" lengths must match.'); + } + if (colors.isNotEmpty && colors.length != rectCount) { + throw ArgumentError( + 'If non-null, "colors" length must match that of "transforms" and "rects".'); + } + + final Float32List rstTransformBuffer = Float32List(rectCount * 4); + final Float32List rectBuffer = Float32List(rectCount * 4); + + for (int i = 0; i < rectCount; ++i) { + final int index0 = i * 4; + final int index1 = index0 + 1; + final int index2 = index0 + 2; + final int index3 = index0 + 3; + final ui.RSTransform rstTransform = transforms[i]; + final ui.Rect rect = rects[i]; + assert(rectIsValid(rect)); + rstTransformBuffer[index0] = rstTransform.scos; + rstTransformBuffer[index1] = rstTransform.ssin; + rstTransformBuffer[index2] = rstTransform.tx; + rstTransformBuffer[index3] = rstTransform.ty; + rectBuffer[index0] = rect.left; + rectBuffer[index1] = rect.top; + rectBuffer[index2] = rect.right; + rectBuffer[index3] = rect.bottom; + } + + final Int32List colorBuffer = + colors.isEmpty ? null : _encodeColorList(colors); + + _drawAtlas( + paint, atlas, rstTransformBuffer, rectBuffer, colorBuffer, blendMode); + } + + @override + void drawRawAtlas( + ui.Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List colors, + ui.BlendMode blendMode, + ui.Rect cullRect, + ui.Paint paint) { + assert(atlas != null); // atlas is checked on the engine side + assert(rstTransforms != null); + assert(rects != null); + assert(colors != null); + assert(blendMode != null); + assert(paint != null); + + final int rectCount = rects.length; + if (rstTransforms.length != rectCount) + throw ArgumentError('"rstTransforms" and "rects" lengths must match.'); + if (rectCount % 4 != 0) + throw ArgumentError( + '"rstTransforms" and "rects" lengths must be a multiple of four.'); + if (colors != null && colors.length * 4 != rectCount) + throw ArgumentError( + 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".'); + + _drawAtlas(paint, atlas, rstTransforms, rects, colors, blendMode); + } + + // TODO(hterkelsen): Pass a cull_rect once CanvasKit supports that. + void _drawAtlas( + ui.Paint paint, + ui.Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List colors, + ui.BlendMode blendMode, + ) { + _canvas.drawAtlasRaw(paint, atlas, rstTransforms, rects, colors, blendMode); + } + + @override + void drawShadow(ui.Path path, ui.Color color, double elevation, + bool transparentOccluder) { + assert(path != null); // path is checked on the engine side + assert(color != null); + assert(transparentOccluder != null); + _drawShadow(path, color, elevation, transparentOccluder); + } + + void _drawShadow(ui.Path path, ui.Color color, double elevation, + bool transparentOccluder) { + _canvas.drawShadow(path, color, elevation, transparentOccluder); + } +} diff --git a/lib/web_ui/lib/src/engine/compositor/layer.dart b/lib/web_ui/lib/src/engine/compositor/layer.dart index f50ae53f9ef3f..3321bbe5dd6f8 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer.dart @@ -110,8 +110,10 @@ class BackdropFilterLayer extends ContainerLayer { class ClipPathLayer extends ContainerLayer { /// The path used to clip child layers. final ui.Path _clipPath; + final ui.Clip _clipBehavior; - ClipPathLayer(this._clipPath); + ClipPathLayer(this._clipPath, this._clipBehavior) + : assert(_clipBehavior != ui.Clip.none); @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { @@ -127,8 +129,15 @@ class ClipPathLayer extends ContainerLayer { assert(needsPainting); paintContext.canvas.save(); - paintContext.canvas.clipPath(_clipPath); + paintContext.canvas.clipPath(_clipPath, _clipBehavior != ui.Clip.hardEdge); + + if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { + paintContext.canvas.saveLayer(paintBounds, null); + } paintChildren(paintContext); + if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { + paintContext.canvas.restore(); + } paintContext.canvas.restore(); } } @@ -137,8 +146,10 @@ class ClipPathLayer extends ContainerLayer { class ClipRectLayer extends ContainerLayer { /// The rectangle used to clip child layers. final ui.Rect _clipRect; + final ui.Clip _clipBehavior; - ClipRectLayer(this._clipRect); + ClipRectLayer(this._clipRect, this._clipBehavior) + : assert(_clipBehavior != ui.Clip.none); @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { @@ -153,8 +164,18 @@ class ClipRectLayer extends ContainerLayer { assert(needsPainting); paintContext.canvas.save(); - paintContext.canvas.clipRect(_clipRect); + paintContext.canvas.clipRect( + _clipRect, + ui.ClipOp.intersect, + _clipBehavior != ui.Clip.hardEdge, + ); + if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { + paintContext.canvas.saveLayer(_clipRect, null); + } paintChildren(paintContext); + if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { + paintContext.canvas.restore(); + } paintContext.canvas.restore(); } } @@ -163,8 +184,10 @@ class ClipRectLayer extends ContainerLayer { class ClipRRectLayer extends ContainerLayer { /// The rounded rectangle used to clip child layers. final ui.RRect _clipRRect; + final ui.Clip _clipBehavior; - ClipRRectLayer(this._clipRRect); + ClipRRectLayer(this._clipRRect, this._clipBehavior) + : assert(_clipBehavior != ui.Clip.none); @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { @@ -179,8 +202,15 @@ class ClipRRectLayer extends ContainerLayer { assert(needsPainting); paintContext.canvas.save(); - paintContext.canvas.clipRRect(_clipRRect); + paintContext.canvas + .clipRRect(_clipRRect, _clipBehavior != ui.Clip.hardEdge); + if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { + paintContext.canvas.saveLayer(paintBounds, null); + } paintChildren(paintContext); + if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { + paintContext.canvas.restore(); + } paintContext.canvas.restore(); } } @@ -287,7 +317,7 @@ class TransformLayer extends ContainerLayer /// A layer containing a [Picture]. class PictureLayer extends Layer { /// The picture to paint into the canvas. - final ui.Picture picture; + final SkPicture picture; /// The offset at which to paint the picture. final ui.Offset offset; @@ -414,13 +444,13 @@ class PhysicalShapeLayer extends ContainerLayer final int saveCount = paintContext.canvas.save(); switch (_clipBehavior) { case ui.Clip.hardEdge: - paintContext.canvas.clipPath(_path, doAntiAlias: false); + paintContext.canvas.clipPath(_path, false); break; case ui.Clip.antiAlias: - paintContext.canvas.clipPath(_path, doAntiAlias: true); + paintContext.canvas.clipPath(_path, true); break; case ui.Clip.antiAliasWithSaveLayer: - paintContext.canvas.clipPath(_path, doAntiAlias: true); + paintContext.canvas.clipPath(_path, true); paintContext.canvas.saveLayer(paintBounds, null); break; case ui.Clip.none: diff --git a/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart index b92e0f903284e..c07fe108351a4 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart @@ -99,21 +99,21 @@ class LayerSceneBuilder implements ui.SceneBuilder { @override ui.ClipPathEngineLayer pushClipPath(ui.Path path, {ui.Clip clipBehavior = ui.Clip.antiAlias, ui.EngineLayer oldLayer}) { - pushLayer(ClipPathLayer(path)); + pushLayer(ClipPathLayer(path, clipBehavior)); return null; } @override ui.ClipRRectEngineLayer pushClipRRect(ui.RRect rrect, {ui.Clip clipBehavior, ui.EngineLayer oldLayer}) { - pushLayer(ClipRRectLayer(rrect)); + pushLayer(ClipRRectLayer(rrect, clipBehavior)); return null; } @override ui.ClipRectEngineLayer pushClipRect(ui.Rect rect, {ui.Clip clipBehavior = ui.Clip.antiAlias, ui.EngineLayer oldLayer}) { - pushLayer(ClipRectLayer(rect)); + pushLayer(ClipRectLayer(rect, clipBehavior)); return null; } diff --git a/lib/web_ui/lib/src/engine/compositor/path.dart b/lib/web_ui/lib/src/engine/compositor/path.dart index da2b6599a8aab..4dd9e05c8ec62 100644 --- a/lib/web_ui/lib/src/engine/compositor/path.dart +++ b/lib/web_ui/lib/src/engine/compositor/path.dart @@ -41,11 +41,11 @@ class SkPath implements ui.Path { @override void addArc(ui.Rect oval, double startAngle, double sweepAngle) { - const double _toDegrees = 180.0 / math.pi; + const double toDegrees = 180.0 / math.pi; _skPath.callMethod('addArc', [ makeSkRect(oval), - startAngle * _toDegrees, - sweepAngle * _toDegrees, + startAngle * toDegrees, + sweepAngle * toDegrees, ]); } @@ -83,20 +83,9 @@ class SkPath implements ui.Path { @override void addPolygon(List points, bool close) { - // TODO(het): Use `addPoly` once CanvasKit makes it available. assert(points != null); - if (points.isEmpty) { - return; - } - - moveTo(points.first.dx, points.first.dy); - for (int i = 1; i < points.length; i++) { - final ui.Offset point = points[i]; - lineTo(point.dx, point.dy); - } - if (close) { - this.close(); - } + final Float32List encodedPoints = encodePointList(points); + _skPath.callMethod('addPoly', [encodedPoints, close]); } @override @@ -124,11 +113,11 @@ class SkPath implements ui.Path { @override void arcTo( ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { - const double radsToDegrees = 180.0 / math.pi; + const double toDegrees = 180.0 / math.pi; _skPath.callMethod('arcTo', [ makeSkRect(rect), - startAngle * radsToDegrees, - sweepAngle * radsToDegrees, + startAngle * toDegrees, + sweepAngle * toDegrees, forceMoveTo, ]); } diff --git a/lib/web_ui/lib/src/engine/compositor/path_metrics.dart b/lib/web_ui/lib/src/engine/compositor/path_metrics.dart index 71f73205283aa..bd9faea2e0bac 100644 --- a/lib/web_ui/lib/src/engine/compositor/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/compositor/path_metrics.dart @@ -102,8 +102,9 @@ class _SkPathMeasure { {bool startWithMoveTo = true}) { assert(contourIndex == currentContourIndex, 'PathMetrics are invalid if it is not the current contour.'); - // TODO(het): Add this once `getSegment` is added to CanvasKit. - throw UnimplementedError('extractPath'); + final js.JsObject skPath = pathMeasure + .callMethod('getSegment', [start, end, startWithMoveTo]); + return SkPath._fromSkPath(skPath); } bool isClosed(int contourIndex) { diff --git a/lib/web_ui/lib/src/engine/compositor/picture.dart b/lib/web_ui/lib/src/engine/compositor/picture.dart index 0796c75ea2257..d9eac4583c97a 100644 --- a/lib/web_ui/lib/src/engine/compositor/picture.dart +++ b/lib/web_ui/lib/src/engine/compositor/picture.dart @@ -6,24 +6,18 @@ part of engine; class SkPicture implements ui.Picture { final js.JsObject skPicture; + final ui.Rect cullRect; SkPicture(this.skPicture, this.cullRect); @override int get approximateBytesUsed => 0; - @override - final ui.Rect cullRect; - @override void dispose() { // TODO: implement dispose } - @override - // TODO: implement recordingCanvas - RecordingCanvas get recordingCanvas => null; - @override Future toImage(int width, int height) { // TODO: implement toImage diff --git a/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart b/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart index 9211bbddb7280..8490f5d35acf7 100644 --- a/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart +++ b/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart @@ -5,20 +5,17 @@ part of engine; class SkPictureRecorder implements ui.PictureRecorder { - @override - ui.Rect cullRect; - + ui.Rect _cullRect; js.JsObject _recorder; - @override - RecordingCanvas beginRecording(ui.Rect bounds) { - cullRect = bounds; + SkCanvas beginRecording(ui.Rect bounds) { + _cullRect = bounds; _recorder = js.JsObject(canvasKit['SkPictureRecorder']); final js.JsObject skRect = js.JsObject(canvasKit['LTRBRect'], [bounds.left, bounds.top, bounds.right, bounds.bottom]); final js.JsObject skCanvas = _recorder.callMethod('beginRecording', [skRect]); - return SkRecordingCanvas(skCanvas); + return SkCanvas(skCanvas); } @override @@ -26,9 +23,10 @@ class SkPictureRecorder implements ui.PictureRecorder { final js.JsObject skPicture = _recorder.callMethod('finishRecordingAsPicture'); _recorder.callMethod('delete'); - return SkPicture(skPicture, cullRect); + _recorder = null; + return SkPicture(skPicture, _cullRect); } @override - bool get isRecording => false; + bool get isRecording => _recorder != null; } diff --git a/lib/web_ui/lib/src/engine/compositor/rasterizer.dart b/lib/web_ui/lib/src/engine/compositor/rasterizer.dart index 5a6dfcdda0771..2953623ce504f 100644 --- a/lib/web_ui/lib/src/engine/compositor/rasterizer.dart +++ b/lib/web_ui/lib/src/engine/compositor/rasterizer.dart @@ -14,11 +14,24 @@ class Rasterizer { /// Creates a new frame from this rasterizer's surface, draws the given /// [LayerTree] into it, and then submits the frame. void draw(LayerTree layerTree) { - final SurfaceFrame frame = surface.acquireFrame(ui.window.physicalSize); - final SkCanvas canvas = frame.canvas; - final Frame compositorFrame = context.acquireFrame(canvas); + if (layerTree == null) { + return; + } + + final ui.Size physicalSize = ui.window.physicalSize; + final ui.Size frameSize = ui.Size( + physicalSize.width.truncate().toDouble(), + physicalSize.height.truncate().toDouble(), + ); - canvas.clear(); + if (frameSize.isEmpty) { + return; + } + layerTree.frameSize = frameSize; + + final SurfaceFrame frame = surface.acquireFrame(layerTree.frameSize); + final SkCanvas canvas = frame.skiaCanvas; + final Frame compositorFrame = context.acquireFrame(canvas); compositorFrame.raster(layerTree, ignoreRasterCache: true); frame.submit(); diff --git a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart deleted file mode 100644 index b4ec063c483c8..0000000000000 --- a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -part of engine; - -class SkRecordingCanvas implements RecordingCanvas { - final js.JsObject skCanvas; - SkRecordingCanvas(this.skCanvas); - - @override - bool _didDraw = true; - - @override - bool _hasArbitraryPaint = true; - - @override - int get saveCount => skCanvas.callMethod('getSaveCount'); - - // This is required to implement RecordingCanvas. - @override - int _saveCount = -1; - - @override - // TODO: implement _commands - List get _commands => null; - - @override - // TODO: implement _paintBounds - _PaintBounds get _paintBounds => null; - - @override - void apply(EngineCanvas engineCanvas) { - throw UnimplementedError("The Skia backend doesn't support apply()"); - } - - @override - void clipPath(ui.Path path, {bool doAntiAlias = true}) { - final SkPath skPath = path; - final js.JsObject intersectClipOp = canvasKit['ClipOp']['Intersect']; - skCanvas.callMethod('clipPath', [ - skPath._skPath, - intersectClipOp, - doAntiAlias, - ]); - } - - @override - void clipRRect( - ui.RRect rrect, { - bool doAntiAlias = true, - }) { - // TODO(het): Use `clipRRect` when CanvasKit makes it available. - // CanvasKit doesn't expose `Canvas.clipRRect`, so we create a path, add the - // RRect to it, and call clipPath with it. - final SkPath rrectPath = SkPath(); - rrectPath.addRRect(rrect); - clipPath(rrectPath, doAntiAlias: doAntiAlias); - } - - @override - void clipRect( - ui.Rect rect, { - ui.ClipOp clipOp = ui.ClipOp.intersect, - bool doAntiAlias = true, - }) { - js.JsObject skClipOp; - switch (clipOp) { - case ui.ClipOp.difference: - skClipOp = canvasKit['ClipOp']['Difference']; - break; - case ui.ClipOp.intersect: - skClipOp = canvasKit['ClipOp']['Intersect']; - break; - } - - skCanvas.callMethod( - 'clipRect', [makeSkRect(rect), skClipOp, doAntiAlias]); - } - - @override - ui.Rect computePaintBounds() { - throw UnimplementedError( - "The Skia backend doesn't use computePaintBounds()"); - } - - @override - void debugDumpCommands() { - throw UnimplementedError( - "The Skia backend doesn't use debugDumpCommands()"); - } - - @override - void debugEnforceArbitraryPaint() { - throw UnimplementedError( - "The Skia backend doesn't use debugEnforceArbitraryPaint()"); - } - - @override - String debugPrintCommands() { - throw UnimplementedError( - "The Skia backend doesn't use debugPrintCommands()"); - } - - @override - bool get didDraw => true; - - @override - void drawCircle(ui.Offset c, double radius, ui.Paint paint) { - skCanvas.callMethod('drawCircle', [ - c.dx, - c.dy, - radius, - makeSkPaint(paint), - ]); - } - - @override - void drawColor(ui.Color color, ui.BlendMode blendMode) { - // TODO(het): Implement this once SkCanvas.drawColor becomes available. - throw 'drawColor'; - } - - @override - void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) { - skCanvas.callMethod('drawDRRect', [ - makeSkRRect(outer), - makeSkRRect(inner), - makeSkPaint(paint), - ]); - } - - @override - void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) { - final SkImage skImage = image; - skCanvas.callMethod('drawImage', [ - skImage.skImage, - offset.dx, - offset.dy, - makeSkPaint(paint), - ]); - } - - @override - void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { - final SkImage skImage = image; - skCanvas.callMethod('drawImageRect', [ - skImage.skImage, - makeSkRect(src), - makeSkRect(dst), - makeSkPaint(paint), - false, - ]); - } - - @override - void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) { - skCanvas.callMethod('drawLine', [ - p1.dx, - p1.dy, - p2.dx, - p2.dy, - makeSkPaint(paint), - ]); - } - - @override - void drawOval(ui.Rect rect, ui.Paint paint) { - skCanvas.callMethod('drawOval', [ - makeSkRect(rect), - makeSkPaint(paint), - ]); - } - - @override - void drawPaint(ui.Paint paint) { - skCanvas.callMethod('drawPaint', [makeSkPaint(paint)]); - } - - @override - void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { - final SkParagraph skParagraph = paragraph; - skCanvas.callMethod('drawParagraph', [ - skParagraph.skParagraph, - offset.dx, - offset.dy, - ]); - } - - @override - void drawPath(ui.Path path, ui.Paint paint) { - final js.JsObject skPaint = makeSkPaint(paint); - final SkPath enginePath = path; - final js.JsObject skPath = enginePath._skPath; - skCanvas.callMethod('drawPath', [skPath, skPaint]); - } - - @override - void drawRRect(ui.RRect rrect, ui.Paint paint) { - skCanvas.callMethod('drawRRect', [ - makeSkRRect(rrect), - makeSkPaint(paint), - ]); - } - - @override - void drawRect(ui.Rect rect, ui.Paint paint) { - final js.JsObject skRect = makeSkRect(rect); - final js.JsObject skPaint = makeSkPaint(paint); - skCanvas.callMethod('drawRect', [skRect, skPaint]); - } - - @override - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, - ui.window.devicePixelRatio); - } - - @override - void drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { - SkVertices skVertices = vertices; - skCanvas.callMethod('drawVertices', [ - skVertices.skVertices, - makeSkBlendMode(blendMode), - makeSkPaint(paint) - ]); - } - - @override - bool get hasArbitraryPaint => true; - - @override - void restore() { - skCanvas.callMethod('restore'); - } - - @override - void rotate(double radians) { - skCanvas - .callMethod('rotate', [radians * 180.0 / math.pi, 0.0, 0.0]); - } - - @override - void save() { - skCanvas.callMethod('save'); - } - - @override - void saveLayer(ui.Rect bounds, ui.Paint paint) { - skCanvas.callMethod('saveLayer', [ - makeSkRect(bounds), - makeSkPaint(paint), - ]); - } - - @override - void saveLayerWithoutBounds(ui.Paint paint) { - skCanvas.callMethod('saveLayer', [null, makeSkPaint(paint)]); - } - - @override - void scale(double sx, double sy) { - skCanvas.callMethod('scale', [sx, sy]); - } - - @override - void skew(double sx, double sy) { - skCanvas.callMethod('skew', [sx, sy]); - } - - @override - void transform(Float64List matrix4) { - skCanvas.callMethod('concat', >[makeSkMatrix(matrix4)]); - } - - @override - void translate(double dx, double dy) { - skCanvas.callMethod('translate', [dx, dy]); - } -} diff --git a/lib/web_ui/lib/src/engine/compositor/surface.dart b/lib/web_ui/lib/src/engine/compositor/surface.dart index 9b294d0dee49c..8e38b678f2fbc 100644 --- a/lib/web_ui/lib/src/engine/compositor/surface.dart +++ b/lib/web_ui/lib/src/engine/compositor/surface.dart @@ -4,51 +4,87 @@ part of engine; +typedef SubmitCallback = bool Function(SurfaceFrame, SkCanvas); + /// A frame which contains a canvas to be drawn into. class SurfaceFrame { - final void Function(SkCanvas) submitFn; - final SkCanvas canvas; - SurfaceFrame(this.submitFn, this.canvas); + final SkSurface skiaSurface; + final SubmitCallback submitCallback; + bool _submitted; + + SurfaceFrame(this.skiaSurface, this.submitCallback) + : _submitted = false, + assert(submitCallback != null); /// Submit this frame to be drawn. - void submit() { - submitFn(canvas); + bool submit() { + if (_submitted) { + return false; + } + return submitCallback(this, skiaCanvas); } + + SkCanvas get skiaCanvas => skiaSurface?.getCanvas(); } /// A surface which can be drawn into by the compositor. /// -/// The underlying representation is a [BitmapCanvas], which can be reused by -/// successive frames if they are the same size. Otherwise, a new canvas is +/// The underlying representation is a [SkSurface], which can be reused by +/// successive frames if they are the same size. Otherwise, a new [SkSurface] is /// created. class Surface { - final _CanvasCache canvasCache = _CanvasCache(); - - /// This function is called with the canvas once drawing on it has been - /// completed for a frame. - final void Function(SkCanvas) submitFunction; - - Surface(this.submitFunction); + SkSurface _surface; /// Acquire a frame of the given [size] containing a drawable canvas. /// /// The given [size] is in physical pixels. SurfaceFrame acquireFrame(ui.Size size) { - final SkCanvas canvas = canvasCache.acquireCanvas(size); - return SurfaceFrame(submitFunction, canvas); + final SkSurface surface = _acquireRenderSurface(size); + + if (surface == null) return null; + + SubmitCallback submitCallback = (SurfaceFrame surfaceFrame, SkCanvas canvas) { + _presentSurface(canvas); + }; + + return SurfaceFrame(surface, submitCallback); } - Matrix4 get rootTransformation => null; -} + SkSurface _acquireRenderSurface(ui.Size size) { + if (!_createOrUpdateSurfaces(size)) { + return null; + } + return _surface; + } + + bool _createOrUpdateSurfaces(ui.Size size) { + if (_surface != null && + size == + ui.Size( + _surface.width().toDouble(), + _surface.height().toDouble(), + )) { + return true; + } + + _surface?.dispose(); + _surface = null; -class _CanvasCache { - SkCanvas _canvas; + if (size.isEmpty) { + html.window.console.error('Cannot create surfaces of empty size.'); + return false; + } + _surface = _wrapHtmlCanvas(size); - SkCanvas acquireCanvas(ui.Size size) { - assert(size != null); - if (size == _canvas?.size) { - return _canvas; + if (_surface == null) { + html.window.console.error('Could not create a surface.'); + return false; } + + return true; + } + + SkSurface _wrapHtmlCanvas(ui.Size size) { final ui.Size logicalSize = size / ui.window.devicePixelRatio; final html.CanvasElement htmlCanvas = html.CanvasElement(width: size.width.ceil(), height: size.height.ceil()) @@ -57,11 +93,46 @@ class _CanvasCache { ..position = 'absolute' ..width = '${logicalSize.width.ceil()}px' ..height = '${logicalSize.height.ceil()}px'; - domRenderer.renderScene(htmlCanvas); - final js.JsObject surface = - canvasKit.callMethod('MakeCanvasSurface', ['flt-sk-canvas']); - final js.JsObject skCanvas = surface.callMethod('getCanvas'); - _canvas = SkCanvas(skCanvas, htmlCanvas, surface, size); - return _canvas; + final js.JsObject skSurface = + canvasKit.callMethod('MakeWebGLCanvasSurface', [ + htmlCanvas, + size.width, + size.height, + ]); + + if (skSurface == null) { + return null; + } else { + domRenderer.renderScene(htmlCanvas); + return SkSurface(skSurface); + } + } + + bool _presentSurface(SkCanvas canvas) { + if (canvas == null) { + return false; + } + + _surface.getCanvas().flush(); + return true; + } +} + +/// A Dart wrapper around Skia's SkSurface. +class SkSurface { + final js.JsObject _surface; + + SkSurface(this._surface); + + SkCanvas getCanvas() { + final js.JsObject skCanvas = _surface.callMethod('getCanvas'); + return SkCanvas(skCanvas); + } + + int width() => _surface.callMethod('width'); + int height() => _surface.callMethod('height'); + + void dispose() { + _surface.callMethod('dispose'); } } diff --git a/lib/web_ui/lib/src/engine/compositor/text.dart b/lib/web_ui/lib/src/engine/compositor/text.dart index 4286bd5ab2141..f598c182f2cd3 100644 --- a/lib/web_ui/lib/src/engine/compositor/text.dart +++ b/lib/web_ui/lib/src/engine/compositor/text.dart @@ -17,6 +17,9 @@ const Map _fontFamilyOverrides = { 'AbrilFatface': 'Abril Fatface', 'packages/cupertino_icons/CupertinoIcons': 'CupertinoIcons', '.SF Pro Text': 'Google Sans', + '.SF Pro Display': 'Google Sans', + '.SF UI Text': 'Google Sans', + '.SF UI Display': 'Google Sans', }; class SkParagraphStyle implements ui.ParagraphStyle { diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index 0647b404af13b..a62655d10c8b4 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -56,6 +56,35 @@ js.JsArray makeSkPoint(ui.Offset point) { return skPoint; } +/// Creates a point list using a typed buffer created by CanvasKit.Malloc. +Float32List encodePointList(List points) { + assert(points != null); + final int pointCount = points.length; + final Float32List result = canvasKit.callMethod('Malloc', [js.context['Float32Array'], pointCount * 2]); + for (int i = 0; i < pointCount; ++i) { + final int xIndex = i * 2; + final int yIndex = xIndex + 1; + final ui.Offset point = points[i]; + assert(_offsetIsValid(point)); + result[xIndex] = point.dx; + result[yIndex] = point.dy; + } + return result; +} + +js.JsObject makeSkPointMode(ui.PointMode pointMode) { + switch (pointMode) { + case ui.PointMode.points: + return canvasKit['PointMode']['Points']; + case ui.PointMode.lines: + return canvasKit['PointMode']['Lines']; + case ui.PointMode.polygon: + return canvasKit['PointMode']['Polygon']; + default: + throw StateError('Unrecognized point mode $pointMode'); + } +} + js.JsObject makeSkBlendMode(ui.BlendMode blendMode) { switch (blendMode) { case ui.BlendMode.clear: @@ -122,6 +151,8 @@ js.JsObject makeSkBlendMode(ui.BlendMode blendMode) { } js.JsObject makeSkPaint(ui.Paint paint) { + if (paint == null) return null; + final dynamic skPaint = js.JsObject(canvasKit['SkPaint']); if (paint.shader != null) { diff --git a/lib/web_ui/lib/src/engine/compositor/vertices.dart b/lib/web_ui/lib/src/engine/compositor/vertices.dart index 7dfc609c9bef6..a0b9455dc3745 100644 --- a/lib/web_ui/lib/src/engine/compositor/vertices.dart +++ b/lib/web_ui/lib/src/engine/compositor/vertices.dart @@ -11,21 +11,6 @@ Int32List _encodeColorList(List colors) { return result; } -Float32List _encodePointList(List points) { - assert(points != null); - final int pointCount = points.length; - final Float32List result = Float32List(pointCount * 2); - for (int i = 0; i < pointCount; ++i) { - final int xIndex = i * 2; - final int yIndex = xIndex + 1; - final ui.Offset point = points[i]; - assert(_offsetIsValid(point)); - result[xIndex] = point.dx; - result[yIndex] = point.dy; - } - return result; -} - class SkVertices implements ui.Vertices { js.JsObject skVertices; final Int32List _colors; @@ -40,8 +25,9 @@ class SkVertices implements ui.Vertices { List indices, }) : assert(mode != null), assert(positions != null), - _colors = Int32List.fromList(colors.map((ui.Color c) => c.value).toList()), - _positions = _offsetListToInt32List(positions), + _colors = + Int32List.fromList(colors.map((ui.Color c) => c.value).toList()), + _positions = encodePointList(positions), _mode = mode { if (textureCoordinates != null && textureCoordinates.length != positions.length) @@ -54,9 +40,9 @@ class SkVertices implements ui.Vertices { throw ArgumentError( '"indices" values must be valid indices in the positions list.'); - final Float32List encodedPositions = _encodePointList(positions); + final Float32List encodedPositions = encodePointList(positions); final Float32List encodedTextureCoordinates = (textureCoordinates != null) - ? _encodePointList(textureCoordinates) + ? encodePointList(textureCoordinates) : null; final Int32List encodedColors = colors != null ? _encodeColorList(colors) : null; @@ -140,19 +126,6 @@ class SkVertices implements ui.Vertices { return encodedPoints; } - static Float32List _offsetListToInt32List(List offsetList) { - if (offsetList == null) { - return null; - } - final int length = offsetList.length; - final floatList = Float32List(length * 2); - for (int i = 0, destIndex = 0; i < length; i++, destIndex += 2) { - floatList[destIndex] = offsetList[i].dx; - floatList[destIndex + 1] = offsetList[i].dx; - } - return floatList; - } - @override Int32List get colors => _colors; diff --git a/lib/web_ui/lib/src/engine/picture.dart b/lib/web_ui/lib/src/engine/picture.dart new file mode 100644 index 0000000000000..9175a3d383147 --- /dev/null +++ b/lib/web_ui/lib/src/engine/picture.dart @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// An implementation of [ui.PictureRecorder] backed by a [RecordingCanvas]. +class EnginePictureRecorder implements ui.PictureRecorder { + EnginePictureRecorder(); + + RecordingCanvas _canvas; + ui.Rect cullRect; + bool _isRecording = false; + + RecordingCanvas beginRecording(ui.Rect bounds) { + assert(!_isRecording); + cullRect = bounds; + _isRecording = true; + _canvas = RecordingCanvas(cullRect); + return _canvas; + } + + @override + bool get isRecording => _isRecording; + + @override + ui.Picture endRecording() { + // Returning null is what the flutter engine does: + // lib/ui/painting/picture_recorder.cc + if (!_isRecording) { + return null; + } + _isRecording = false; + return EnginePicture(_canvas, cullRect); + } +} + +/// An implementation of [ui.Picture] which is backed by a [RecordingCanvas]. +class EnginePicture implements ui.Picture { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + /// + /// To create a [Picture], use a [PictureRecorder]. + EnginePicture(this.recordingCanvas, this.cullRect); + + @override + Future toImage(int width, int height) async { + final BitmapCanvas canvas = BitmapCanvas(ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble())); + recordingCanvas.apply(canvas); + final String imageDataUrl = canvas.canvas.toDataUrl(); + final html.ImageElement imageElement = html.ImageElement() + ..src = imageDataUrl + ..width = width + ..height = height; + return HtmlImage( + imageElement, + width, + height, + ); + } + + @override + void dispose() {} + + @override + int get approximateBytesUsed => 0; + + final RecordingCanvas recordingCanvas; + final ui.Rect cullRect; +} diff --git a/lib/web_ui/lib/src/engine/surface/picture.dart b/lib/web_ui/lib/src/engine/surface/picture.dart index 7186d8deb4c36..af7d20c61325f 100644 --- a/lib/web_ui/lib/src/engine/surface/picture.dart +++ b/lib/web_ui/lib/src/engine/surface/picture.dart @@ -340,7 +340,7 @@ abstract class PersistedPicture extends PersistedLeafSurface { final double dx; final double dy; - final ui.Picture picture; + final EnginePicture picture; final ui.Rect localPaintBounds; final int hints; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 16cfe749ca557..96f6409a8d057 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -55,7 +55,8 @@ class EngineWindow extends ui.Window { double windowInnerHeight; if (html.window.visualViewport != null) { windowInnerWidth = html.window.visualViewport.width * devicePixelRatio; - windowInnerHeight = html.window.visualViewport.height * devicePixelRatio; + windowInnerHeight = + html.window.visualViewport.height * devicePixelRatio; } else { windowInnerWidth = html.window.innerWidth * devicePixelRatio; windowInnerHeight = html.window.innerHeight * devicePixelRatio; @@ -281,12 +282,8 @@ class EngineWindow extends ui.Window { } } - final Rasterizer _rasterizer = experimentalUseSkia - ? Rasterizer(Surface((SkCanvas canvas) { - domRenderer.renderScene(canvas.htmlCanvas); - canvas.skSurface.callMethod('flush'); - })) - : null; + final Rasterizer _rasterizer = + experimentalUseSkia ? Rasterizer(Surface()) : null; } /// The window singleton. diff --git a/lib/web_ui/lib/src/ui/canvas.dart b/lib/web_ui/lib/src/ui/canvas.dart index 0b58e57b44226..c340e443ee24a 100644 --- a/lib/web_ui/lib/src/ui/canvas.dart +++ b/lib/web_ui/lib/src/ui/canvas.dart @@ -165,7 +165,7 @@ class Vertices { /// /// To begin recording, construct a [Canvas] to record the commands. /// To end recording, use the [PictureRecorder.endRecording] method. -class PictureRecorder { +abstract class PictureRecorder { /// Creates a new idle PictureRecorder. To associate it with a /// [Canvas] and begin recording, pass this [PictureRecorder] to the /// [Canvas] constructor. @@ -173,24 +173,10 @@ class PictureRecorder { if (engine.experimentalUseSkia) { return engine.SkPictureRecorder(); } else { - return PictureRecorder._(); + return engine.EnginePictureRecorder(); } } - PictureRecorder._(); - - engine.RecordingCanvas _canvas; - Rect cullRect; - bool _isRecording = false; - - engine.RecordingCanvas beginRecording(Rect bounds) { - assert(!_isRecording); - cullRect = bounds; - _isRecording = true; - _canvas = engine.RecordingCanvas(cullRect); - return _canvas; - } - /// Whether this object is currently recording commands. /// /// Specifically, this returns true if a [Canvas] object has been @@ -198,7 +184,7 @@ class PictureRecorder { /// call to [endRecording], and false if either this /// [PictureRecorder] has not yet been associated with a [Canvas], /// or the [endRecording] method has already been called. - bool get isRecording => _isRecording; + bool get isRecording; /// Finishes recording graphical operations. /// @@ -207,15 +193,7 @@ class PictureRecorder { /// and the canvas objects are invalid and cannot be used further. /// /// Returns null if the PictureRecorder is not associated with a canvas. - Picture endRecording() { - // Returning null is what the flutter engine does: - // lib/ui/painting/picture_recorder.cc - if (!_isRecording) { - return null; - } - _isRecording = false; - return Picture._(_canvas, cullRect); - } + Picture endRecording(); } /// An interface for recording graphical operations. @@ -238,6 +216,14 @@ class PictureRecorder { class Canvas { engine.RecordingCanvas _canvas; + factory Canvas(PictureRecorder recorder, [Rect cullRect]) { + if (engine.experimentalUseSkia) { + return engine.CanvasKitCanvas(recorder, cullRect); + } else { + return Canvas._(recorder, cullRect); + } + } + /// Creates a canvas for recording graphical operations into the /// given picture recorder. /// @@ -250,7 +236,8 @@ class Canvas { /// /// To end the recording, call [PictureRecorder.endRecording] on the /// given recorder. - Canvas(PictureRecorder recorder, [Rect cullRect]) : assert(recorder != null) { + Canvas._(engine.EnginePictureRecorder recorder, [Rect cullRect]) + : assert(recorder != null) { if (recorder.isRecording) { throw ArgumentError( '"recorder" must not already be associated with another Canvas.'); @@ -1054,13 +1041,7 @@ class Canvas { /// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via /// the [SceneBuilder.addPicture] method. A [Picture] can also be /// drawn into a [Canvas], using the [Canvas.drawPicture] method. -class Picture { - /// This class is created by the engine, and should not be instantiated - /// or extended directly. - /// - /// To create a [Picture], use a [PictureRecorder]. - Picture._(this.recordingCanvas, this.cullRect); - +abstract class Picture { /// Creates an image from this picture. /// /// The returned image will be `width` pixels wide and `height` pixels high. @@ -1069,33 +1050,17 @@ class Picture { /// /// Although the image is returned synchronously, the picture is actually /// rasterized the first time the image is drawn and then cached. - Future toImage(int width, int height) async { - final engine.BitmapCanvas canvas = engine.BitmapCanvas(Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble())); - recordingCanvas.apply(canvas); - final String imageDataUrl = canvas.canvas.toDataUrl(); - final html.ImageElement imageElement = html.ImageElement() - ..src = imageDataUrl - ..width = width - ..height = height; - return engine.HtmlImage( - imageElement, - width, - height, - ); - } + Future toImage(int width, int height); /// Release the resources used by this object. The object is no longer usable /// after this method is called. - void dispose() {} + void dispose(); /// Returns the approximate number of bytes allocated for this object. /// /// The actual size of this picture may be larger, particularly if it contains /// references to image or other large objects. - int get approximateBytesUsed => 0; - - final engine.RecordingCanvas recordingCanvas; - final Rect cullRect; + int get approximateBytesUsed; } /// Determines the winding rule that decides how the interior of a [Path] is diff --git a/lib/web_ui/test/compositing_test.dart b/lib/web_ui/test/compositing_test.dart index a2be375256cb0..ea0b98901c55c 100644 --- a/lib/web_ui/test/compositing_test.dart +++ b/lib/web_ui/test/compositing_test.dart @@ -255,7 +255,7 @@ void testLayerLifeCycle( class MockPersistedPicture extends PersistedPicture { factory MockPersistedPicture() { - final PictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = PictureRecorder(); // Use the largest cull rect so that layer clips are effective. The tests // rely on this. recorder.beginRecording(Rect.largest)..drawPaint(Paint()); diff --git a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart index 6334ea11f8492..4c0bd86058428 100644 --- a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart @@ -494,7 +494,7 @@ void _testCullRectComputation() { } void _drawTestPicture(SceneBuilder builder) { - final PictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = PictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); canvas.drawCircle( @@ -529,7 +529,7 @@ typedef PaintCallback = void Function(RecordingCanvas canvas); void drawWithBitmapCanvas(SceneBuilder builder, PaintCallback callback, {Rect bounds = Rect.largest}) { - final PictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = PictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(bounds); canvas.debugEnforceArbitraryPaint(); diff --git a/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart b/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart index 8fc1f01adbc1a..e0350fff97736 100644 --- a/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart @@ -13,7 +13,7 @@ import 'package:web_engine_tester/golden_tester.dart'; void main() { group('Picture', () { test('toImage produces an image', () async { - final ui.PictureRecorder recorder = ui.PictureRecorder(); + final EnginePictureRecorder recorder = ui.PictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(ui.Rect.fromLTRB(0, 0, 200, 100)); canvas.drawCircle( const ui.Offset(100, 50), From 96cba6c92a8aac3908d5146b8396ed97bde49410 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 25 Nov 2019 21:47:53 -0500 Subject: [PATCH 261/591] Roll src/third_party/skia 8fa469d3bcd6..4f90f9d83183 (4 commits) (#14012) https://skia.googlesource.com/skia.git/+log/8fa469d3bcd6..4f90f9d83183 git log 8fa469d3bcd6..4f90f9d83183 --date=short --first-parent --format='%ad %ae %s' 2019-11-25 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-25 reed@google.com switch to new filltype for SkPath 2019-11-25 egdaniel@google.com When wrapped, store ref to GrVkSecondaryCommandBuffer on GrVkRenderTarget. 2019-11-25 jvanverth@google.com Revert "Rename GLRTFBOIDIs0 to WrapsSwapchainSurface and use for Metal." Created with: gclient setdep -r src/third_party/skia@4f90f9d83183 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 68f6badf9adec..df324ed61d36a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8fa469d3bcd68ec95207558a3e9d7af95991cf4b', + 'skia_revision': '4f90f9d831834e3abaab9893d186c5df2ea793b9', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 56f7d7a4dca7f..2ab31e915e952 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 44ae6068dc41be876d6b25233fd384cf +Signature: dea2b8dccdd1074ee1223882827f1fff UNUSED LICENSES: From e87b5bed28a64c9e63b5715f01c322ef59fce805 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 26 Nov 2019 01:59:49 -0500 Subject: [PATCH 262/591] Roll src/third_party/skia 4f90f9d83183..54036c9b8e4b (4 commits) (#14013) https://skia.googlesource.com/skia.git/+log/4f90f9d83183..54036c9b8e4b git log 4f90f9d83183..54036c9b8e4b --date=short --first-parent --format='%ad %ae %s' 2019-11-26 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 0ec8ef3c9f4f..92fe999ae056 (465 commits) 2019-11-26 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader d44d61514749..f2637d0dd7eb (9 commits) 2019-11-26 reed@google.com Revert "switch to new filltype for SkPath" 2019-11-26 fmalita@chromium.org [skottie-wasm] Disable image pre-decoding Created with: gclient setdep -r src/third_party/skia@54036c9b8e4b If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index df324ed61d36a..2fbada9c5c714 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4f90f9d831834e3abaab9893d186c5df2ea793b9', + 'skia_revision': '54036c9b8e4b264160d7d7cfe1c231a1af689d22', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2ab31e915e952..09d1c82378a8c 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: dea2b8dccdd1074ee1223882827f1fff +Signature: 915e37ad8c228d765d92780fbc93875f UNUSED LICENSES: From e136d637a87379658ee8f326c4e7875315d1f926 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 26 Nov 2019 02:08:44 -0500 Subject: [PATCH 263/591] Roll fuchsia/sdk/core/mac-amd64 from 1nxSz... to Bxgic... (#14014) Roll fuchsia/sdk/core/mac-amd64 from 1nxSz... to Bxgic... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 2fbada9c5c714..ae95bd2398352 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '1nxSz1QqLbQZnQOtHW1T-g5BCbDWuM9lK7AJK92M3vsC' + 'version': 'BxgichKeSjx2QXPI4uREU7V-7yDVUZFHlcuvXJmzK90C' } ], 'condition': 'host_os == "mac"', From 0a82657c1cfde9383806b0729ea29e6717beb3aa Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 26 Nov 2019 02:46:07 -0500 Subject: [PATCH 264/591] Roll fuchsia/sdk/core/linux-amd64 from hK-BD... to kzFUw... (#14015) Roll fuchsia/sdk/core/linux-amd64 from hK-BD... to kzFUw... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 18 +----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/DEPS b/DEPS index ae95bd2398352..721c1dab52c3d 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'hK-BDfkZM0WodvMhp8AgZTZjg71JkcNSeB3zIIVArXQC' + 'version': 'kzFUwRXXe_pIdY0broF7oK_TO_YTz4k7Sj8g8M8qnToC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index ca1ca01d3054d..4bb2d86695272 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 7a0361087fcc8ddbe4707acaefe438ea +Signature: aeb7438cd2fa58d40202faeb0b5c0224 UNUSED LICENSES: @@ -20,7 +20,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_unique_objects.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libVkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsyslog.so @@ -30,7 +29,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libvulkan.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsync.a @@ -230,7 +228,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_unique_objects.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libVkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsyslog.so @@ -240,7 +237,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libvulkan.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsync.a @@ -540,7 +536,6 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/inspect-common/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/meta.json @@ -715,7 +710,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_unique_objects.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libVkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsyslog.so @@ -725,7 +719,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libvulkan.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsync.a @@ -971,7 +964,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_unique_objects.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libVkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsyslog.so @@ -981,7 +973,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libvulkan.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsync.a @@ -1532,8 +1523,6 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/string_view.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/utility_internal.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/inspect-common/include/lib/inspect/common.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect-common/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/health.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/hierarchy.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/health.h @@ -1683,7 +1672,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_unique_objects.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libVkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libsyslog.so @@ -1693,7 +1681,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libvulkan.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/lib/libsync.a @@ -1893,7 +1880,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_unique_objects.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libVkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libsyslog.so @@ -1903,7 +1889,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libvulkan.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-default.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libasync-loop-default.a FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libfdio.so -FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libinspect-common.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libmemfs.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsvc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/lib/libsync.a @@ -2203,7 +2188,6 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/inspect-common/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/meta.json From 27fa60793bcb870ebd3a5f6e940005ddb50eae4f Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Tue, 26 Nov 2019 13:10:24 -0800 Subject: [PATCH 265/591] Added docstring for FlutterOverlayView. (#14019) --- .../darwin/ios/framework/Source/FlutterOverlayView.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h index fac783f87f33f..0ace741ccab80 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h @@ -17,6 +17,17 @@ #include "flutter/shell/platform/darwin/ios/ios_surface.h" #include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +/// UIViews that are used by |FlutterPlatformViews| to present Flutter +/// rendering on top of system compositor rendering (ex. a web view). +/// +/// When there is a view composited by the system compositor within a Flutter +/// view hierarchy, instead of rendering into a single render target, Flutter +/// renders into multiple render targets (depending on the number of +/// interleaving levels between Flutter & non-Flutter contents). While the +/// FlutterView contains the backing store for the root render target, the +/// FlutterOverlay view contains the backing stores for the rest. The overlay +/// views also handle touch propagation and the like for touches that occurs +/// either on overlays or otherwise may be intercepted by the platform views. @interface FlutterOverlayView : UIView - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; From ca68af252c850ece1cfae10c1a9fc4fec461215a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 26 Nov 2019 13:33:56 -0800 Subject: [PATCH 266/591] Add a separate target for Dart coverter on FML types. (#14011) The converters are still in a separate target that must be included manually. This allows targets that depend on FML but not Dart runtime not have to depend on the runtime. Adds a test that includes this target and tests image decompression from assets. There is also a test for the standalone DartConvertor in shell_unittests but not in fml_unittests be cause FML uni-tests cannot yet launch a VM. I will work on adding fixtures for those. --- ci/licenses_golden/licenses_flutter | 2 + fml/BUILD.gn | 3 +- fml/dart/BUILD.gn | 19 ++++ fml/dart/dart_converter.cc | 11 ++ fml/dart/dart_converter.h | 123 +++++++++++++++++++++++ shell/common/BUILD.gn | 1 + shell/common/fixtures/shell_test.dart | 17 ++++ shell/common/shell_unittests.cc | 139 ++++++++++++++++++++++++++ testing/testing.cc | 4 + testing/testing.h | 43 +++++++- 10 files changed, 358 insertions(+), 4 deletions(-) create mode 100644 fml/dart/BUILD.gn create mode 100644 fml/dart/dart_converter.cc create mode 100644 fml/dart/dart_converter.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 16ba00d5d0238..37391a4f53217 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -98,6 +98,8 @@ FILE: ../../../flutter/fml/command_line_unittest.cc FILE: ../../../flutter/fml/compiler_specific.h FILE: ../../../flutter/fml/concurrent_message_loop.cc FILE: ../../../flutter/fml/concurrent_message_loop.h +FILE: ../../../flutter/fml/dart/dart_converter.cc +FILE: ../../../flutter/fml/dart/dart_converter.h FILE: ../../../flutter/fml/delayed_task.cc FILE: ../../../flutter/fml/delayed_task.h FILE: ../../../flutter/fml/eintr_wrapper.h diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 32f58eddf6056..68b9725fa6b2e 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -249,8 +249,9 @@ executable("fml_unittests") { deps = [ ":fml_fixtures", "$flutter_root/fml", + "$flutter_root/fml/dart", + "$flutter_root/runtime:libdart", "$flutter_root/testing", - "//third_party/dart/runtime:libdart_jit", ] } diff --git a/fml/dart/BUILD.gn b/fml/dart/BUILD.gn new file mode 100644 index 0000000000000..c6b3883955e81 --- /dev/null +++ b/fml/dart/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Utilities for working with FML types with the Dart API. Targets that use FML +# as well as Dart must explicitly include this target as FML itself cannot +# depend on Dart. +source_set("dart") { + sources = [ + "dart_converter.cc", + "dart_converter.h", + ] + + public_deps = [ + "$flutter_root/fml", + "$flutter_root/runtime:libdart", + "//third_party/tonic", + ] +} diff --git a/fml/dart/dart_converter.cc b/fml/dart/dart_converter.cc new file mode 100644 index 0000000000000..7e17bd8bb3c30 --- /dev/null +++ b/fml/dart/dart_converter.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/dart/dart_converter.h" + +namespace fml { + +// + +} // namespace fml diff --git a/fml/dart/dart_converter.h b/fml/dart/dart_converter.h new file mode 100644 index 0000000000000..159ec44e25c31 --- /dev/null +++ b/fml/dart/dart_converter.h @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_DART_DART_CONVERTER_H_ +#define FLUTTER_FML_DART_DART_CONVERTER_H_ + +#include +#include + +#include "flutter/fml/mapping.h" +#include "third_party/dart/runtime/include/dart_api.h" +#include "third_party/tonic/converter/dart_converter.h" + +namespace tonic { + +using DartConverterMapping = std::unique_ptr; + +template <> +struct DartConverter { + static Dart_Handle ToDart(const DartConverterMapping& val) { + if (!val) { + return Dart_Null(); + } + + auto dart_list_handle = Dart_NewListOf(Dart_CoreType_Int, val->GetSize()); + + if (Dart_IsError(dart_list_handle)) { + FML_LOG(ERROR) << "Error while attempting to allocate a list: " + << Dart_GetError(dart_list_handle); + return dart_list_handle; + } + + if (val->GetSize() == 0) { + // Nothing to copy. Just return the zero sized list. + return dart_list_handle; + } + + auto result = Dart_ListSetAsBytes(dart_list_handle, // list + 0, // offset + val->GetMapping(), // native array, + val->GetSize() // length + ); + + if (Dart_IsError(result)) { + FML_LOG(ERROR) << "Error while attempting to create a Dart list: " + << Dart_GetError(result); + return result; + } + + return dart_list_handle; + } + + static void SetReturnValue(Dart_NativeArguments args, + const DartConverterMapping& val) { + Dart_SetReturnValue(args, ToDart(val)); + } + + static DartConverterMapping FromDart(Dart_Handle dart_list) { + if (Dart_IsNull(dart_list)) { + return nullptr; + } + + if (Dart_IsError(dart_list)) { + FML_LOG(ERROR) << "Cannot convert an error handle to a list: " + << Dart_GetError(dart_list); + return nullptr; + } + + if (!Dart_IsList(dart_list)) { + FML_LOG(ERROR) << "Dart handle was not a list."; + return nullptr; + } + + intptr_t length = 0; + auto handle = Dart_ListLength(dart_list, &length); + + if (Dart_IsError(handle)) { + FML_LOG(ERROR) << "Could not get the length of the Dart list: " + << Dart_GetError(handle); + return nullptr; + } + + if (length == 0) { + // Return a valid zero sized mapping. + return std::make_unique(nullptr, 0); + } + + auto mapping_buffer = ::malloc(length); + + if (!mapping_buffer) { + FML_LOG(ERROR) + << "Out of memory while attempting to allocate a mapping of size: " + << length; + return nullptr; + } + + auto mapping = std::make_unique( + static_cast(mapping_buffer), length, + [](const uint8_t* data, size_t size) { + ::free(const_cast(data)); + }); + + handle = Dart_ListGetAsBytes( + dart_list, // list + 0, // offset + static_cast(mapping_buffer), // native array + length // length + ); + + if (Dart_IsError(handle)) { + FML_LOG(ERROR) << "Could not copy Dart list to native buffer: " + << Dart_GetError(handle); + return nullptr; + } + + return mapping; + } +}; + +} // namespace tonic + +#endif // FLUTTER_FML_DART_DART_CONVERTER_H_ diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 9d57ee5ff5ecf..1564547bd34d1 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -173,6 +173,7 @@ if (current_toolchain == host_toolchain) { ":shell_unittests_gpu_configuration", "$flutter_root/common", "$flutter_root/flow", + "$flutter_root/fml/dart", "$flutter_root/lib/ui:ui", "$flutter_root/shell", "$flutter_root/testing:dart", diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index c6873995b4885..8f8dcab38cbda 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -121,3 +121,20 @@ void canAccessIsolateLaunchData() { } void notifyMessage(String string) native 'NotifyMessage'; + +@pragma('vm:entry-point') +void canConvertMappings() { + sendFixtureMapping(getFixtureMapping()); +} + +List getFixtureMapping() native 'GetFixtureMapping'; +void sendFixtureMapping(List list) native 'SendFixtureMapping'; + +@pragma('vm:entry-point') +void canDecompressImageFromAsset() { + decodeImageFromList(Uint8List.fromList(getFixtureImage()), (Image result) { + notifyWidthHeight(result.width, result.height); + }); +} + +List getFixtureImage() native 'GetFixtureImage'; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index b69bf226b5c61..636b879178d88 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -13,6 +13,7 @@ #include "flutter/flow/layers/picture_layer.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/fml/command_line.h" +#include "flutter/fml/dart/dart_converter.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/synchronization/count_down_latch.h" @@ -1014,5 +1015,143 @@ TEST_F(ShellTest, Screenshot) { DestroyShell(std::move(shell)); } +enum class MemsetPatternOp { + kMemsetPatternOpSetBuffer, + kMemsetPatternOpCheckBuffer, +}; + +//------------------------------------------------------------------------------ +/// @brief Depending on the operation, either scribbles a known pattern +/// into the buffer or checks if that pattern is present in an +/// existing buffer. This is a portable variant of the +/// memset_pattern class of methods that also happen to do assert +/// that the same pattern exists. +/// +/// @param buffer The buffer +/// @param[in] size The size +/// @param[in] op The operation +/// +/// @return If the result of the operation was a success. +/// +static bool MemsetPatternSetOrCheck(uint8_t* buffer, + size_t size, + MemsetPatternOp op) { + if (buffer == nullptr) { + return false; + } + + auto pattern = reinterpret_cast("dErP"); + constexpr auto pattern_length = 4; + + uint8_t* start = buffer; + uint8_t* p = buffer; + + while ((start + size) - p >= pattern_length) { + switch (op) { + case MemsetPatternOp::kMemsetPatternOpSetBuffer: + memmove(p, pattern, pattern_length); + break; + case MemsetPatternOp::kMemsetPatternOpCheckBuffer: + if (memcmp(pattern, p, pattern_length) != 0) { + return false; + } + break; + }; + p += pattern_length; + } + + if ((start + size) - p != 0) { + switch (op) { + case MemsetPatternOp::kMemsetPatternOpSetBuffer: + memmove(p, pattern, (start + size) - p); + break; + case MemsetPatternOp::kMemsetPatternOpCheckBuffer: + if (memcmp(pattern, p, (start + size) - p) != 0) { + return false; + } + break; + } + } + + return true; +} + +TEST_F(ShellTest, CanConvertToAndFromMappings) { + const size_t buffer_size = 2 << 20; + + uint8_t* buffer = static_cast(::malloc(buffer_size)); + ASSERT_NE(buffer, nullptr); + ASSERT_TRUE(MemsetPatternSetOrCheck( + buffer, buffer_size, MemsetPatternOp::kMemsetPatternOpSetBuffer)); + + std::unique_ptr mapping = + std::make_unique( + buffer, buffer_size, [](const uint8_t* buffer, size_t size) { + ::free(const_cast(buffer)); + }); + + ASSERT_EQ(mapping->GetSize(), buffer_size); + + fml::AutoResetWaitableEvent latch; + AddNativeCallback( + "SendFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) { + auto mapping_from_dart = + tonic::DartConverter>::FromDart( + Dart_GetNativeArgument(args, 0)); + ASSERT_NE(mapping_from_dart, nullptr); + ASSERT_EQ(mapping_from_dart->GetSize(), buffer_size); + ASSERT_TRUE(MemsetPatternSetOrCheck( + const_cast(mapping_from_dart->GetMapping()), // buffer + mapping_from_dart->GetSize(), // size + MemsetPatternOp::kMemsetPatternOpCheckBuffer // op + )); + latch.Signal(); + })); + + AddNativeCallback( + "GetFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) { + tonic::DartConverter::SetReturnValue( + args, mapping); + })); + + auto settings = CreateSettingsForFixture(); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("canConvertMappings"); + std::unique_ptr shell = CreateShell(settings); + ASSERT_NE(shell.get(), nullptr); + RunEngine(shell.get(), std::move(configuration)); + latch.Wait(); + DestroyShell(std::move(shell)); +} + +TEST_F(ShellTest, CanDecompressImageFromAsset) { + fml::AutoResetWaitableEvent latch; + AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) { + auto width = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 0)); + auto height = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 1)); + ASSERT_EQ(width, 100); + ASSERT_EQ(height, 100); + latch.Signal(); + })); + + AddNativeCallback( + "GetFixtureImage", CREATE_NATIVE_ENTRY([](auto args) { + auto fixture = OpenFixtureAsMapping("shelltest_screenshot.png"); + tonic::DartConverter::SetReturnValue( + args, fixture); + })); + + auto settings = CreateSettingsForFixture(); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("canDecompressImageFromAsset"); + std::unique_ptr shell = CreateShell(settings); + ASSERT_NE(shell.get(), nullptr); + RunEngine(shell.get(), std::move(configuration)); + latch.Wait(); + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter diff --git a/testing/testing.cc b/testing/testing.cc index 303ff388d6464..b5e229f06bb5c 100644 --- a/testing/testing.cc +++ b/testing/testing.cc @@ -49,5 +49,9 @@ fml::UniqueFD OpenFixture(std::string fixture_name) { return fixture_fd; } +std::unique_ptr OpenFixtureAsMapping(std::string fixture_name) { + return fml::FileMapping::CreateReadOnly(OpenFixture(fixture_name)); +} + } // namespace testing } // namespace flutter diff --git a/testing/testing.h b/testing/testing.h index 9f0fba0349247..da8571b18b76e 100644 --- a/testing/testing.h +++ b/testing/testing.h @@ -8,21 +8,58 @@ #include #include "flutter/fml/file.h" +#include "flutter/fml/mapping.h" #include "flutter/testing/assertions.h" #include "gtest/gtest.h" namespace flutter { namespace testing { -// Returns the directory containing the test fixture for the target if this -// target has fixtures configured. If there are no fixtures, this is a link -// error. +//------------------------------------------------------------------------------ +/// @brief Returns the directory containing the test fixture for the target +/// if this target has fixtures configured. If there are no +/// fixtures, this is a link error. If you see a linker error on +/// this symbol, the unit-test target needs to depend on a +/// `test_fixtures` target. +/// +/// @return The fixtures path. +/// const char* GetFixturesPath(); +//------------------------------------------------------------------------------ +/// @brief Opens the fixtures directory for the unit-test harness. +/// +/// @return The file descriptor of the fixtures directory. +/// fml::UniqueFD OpenFixturesDirectory(); +//------------------------------------------------------------------------------ +/// @brief Opens a fixture of the given file name. +/// +/// @param[in] fixture_name The fixture name +/// +/// @return The file descriptor of the given fixture. An invalid file +/// descriptor is returned in case the fixture is not found. +/// fml::UniqueFD OpenFixture(std::string fixture_name); +//------------------------------------------------------------------------------ +/// @brief Opens a fixture of the given file name and returns a mapping to +/// its contents. +/// +/// @param[in] fixture_name The fixture name +/// +/// @return A mapping to the contents of fixture or null if the fixture does +/// not exist or its contents cannot be mapped in. +/// +std::unique_ptr OpenFixtureAsMapping(std::string fixture_name); + +//------------------------------------------------------------------------------ +/// @brief Gets the name of the currently running test. This is useful in +/// generating logs or assets based on test name. +/// +/// @return The current test name. +/// std::string GetCurrentTestName(); } // namespace testing From 9de48de0663edf0eda0572a92bd194b1b338119b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 26 Nov 2019 16:52:06 -0500 Subject: [PATCH 267/591] Roll fuchsia/sdk/core/linux-amd64 from kzFUw... to xyyOR... (#14021) Roll fuchsia/sdk/core/linux-amd64 from kzFUw... to xyyOR... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/DEPS b/DEPS index 721c1dab52c3d..0e55eee34472d 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'kzFUwRXXe_pIdY0broF7oK_TO_YTz4k7Sj8g8M8qnToC' + 'version': 'xyyORYHKSpVC7v-Z2qPHQvKfpeKCZalbSfkvEsKSMjkC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 4bb2d86695272..6f2710f3f8191 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: aeb7438cd2fa58d40202faeb0b5c0224 +Signature: c495a1a3d63c57a6590969a0687f03e5 UNUSED LICENSES: @@ -470,10 +470,10 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ledger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json @@ -1360,11 +1360,12 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/hwinfo.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/image_pipe2.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/tree.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/property_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ledger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json @@ -1528,6 +1529,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/inspect/hierarchy.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/health.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/hierarchy.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/reader.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/value_list.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/block.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/scanner.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/snapshot.h @@ -2122,10 +2124,10 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ledger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json @@ -3203,7 +3205,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/types. FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/types.h FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/interface.dart FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/font_provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ledger/ledger.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/math.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.playback/problem.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.playback/seeking_reader.fidl From 6e5f4dca8e6cf0e1d8bf8dfbed76368fc456bf50 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 26 Nov 2019 16:56:14 -0500 Subject: [PATCH 268/591] Roll fuchsia/sdk/core/mac-amd64 from Bxgic... to aVdHP... (#14017) Roll fuchsia/sdk/core/mac-amd64 from Bxgic... to aVdHP... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 0e55eee34472d..4c7002c90e97d 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'BxgichKeSjx2QXPI4uREU7V-7yDVUZFHlcuvXJmzK90C' + 'version': 'aVdHPkA77NjC6uFR_fhWb5gjpTaUlIxQ1TE_rWB5zCQC' } ], 'condition': 'host_os == "mac"', From 5250d6315e7a18b1016942ee249caacc7469ef3b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 26 Nov 2019 17:01:05 -0500 Subject: [PATCH 269/591] Roll src/third_party/skia 54036c9b8e4b..40a7dfc268b6 (1 commits) (#14016) https://skia.googlesource.com/skia.git/+log/54036c9b8e4b..40a7dfc268b6 git log 54036c9b8e4b..40a7dfc268b6 --date=short --first-parent --format='%ad %ae %s' 2019-11-26 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 e9b68f332a30..f65b212492ec (10 commits) Created with: gclient setdep -r src/third_party/skia@40a7dfc268b6 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4c7002c90e97d..33e2bb25c441b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '54036c9b8e4b264160d7d7cfe1c231a1af689d22', + 'skia_revision': '40a7dfc268b697eb3db1981ce38c1b6f18507b42', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 09d1c82378a8c..1d14da7521083 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 915e37ad8c228d765d92780fbc93875f +Signature: 396dca43669faa32136d5c09b69aab0d UNUSED LICENSES: From c139def52f064c0434d7f9ca3e81e0a590f1c7f4 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 26 Nov 2019 14:47:00 -0800 Subject: [PATCH 270/591] Avoid retaining self in block (#14022) --- shell/platform/darwin/ios/framework/Source/FlutterEngine.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index c2af9bfd66ace..4e9ea96d03de4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -295,9 +295,13 @@ - (void)resetChannels { - (void)setupChannels { // This will be invoked once the shell is done setting up and the isolate ID // for the UI isolate is available. + fml::WeakPtr weakSelf = [self getWeakPtr]; [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate" binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) { - self.isolateId = [[FlutterStringCodec sharedInstance] decode:message]; + if (weakSelf) { + weakSelf.get().isolateId = + [[FlutterStringCodec sharedInstance] decode:message]; + } }]; _localizationChannel.reset([[FlutterMethodChannel alloc] From 2c0eee43923fb1ee140d9a55549965e0daf748ca Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 27 Nov 2019 07:51:04 +0900 Subject: [PATCH 271/591] Always set mEditable values when different in TextPlugin (#13951) --- .../plugin/editing/TextInputPlugin.java | 18 +++++++++++---- .../plugin/editing/TextInputPluginTest.java | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 476592669fb0e..e66e27053ee68 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -102,6 +102,10 @@ public InputMethodManager getInputMethodManager() { return mImm; } + @VisibleForTesting Editable getEditable() { + return mEditable; + } + /*** * Use the current platform view input connection until unlockPlatformViewInputConnection is called. * @@ -306,15 +310,21 @@ private void applyStateToSelection(TextInputChannel.TextEditState state) { } @VisibleForTesting void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { - if (!restartAlwaysRequired && !mRestartInputPending && state.text.equals(mEditable.toString())) { - applyStateToSelection(state); + // Always replace the contents of mEditable if the text differs + if (!state.text.equals(mEditable.toString())) { + mEditable.replace(0, mEditable.length(), state.text); + } + // Always apply state to selection which handles updating the selection if needed. + applyStateToSelection(state); + // Use updateSelection to update imm on selection if it is not neccessary to restart. + if (!restartAlwaysRequired && !mRestartInputPending) { mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0), Math.max(Selection.getSelectionEnd(mEditable), 0), BaseInputConnection.getComposingSpanStart(mEditable), BaseInputConnection.getComposingSpanEnd(mEditable)); + // Restart if there is a pending restart or the device requires a force restart + // (see isRestartAlwaysRequired). Restarting will also update the selection. } else { - mEditable.replace(0, mEditable.length(), state.text); - applyStateToSelection(state); mImm.restartInput(view); mRestartInputPending = false; } diff --git a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java index 96bf9e39d2b28..e4cd753b8fa39 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -29,6 +29,7 @@ import io.flutter.plugin.platform.PlatformViewsController; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -73,6 +74,27 @@ public void setTextInputEditingState_doesNotRestartWhenTextIsIdentical() { assertEquals(1, testImm.getRestartCount(testView)); } + @Test + public void setTextInputEditingState_alwaysSetEditableWhenDifferent() { + // Initialize a general TextInputPlugin. + InputMethodSubtype inputMethodSubtype = mock(InputMethodSubtype.class); + TestImm testImm = Shadow.extract(RuntimeEnvironment.application.getSystemService(Context.INPUT_METHOD_SERVICE)); + testImm.setCurrentInputMethodSubtype(inputMethodSubtype); + View testView = new View(RuntimeEnvironment.application); + TextInputPlugin textInputPlugin = new TextInputPlugin(testView, mock(DartExecutor.class), mock(PlatformViewsController.class)); + textInputPlugin.setTextInputClient(0, new TextInputChannel.Configuration(false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + // There's a pending restart since we initialized the text input client. Flush that now. With changed text, we should + // always set the Editable contents. + textInputPlugin.setTextInputEditingState(testView, new TextInputChannel.TextEditState("hello", 0, 0)); + assertEquals(1, testImm.getRestartCount(testView)); + assertTrue(textInputPlugin.getEditable().toString().equals("hello")); + + // No pending restart, set Editable contents anyways. + textInputPlugin.setTextInputEditingState(testView, new TextInputChannel.TextEditState("Shibuyawoo", 0, 0)); + assertEquals(1, testImm.getRestartCount(testView)); + assertTrue(textInputPlugin.getEditable().toString().equals("Shibuyawoo")); + } + // See https://github.com/flutter/flutter/issues/29341 and https://github.com/flutter/flutter/issues/31512 // All modern Samsung keybords are affected including non-korean languages and thus // need the restart. From e3e5f8dabc2e8338a028accdb26a9d13180c1119 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Tue, 26 Nov 2019 18:59:26 -0800 Subject: [PATCH 272/591] Adding support for enabling semantics on desktop (#14003) * DRAFT: adding support for enabling semantics on desktop * Working solution for desktop. * Refactoring class structure. Removing unrelated comments. * Fixing enabling semantics from dom_renderer * Adding unit tests.Fixing failing cases. More refactoring. * more work on tests. * Fixing licences * addressing PR comments --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 1 + .../lib/src/engine/browser_detection.dart | 16 + lib/web_ui/lib/src/engine/dom_renderer.dart | 13 +- .../lib/src/engine/semantics/semantics.dart | 199 +-------- .../engine/semantics/semantics_helper.dart | 385 ++++++++++++++++++ lib/web_ui/test/dom_renderer_test.dart | 7 + .../semantics/semantics_helper_test.dart | 147 +++++++ 8 files changed, 573 insertions(+), 196 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/semantics/semantics_helper.dart create mode 100644 lib/web_ui/test/engine/semantics/semantics_helper_test.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 37391a4f53217..c296ebe416fbd 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -415,6 +415,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/label_and_value.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/live_region.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/scrollable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/tappable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/text_field.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/buffers.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 4aa372e1c91a6..da577462e95dc 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -73,6 +73,7 @@ part 'engine/semantics/label_and_value.dart'; part 'engine/semantics/live_region.dart'; part 'engine/semantics/scrollable.dart'; part 'engine/semantics/semantics.dart'; +part 'engine/semantics/semantics_helper.dart'; part 'engine/semantics/tappable.dart'; part 'engine/semantics/text_field.dart'; part 'engine/services/buffers.dart'; diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index fffcadeef6e57..5d0f55d4a2db3 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -121,3 +121,19 @@ OperatingSystem _detectOperatingSystem() { return OperatingSystem.unknown; } } + +/// List of Operating Systems we know to be working on laptops/desktops. +/// +/// These devices tend to behave differently on many core issues such as events, +/// screen readers, input devices. +const Set _desktopOperatingSystems = { + OperatingSystem.macOs, + OperatingSystem.linux, + OperatingSystem.windows, +}; + +/// A flag to check if the current operating system is a laptop/desktop +/// operating system. +/// +/// See [_desktopOperatingSystems]. +bool get isDesktop => _desktopOperatingSystems.contains(operatingSystem); diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 4152e5e5752a2..90c10b40d55e0 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -389,7 +389,18 @@ flt-glass-pane * { _glassPaneElement.append(_sceneHostElement); - EngineSemanticsOwner.instance.autoEnableOnTap(this); + final html.Element _accesibilityPlaceholder = EngineSemanticsOwner + .instance.enableSemantics + .prepareAccesibilityPlaceholder(); + + // Insert the semantics placeholder after the scene host. For all widgets + // in the scene, except for platform widgets, the scene host will pass the + // pointer events through to the semantics tree. However, for platform + // views, the pointer events will not pass through, and will be handled + // by the platform view. + glassPaneElement + .insertBefore(_accesibilityPlaceholder, _sceneHostElement); + PointerBinding(this); // Hide the DOM nodes used to render the scene from accessibility, because diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 6005d288ea8b3..17f30262939d5 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -1198,195 +1198,7 @@ class EngineSemanticsOwner { _now = () => DateTime.now(); } - /// A temporary placeholder used to capture a request to activate semantics. - html.Element _semanticsPlaceholder; - - /// We do not immediately enable semantics when the user requests it, but - /// instead wait for a short period of time before doing it. This is because - /// the request comes as a tap on the [_semanticsPlaceholder]. The tap, - /// depending on the browser, comes as a burst of events. For example, Safari - /// sends "touchstart", "touchend", and "click". So during a short time period - /// we consume all events and prevent forwarding to the framework. Otherwise, - /// the events will be interpreted twice, once as a request to activate - /// semantics, and a second time by Flutter's gesture recognizers. - Timer _semanticsActivationTimer; - - /// The number of events we processed that could potentially activate - /// semantics. - int _semanticsActivationAttempts = 0; - - /// The maximum [_semanticsActivationAttempts] before we give up waiting for - /// the user to enable semantics. - /// - /// This number is arbitrary and can be adjusted if it doesn't work well. - static const int _kMaxSemanticsActivationAttempts = 20; - - /// Whether we are waiting for the user to enable semantics. - bool get _isWaitingToEnableSemantics => _semanticsPlaceholder != null; - - /// Instructs [_tryEnableSemantics] to remove [_semanticsPlaceholder]. - /// - /// On Chrome the placeholder is removed upon any next event. - /// - /// On Safari the placeholder is removed upon the next "touchend" event. This - /// is to prevent Safari from swallowing the event that happens on an element - /// that's being removed. Chrome doesn't have this issue. - bool _schedulePlaceholderRemoval = false; - - /// Attempts to activate semantics. - /// - /// Returns true if the `event` is not related to semantics activation and - /// should be forwarded to the framework. - bool _tryEnableSemantics(html.Event event) { - if (_schedulePlaceholderRemoval) { - final bool removeNow = - browserEngine != BrowserEngine.webkit || event.type == 'touchend'; - if (removeNow) { - _semanticsPlaceholder.remove(); - _semanticsPlaceholder = null; - _semanticsActivationTimer = null; - } - return true; - } - - if (semanticsEnabled) { - // Semantics already enabled, forward to framework as normal. - return true; - } - - _semanticsActivationAttempts += 1; - if (_semanticsActivationAttempts >= _kMaxSemanticsActivationAttempts) { - // We have received multiple user events, none of which resulted in - // semantics activation. This is a signal that the user is not interested - // in semantics, and so we will stop waiting for it. - _schedulePlaceholderRemoval = true; - return true; - } - - const List kInterestingEventTypes = [ - 'click', - 'touchstart', - 'touchend', - ]; - - if (!kInterestingEventTypes.contains(event.type)) { - // The event is not relevant, forward to framework as normal. - return true; - } - - if (_semanticsActivationTimer != null) { - // We are in a waiting period to activate a timer. While the timer is - // active we should consume events pertaining to semantics activation. - // Otherwise the event will also be interpreted by the framework and - // potentially result in activating a gesture in the app. - return false; - } - - // In Chrome the debouncing works well enough to detect accessibility - // request. - final bool blinkEnableConditionPassed = - browserEngine == BrowserEngine.blink && - _gestureMode == GestureMode.browserGestures; - - // In Safari debouncing doesn't work. Instead we look at where exactly - // (within 1 pixel) the event landed. If it landed exactly in the middle of - // the placeholder we interpret it as a signal to enable accessibility. This - // is because when VoiceOver generates a tap it lands it in the middle of - // the focused element. This method is a bit flawed in that a user's finger - // could theoretically land in the middle of the element too. However, the - // chance of that happening is very small. Even low-end phones typically - // have >2 million pixels (e.g. Moto G4). It is very unlikely that a user - // will land their finger exactly in the middle. In the worst case an - // unlucky user would accidentally enable accessibility and the app will be - // slightly slower than normal, but the app will continue functioning as - // normal. Our semantics tree is designed to not interfere with Flutter's - // gesture detection. - bool safariEnableConditionPassed = false; - if (browserEngine == BrowserEngine.webkit) { - html.Point activationPoint; - - switch (event.type) { - case 'click': - final html.MouseEvent click = event; - activationPoint = click.offset; - break; - case 'touchstart': - case 'touchend': - final html.TouchEvent touch = event; - activationPoint = touch.changedTouches.first.client; - break; - default: - // The event is not relevant, forward to framework as normal. - return true; - } - - assert(activationPoint != null); - - final html.Rectangle activatingElementRect = - domRenderer.glassPaneElement.getBoundingClientRect(); - final double midX = activatingElementRect.left + - (activatingElementRect.right - activatingElementRect.left) / 2; - final double midY = activatingElementRect.top + - (activatingElementRect.bottom - activatingElementRect.top) / 2; - final double deltaX = activationPoint.x - midX; - final double deltaY = activationPoint.y - midY; - final double deltaSquared = deltaX * deltaX + deltaY * deltaY; - if (deltaSquared < 1.0) { - safariEnableConditionPassed = true; - } - } - - if (blinkEnableConditionPassed || safariEnableConditionPassed) { - assert(_semanticsActivationTimer == null); - _semanticsActivationTimer = Timer(const Duration(milliseconds: 300), () { - semanticsEnabled = true; - _schedulePlaceholderRemoval = true; - }); - return false; - } - - // This was not a semantics activating event; forward as normal. - return true; - } - - /// The message in the label for the placeholder element used to enable - /// accessibility. - /// - /// This uses US English as the default message. Set this value prior to - /// calling `runApp` to translate to another language. - static String placeholderMessage = 'Enable accessibility'; - - /// Enables accessibility when the user taps on the glasspane via an - /// accessibility focus. - /// - /// This creates a placeholder inside the glasspane, which, when focused, - /// announces that accessibility can be enabled by tapping. - void autoEnableOnTap(DomRenderer domRenderer) { - _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); - - // Only listen to "click" because other kinds of events are reported via - // PointerBinding. - _semanticsPlaceholder.addEventListener('click', (html.Event event) { - _tryEnableSemantics(event); - }, true); - - _semanticsPlaceholder - ..setAttribute('role', 'button') - ..setAttribute('aria-label', placeholderMessage); - _semanticsPlaceholder.style - ..position = 'absolute' - ..left = '0' - ..top = '0' - ..right = '0' - ..bottom = '0'; - // Insert the semantics placeholder after the scene host. For all widgets - // in the scene, except for platform widgets, the scene host will pass the - // pointer events through to the semantics tree. However, for platform - // views, the pointer events will not pass through, and will be handled - // by the platform view. - domRenderer.glassPaneElement - .insertBefore(_semanticsPlaceholder, domRenderer.sceneHostElement); - } + final SemanticsHelper enableSemantics = SemanticsHelper(); /// Whether the user has requested that [updateSemantics] be called when /// the semantic contents of window changes. @@ -1526,18 +1338,15 @@ class EngineSemanticsOwner { 'mousedown', 'mousemove', 'mouseup', + 'keyup', + 'keydown', ]; if (_pointerEventTypes.contains(event.type)) { _temporarilyDisableBrowserGestureMode(); } - if (!_isWaitingToEnableSemantics) { - // Forward to framework as normal. - return true; - } else { - return _tryEnableSemantics(event); - } + return enableSemantics.shouldEnableSemantics(event); } /// Callbacks called when the [GestureMode] changes. diff --git a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart new file mode 100644 index 0000000000000..d353b21f34d3e --- /dev/null +++ b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart @@ -0,0 +1,385 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// The maximum [semanticsActivationAttempts] before we give up waiting for +/// the user to enable semantics. +/// +/// This number is arbitrary and can be adjusted if it doesn't work well. +const int kMaxSemanticsActivationAttempts = 20; + +/// After an event related to semantics activation has been received, we consume +/// the consecutive events on the engine. Do not send them to the framework. +/// For example when a 'mousedown' targeting a placeholder received following +/// 'mouseup' is also not sent to the framework. +/// Otherwise these events can cause unintended gestures on the framework side. +const Duration _periodToConsumeEvents = const Duration(milliseconds: 300); + +/// The message in the label for the placeholder element used to enable +/// accessibility. +/// +/// This uses US English as the default message. Set this value prior to +/// calling `runApp` to translate to another language. +String placeholderMessage = 'Enable accessibility'; + +/// A helper for [EngineSemanticsOwner]. +/// +/// [SemanticsHelper] prepares and placeholder to enable semantics. +/// +/// It decides if an event is purely semantics enabling related or a regular +/// event which should be forwarded to the framework. +/// +/// It does this by using a [SemanticsEnabler]. The [SemanticsEnabler] +/// implementation is choosen using form factor type. +/// +/// See [DesktopSemanticsEnabler], [MobileSemanticsEnabler]. +class SemanticsHelper { + final _SemanticsEnabler _enableSemantics = + isDesktop ? DesktopSemanticsEnabler() : MobileSemanticsEnabler(); + + bool shouldEnableSemantics(html.Event event) { + return _enableSemantics.tryEnableSemantics(event); + } + + html.Element prepareAccesibilityPlaceholder() { + return _enableSemantics.prepareAccesibilityPlaceholder(); + } +} + +abstract class _SemanticsEnabler { + /// Whether to enable semantics. + /// + /// Semantics should be enabled if the web engine is no longer waiting for + /// extra signals from the user events. See [isWaitingToEnableSemantics]. + /// + /// Or if the received [html.Event] is suitable/enough for enabling the + /// semantics. See [tryEnableSemantics]. + bool shouldEnableSemantics(html.Event event) { + if (!isWaitingToEnableSemantics) { + // Forward to framework as normal. + return true; + } else { + return tryEnableSemantics(event); + } + } + + /// Attempts to activate semantics. + /// + /// Returns true if the `event` is not related to semantics activation and + /// should be forwarded to the framework. + bool tryEnableSemantics(html.Event event); + + /// Creates the placeholder for accesibility. + /// + /// Puts it inside the glasspane. + /// + /// On focus the element announces that accessibility can be enabled by + /// tapping/clicking. (Announcement depends on the assistive technology) + html.Element prepareAccesibilityPlaceholder(); + + /// Whether platform is still consisering enabling semantics. + /// + /// At this stage a relevant set of events are always assessed to see if + /// they activate the semantics. + /// + /// If not they are sent to framework as normal events. + bool get isWaitingToEnableSemantics; +} + +@visibleForTesting +class DesktopSemanticsEnabler extends _SemanticsEnabler { + /// We do not immediately enable semantics when the user requests it, but + /// instead wait for a short period of time before doing it. This is because + /// the request comes as an event targeted on the [_semanticsPlaceholder]. + /// This event, depending on the browser, comes as a burst of events. + /// For example, Safari on MacOS sends "pointerup", "pointerdown". So during a + /// short time period we consume all events and prevent forwarding to the + /// framework. Otherwise, the events will be interpreted twice, once as a + /// request to activate semantics, and a second time by Flutter's gesture + /// recognizers. + @visibleForTesting + Timer semanticsActivationTimer; + + /// A temporary placeholder used to capture a request to activate semantics. + html.Element _semanticsPlaceholder; + + /// The number of events we processed that could potentially activate + /// semantics. + int semanticsActivationAttempts = 0; + + /// Instructs [_tryEnableSemantics] to remove [_semanticsPlaceholder]. + /// + /// The placeholder is removed upon any next event. + bool _schedulePlaceholderRemoval = false; + + /// Whether we are waiting for the user to enable semantics. + @override + bool get isWaitingToEnableSemantics => _semanticsPlaceholder != null; + + @override + bool tryEnableSemantics(html.Event event) { + if (_schedulePlaceholderRemoval) { + _semanticsPlaceholder.remove(); + _semanticsPlaceholder = null; + semanticsActivationTimer = null; + return true; + } + + if (EngineSemanticsOwner.instance.semanticsEnabled) { + // Semantics already enabled, forward to framework as normal. + return true; + } + + // In touch screen laptops, the touch is received as a mouse click + const Set kInterestingEventTypes = { + 'click', + 'keyup', + 'keydown', + 'mouseup', + 'mousedown', + 'pointerdown', + 'pointerup', + }; + + if (!kInterestingEventTypes.contains(event.type)) { + // The event is not relevant, forward to framework as normal. + return true; + } + + semanticsActivationAttempts += 1; + if (semanticsActivationAttempts >= kMaxSemanticsActivationAttempts) { + // We have received multiple user events, none of which resulted in + // semantics activation. This is a signal that the user is not interested + // in semantics, and so we will stop waiting for it. + _schedulePlaceholderRemoval = true; + return true; + } + + if (semanticsActivationTimer != null) { + // We are in a waiting period to activate a timer. While the timer is + // active we should consume events pertaining to semantics activation. + // Otherwise the event will also be interpreted by the framework and + // potentially result in activating a gesture in the app. + return false; + } + + // Check for the event target. + final bool enableConditionPassed = (event.target == _semanticsPlaceholder); + + if (enableConditionPassed) { + assert(semanticsActivationTimer == null); + semanticsActivationTimer = Timer(_periodToConsumeEvents, () { + EngineSemanticsOwner.instance.semanticsEnabled = true; + _schedulePlaceholderRemoval = true; + }); + return false; + } + + // This was not a semantics activating event; forward as normal. + return true; + } + + @override + html.Element prepareAccesibilityPlaceholder() { + _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); + + // Only listen to "click" because other kinds of events are reported via + // PointerBinding. + _semanticsPlaceholder.addEventListener('click', (html.Event event) { + tryEnableSemantics(event); + }, true); + + // Adding roles to semantics placeholder. 'aria-live' will make sure that + // the content is announced to the assistive technology user as soon as the + // page receives focus. 'tab-index' makes sure the button is the first + // target of tab. 'aria-label' is used to define the placeholder message + // to the assistive technology user. + _semanticsPlaceholder + ..setAttribute('role', 'button') + ..setAttribute('aria-live', 'true') + ..setAttribute('tabindex', '0') + ..setAttribute('aria-label', placeholderMessage); + _semanticsPlaceholder.style + ..position = 'absolute' + ..left = '-1px' + ..top = '-1px' + ..width = '1px' + ..height = '1px'; + return _semanticsPlaceholder; + } +} + +@visibleForTesting +class MobileSemanticsEnabler extends _SemanticsEnabler { + /// We do not immediately enable semantics when the user requests it, but + /// instead wait for a short period of time before doing it. This is because + /// the request comes as an event targeted on the [_semanticsPlaceholder]. + /// This event, depending on the browser, comes as a burst of events. + /// For example, Safari on IOS sends "touchstart", "touchend", and "click". + /// So during a short time period we consume all events and prevent forwarding + /// to the framework. Otherwise, the events will be interpreted twice, once as + /// a request to activate semantics, and a second time by Flutter's gesture + /// recognizers. + @visibleForTesting + Timer semanticsActivationTimer; + + /// A temporary placeholder used to capture a request to activate semantics. + html.Element _semanticsPlaceholder; + + /// The number of events we processed that could potentially activate + /// semantics. + int semanticsActivationAttempts = 0; + + /// Instructs [_tryEnableSemantics] to remove [_semanticsPlaceholder]. + /// + /// For Blink browser engine the placeholder is removed upon any next event. + /// + /// For Webkit browser engine the placeholder is removed upon the next + /// "touchend" event. This is to prevent Safari from swallowing the event + /// that happens on an element that's being removed. Blink doesn't have + /// this issue. + bool _schedulePlaceholderRemoval = false; + + /// Whether we are waiting for the user to enable semantics. + @override + bool get isWaitingToEnableSemantics => _semanticsPlaceholder != null; + + @override + bool tryEnableSemantics(html.Event event) { + if (_schedulePlaceholderRemoval) { + final bool removeNow = + (browserEngine != BrowserEngine.webkit || event.type == 'touchend'); + if (removeNow) { + _semanticsPlaceholder.remove(); + _semanticsPlaceholder = null; + semanticsActivationTimer = null; + } + return true; + } + + if (EngineSemanticsOwner.instance.semanticsEnabled) { + // Semantics already enabled, forward to framework as normal. + return true; + } + + semanticsActivationAttempts += 1; + if (semanticsActivationAttempts >= kMaxSemanticsActivationAttempts) { + // We have received multiple user events, none of which resulted in + // semantics activation. This is a signal that the user is not interested + // in semantics, and so we will stop waiting for it. + _schedulePlaceholderRemoval = true; + return true; + } + + const Set kInterestingEventTypes = { + 'click', + 'touchstart', + 'touchend', + }; + + if (!kInterestingEventTypes.contains(event.type)) { + // The event is not relevant, forward to framework as normal. + return true; + } + + if (semanticsActivationTimer != null) { + // We are in a waiting period to activate a timer. While the timer is + // active we should consume events pertaining to semantics activation. + // Otherwise the event will also be interpreted by the framework and + // potentially result in activating a gesture in the app. + return false; + } + + // In Chrome the debouncing works well enough to detect accessibility + // request. + final bool blinkEnableConditionPassed = + browserEngine == BrowserEngine.blink && + EngineSemanticsOwner.instance.gestureMode == + GestureMode.browserGestures; + + // In Safari debouncing doesn't work. Instead we look at where exactly + // (within 1 pixel) the event landed. If it landed exactly in the middle of + // the placeholder we interpret it as a signal to enable accessibility. This + // is because when VoiceOver generates a tap it lands it in the middle of + // the focused element. This method is a bit flawed in that a user's finger + // could theoretically land in the middle of the element too. However, the + // chance of that happening is very small. Even low-end phones typically + // have >2 million pixels (e.g. Moto G4). It is very unlikely that a user + // will land their finger exactly in the middle. In the worst case an + // unlucky user would accidentally enable accessibility and the app will be + // slightly slower than normal, but the app will continue functioning as + // normal. Our semantics tree is designed to not interfere with Flutter's + // gesture detection. + bool safariEnableConditionPassed = false; + if (browserEngine == BrowserEngine.webkit) { + html.Point activationPoint; + + switch (event.type) { + case 'click': + final html.MouseEvent click = event; + activationPoint = click.offset; + break; + case 'touchstart': + case 'touchend': + final html.TouchEvent touch = event; + activationPoint = touch.changedTouches.first.client; + break; + default: + // The event is not relevant, forward to framework as normal. + return true; + } + + assert(activationPoint != null); + + final html.Rectangle activatingElementRect = + domRenderer.glassPaneElement.getBoundingClientRect(); + final double midX = activatingElementRect.left + + (activatingElementRect.right - activatingElementRect.left) / 2; + final double midY = activatingElementRect.top + + (activatingElementRect.bottom - activatingElementRect.top) / 2; + final double deltaX = activationPoint.x - midX; + final double deltaY = activationPoint.y - midY; + final double deltaSquared = deltaX * deltaX + deltaY * deltaY; + if (deltaSquared < 1.0) { + safariEnableConditionPassed = true; + } + } + + if (blinkEnableConditionPassed || safariEnableConditionPassed) { + assert(semanticsActivationTimer == null); + semanticsActivationTimer = Timer(_periodToConsumeEvents, () { + EngineSemanticsOwner.instance.semanticsEnabled = true; + _schedulePlaceholderRemoval = true; + }); + return false; + } + + // This was not a semantics activating event; forward as normal. + return true; + } + + @override + html.Element prepareAccesibilityPlaceholder() { + _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); + + // Only listen to "click" because other kinds of events are reported via + // PointerBinding. + _semanticsPlaceholder.addEventListener('click', (html.Event event) { + tryEnableSemantics(event); + }, true); + + _semanticsPlaceholder + ..setAttribute('role', 'button') + ..setAttribute('aria-label', placeholderMessage); + _semanticsPlaceholder.style + ..position = 'absolute' + ..left = '0' + ..top = '0' + ..right = '0' + ..bottom = '0'; + + return _semanticsPlaceholder; + } +} diff --git a/lib/web_ui/test/dom_renderer_test.dart b/lib/web_ui/test/dom_renderer_test.dart index 724cc318e6f13..7ff6fc078c29a 100644 --- a/lib/web_ui/test/dom_renderer_test.dart +++ b/lib/web_ui/test/dom_renderer_test.dart @@ -116,4 +116,11 @@ void main() { final DomRenderer renderer = DomRenderer(); renderer.reset(); }); + + test('accesibility placeholder is attached after creation', () { + DomRenderer(); + + expect(html.document.getElementsByTagName('flt-semantics-placeholder'), + isNotEmpty); + }); } diff --git a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart new file mode 100644 index 0000000000000..c7fd8bc204176 --- /dev/null +++ b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart @@ -0,0 +1,147 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:ui/src/engine.dart'; + +import 'package:test/test.dart'; + +void main() { + group('$DesktopSemanticsEnabler', () { + DesktopSemanticsEnabler desktopSemanticsEnabler; + html.Element _placeholder; + + setUp(() { + desktopSemanticsEnabler = DesktopSemanticsEnabler(); + }); + + tearDown(() { + if (_placeholder != null) { + _placeholder.remove(); + } + if(desktopSemanticsEnabler?.semanticsActivationTimer != null) { + desktopSemanticsEnabler.semanticsActivationTimer.cancel(); + desktopSemanticsEnabler.semanticsActivationTimer = null; + } + }); + + test('prepare accesibility placeholder', () async { + _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); + + expect(_placeholder.getAttribute('role'), 'button'); + expect(_placeholder.getAttribute('aria-live'), 'true'); + expect(_placeholder.getAttribute('tabindex'), '0'); + + html.document.body.append(_placeholder); + + expect(html.document.getElementsByTagName('flt-semantics-placeholder'), + isNotEmpty); + + expect(_placeholder.getBoundingClientRect().height, 1); + expect(_placeholder.getBoundingClientRect().width, 1); + expect(_placeholder.getBoundingClientRect().top, -1); + expect(_placeholder.getBoundingClientRect().left, -1); + }); + + test('Not relevant events should be forwarded to the framework', () async { + // Prework. Attach the placeholder to dom. + _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); + html.document.body.append(_placeholder); + + html.Event event = html.MouseEvent('mousemove'); + bool shouldForwardToFramework = + desktopSemanticsEnabler.tryEnableSemantics(event); + + expect(shouldForwardToFramework, true); + + event = html.PointerEvent('pointermove'); + shouldForwardToFramework = + desktopSemanticsEnabler.tryEnableSemantics(event); + + expect(shouldForwardToFramework, true); + }); + + test( + 'Relevants events targeting placeholder should not be forwarded to the framework', + () async { + // Prework. Attach the placeholder to dom. + _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); + html.document.body.append(_placeholder); + + html.Event event = html.MouseEvent('mousedown'); + _placeholder.dispatchEvent(event); + + bool shouldForwardToFramework = + desktopSemanticsEnabler.tryEnableSemantics(event); + + expect(shouldForwardToFramework, false); + }); + + test( + 'After max number of relevant events, events should be forwarded to the framework', + () async { + // Prework. Attach the placeholder to dom. + _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); + html.document.body.append(_placeholder); + + html.Event event = html.MouseEvent('mousedown'); + _placeholder.dispatchEvent(event); + + bool shouldForwardToFramework = + desktopSemanticsEnabler.tryEnableSemantics(event); + + expect(shouldForwardToFramework, false); + + // Send max number of events; + for (int i = 1; i <= kMaxSemanticsActivationAttempts; i++) { + event = html.MouseEvent('mousedown'); + _placeholder.dispatchEvent(event); + + shouldForwardToFramework = + desktopSemanticsEnabler.tryEnableSemantics(event); + } + + expect(shouldForwardToFramework, true); + }); + }); + + group('$MobileSemanticsEnabler', () { + MobileSemanticsEnabler mobileSemanticsEnabler; + html.Element _placeholder; + + setUp(() { + mobileSemanticsEnabler = MobileSemanticsEnabler(); + }); + + tearDown(() { + if (_placeholder != null) { + _placeholder.remove(); + } + }); + + test('prepare accesibility placeholder', () async { + _placeholder = mobileSemanticsEnabler.prepareAccesibilityPlaceholder(); + + expect(_placeholder.getAttribute('role'), 'button'); + + html.document.body.append(_placeholder); + + // Placeholder should cover all the screen on a mobile device. + final num bodyHeight = html.window.innerHeight; + final num bodyWidht = html.window.innerWidth; + + expect(_placeholder.getBoundingClientRect().height, bodyHeight); + expect(_placeholder.getBoundingClientRect().width, bodyWidht); + }); + + test('Not relevant events should be forwarded to the framework', () async { + final html.Event event = html.TouchEvent('touchcancel'); + bool shouldForwardToFramework = + mobileSemanticsEnabler.tryEnableSemantics(event); + + expect(shouldForwardToFramework, true); + }); + }); +} From 18d74fe45b9c73863a724d06130b35f1616ee518 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 26 Nov 2019 22:05:43 -0800 Subject: [PATCH 273/591] Show test output when engine unit test fails. (#14026) --- testing/run_tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testing/run_tests.py b/testing/run_tests.py index f32e86398d247..0bb6a88d5b8d5 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -24,7 +24,11 @@ fml_unittests_filter = '--gtest_filter=-*TimeSensitiveTest*:*GpuThreadMerger*' def RunCmd(cmd, **kwargs): - print(subprocess.check_output(cmd, **kwargs)) + try: + print(subprocess.check_output(cmd, **kwargs)) + except subprocess.CalledProcessError as cpe: + print(cpe.output) + raise cpe def IsMac(): return sys.platform == 'darwin' From fad1b23c42b96197a097fe42d37171f32459fecb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 27 Nov 2019 05:47:39 -0500 Subject: [PATCH 274/591] Roll fuchsia/sdk/core/linux-amd64 from xyyOR... to mSEnz... (#14043) Roll fuchsia/sdk/core/linux-amd64 from xyyOR... to mSEnz... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 33e2bb25c441b..fd755ae134ee1 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'xyyORYHKSpVC7v-Z2qPHQvKfpeKCZalbSfkvEsKSMjkC' + 'version': 'mSEnzCU3uCmYqqAOmKbv26c56DPvGn4mXFgY889ClgIC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 6f2710f3f8191..c408c96b0b2ed 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: c495a1a3d63c57a6590969a0687f03e5 +Signature: c1e1c0eb7d9d96272430cfb588eeeff6 UNUSED LICENSES: From f61bec84674e608f631468d97096ac1c957ce351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=9E=E6=A5=BD=E5=9D=82=E8=8A=B1=E7=81=AB?= Date: Wed, 27 Nov 2019 22:46:30 +0800 Subject: [PATCH 275/591] Pass "null" when copy data is null to iOS pasteboard.string (#14046) --- .../darwin/ios/framework/Source/FlutterPlatformPlugin.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index 585d5a3879356..941e3945d015a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -224,7 +224,11 @@ - (NSDictionary*)getClipboardData:(NSString*)format { - (void)setClipboardData:(NSDictionary*)data { UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; - pasteboard.string = data[@"text"]; + if (data[@"text"]) { + pasteboard.string = data[@"text"]; + } else { + pasteboard.string = @"null"; + } } @end From ca9cd2df681bc736086d59aa6fbd23d915135720 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 27 Nov 2019 11:41:57 -0500 Subject: [PATCH 276/591] Roll src/third_party/skia 40a7dfc268b6..861ac61cb1a9 (34 commits) (#14054) https://skia.googlesource.com/skia.git/+log/40a7dfc268b6..861ac61cb1a9 git log 40a7dfc268b6..861ac61cb1a9 --date=short --first-parent --format='%ad %ae %s' 2019-11-27 jvanverth@google.com Fix warning in Fuchsia build 2019-11-27 rosasco@google.com Add deps file for manifest. Use libwebp always. 2019-11-27 bsalomon@google.com Make fragment processor iterators work with range for loops. 2019-11-27 egdaniel@google.com Handle failures in VkCommandBuffer creation. 2019-11-27 robertphillips@google.com Move creation of GrProgramDesc to GrCaps-derived classes 2019-11-27 herb@google.com Revert "Naive CPU large emoji" 2019-11-27 herb@google.com Make SkEnumerate make flatten tuples 2019-11-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 92fe999ae056..c493edcc78ba (509 commits) 2019-11-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 f65b212492ec..c373dfd84194 (8 commits) 2019-11-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader f2637d0dd7eb..663dcefa22ea (3 commits) 2019-11-27 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-27 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-26 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-26 ethannicholas@google.com SkSL now handles sk_Caps in include files 2019-11-26 herb@google.com Naive CPU large emoji 2019-11-26 reed@google.com hide deprecated next() on SkPath::Iter 2019-11-26 egdaniel@google.com Make sure we recycle the secondary command buffers when release primary command buffers. 2019-11-26 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-26 jlavrova@google.com Bug fixes 2019-11-26 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-26 benjaminwagner@google.com [infra] Upgrade NVIDIA driver 2019-11-26 egdaniel@google.com Return a size of 0 for external format GrSurfaces. 2019-11-26 egdaniel@google.com Cleanup GrVkCommandBuffer resource ownership and freeing. 2019-11-26 robertphillips@google.com Move the initial GrProgramDesc keyLength into the header 2019-11-26 benjaminwagner@google.com [infra] Upgrade Intel Skylake driver 2019-11-26 jvanverth@google.com Enable cross-context textures in Metal. 2019-11-26 fmalita@chromium.org [skottie] Handle null mask filter effects gracefully 2019-11-26 borenet@google.com [infra] Make CheckGeneratedFiles use Clang 2019-11-26 robertphillips@google.com Stop using GrPixelConfig in Metal & Dawn's GrProgramDesc creation 2019-11-26 reed@google.com Revert "Revert "switch to new filltype for SkPath"" 2019-11-26 emircan@google.com Revert "Disable VkProtectedContext_DDLMakeRenderTargetTest" 2019-11-26 brianosman@google.com Remove ResourceProvider and ImageAsset sample impls from skottie 2019-11-26 michaelludwig@google.com Allow Tessellator to operate on provided GrQuads 2019-11-26 brianosman@google.com Copy SkottieUtils' classes into skresources Created with: gclient setdep -r src/third_party/skia@861ac61cb1a9 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index fd755ae134ee1..a4cdc4ea5046e 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '40a7dfc268b697eb3db1981ce38c1b6f18507b42', + 'skia_revision': '861ac61cb1a925074192da6efceec7385b1c6c53', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 1d14da7521083..312e3837d5c61 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 396dca43669faa32136d5c09b69aab0d +Signature: 18ab1a9a6b2e4ad6590e56e9b7962e04 UNUSED LICENSES: From 268107d2fcd2b02dd6d11e9fca69dc6ab42018e2 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Wed, 27 Nov 2019 10:39:16 -0800 Subject: [PATCH 277/591] fixing the method call for testing semantics (#14056) --- lib/web_ui/lib/src/engine/semantics/semantics_helper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart index d353b21f34d3e..bdcc87f05ac42 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart @@ -40,7 +40,7 @@ class SemanticsHelper { isDesktop ? DesktopSemanticsEnabler() : MobileSemanticsEnabler(); bool shouldEnableSemantics(html.Event event) { - return _enableSemantics.tryEnableSemantics(event); + return _enableSemantics.shouldEnableSemantics(event); } html.Element prepareAccesibilityPlaceholder() { From 35937eb31240dfbddb741d548635feb01ed66bb1 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 27 Nov 2019 11:18:11 -0800 Subject: [PATCH 278/591] Roll src/third_party/dart 134e0e28cd..96e7a4ff30 (38 commits) (#14058) dart-lang/sdk@96e7a4ff30 [dart:async] Temporarily work around compilation errors dart-lang/sdk@10f158654e Return the URL of recently published docs dart-lang/sdk@61f5e88454 [CFE] [Strong test] Check non-serialized expectations first dart-lang/sdk@3e5ae6adaf Revert "[dartdevc] Break dart:_debugger dependency on dart:html" dart-lang/sdk@f6a76586de [analyzer] send additional data to crash reporting dart-lang/sdk@461b80a0e4 [dartdevc] Fix analysis errors in NNBD dart:core patch file dart-lang/sdk@abeebc04e0 Compute LUB for FunctionType(s) in one pass over parameters. dart-lang/sdk@edf08f63ed [dartfuzz] Temporarily disabling Int32x4 dart-lang/sdk@1a6b76a8a0 Update DeprecatedMemberUseTest from ResolverTestCase to DriverResolutionTest. dart-lang/sdk@dc3af706e9 [nnbd_migration] assume non-nullability in is check dart-lang/sdk@1ecb8c7038 [ dartfuzz ] Added update_spreadsheet.py which automatically updates DartFuzzStats with latest fuzzing run results dart-lang/sdk@c2c716d0c5 [ Observatory ] Fix issue where timeline would hang if CPU profiling was disabled. dart-lang/sdk@740af3c009 [dartdevc] Pass nnbd experiment to analyzer in check_nnbd_sdk.dart dart-lang/sdk@1d914d2e7e Revert "[dartdevc] Migrating dart:_js_helper to NNBD." dart-lang/sdk@674810a38d Generated overriding methods throw an exception rather than returning null dart-lang/sdk@bb76f935c1 [nnbd_migration] Better descriptions for the most common bangs dart-lang/sdk@3524bb1a93 [co19] Roll co19_2 and add co19 (nnbd) to DEPS. dart-lang/sdk@3b99524167 Revert "[observatory] Properly wait for Catapult's iframe to load. Give message while fetching timeline." dart-lang/sdk@a5a9057d73 [cfe] Add non-function type alias usage tests dart-lang/sdk@b563d6cad5 [kernel] Exclude pkg/kernel from front_end spell checking dart-lang/sdk@d5dbf10672 [SDK] Adds non-eager async stack frame collector. dart-lang/sdk@cdb9830a54 [cfe] Shard text_serialization tests dart-lang/sdk@ee8d9d2fd3 Remove unused getters from AnalysisResult. dart-lang/sdk@42ec44a6a4 Reland "Revamp patch_sdk.dart mainly to use libraries.json." dart-lang/sdk@58e9aeafdb Deprecate AnalysisSession.typeProvider and typeSystem. dart-lang/sdk@3663abd8db Update the header in the generated diagnostic docs dart-lang/sdk@3e86e8f8fb [dartdevc] Migrating dart:_js_helper to NNBD. dart-lang/sdk@4987416f69 [nnbd_migration] Only restyle code links (which I also bolded) dart-lang/sdk@273ca5858b Deprecate AnalysisContext.typeProvider/typeSystem dart-lang/sdk@965fb9942f [dartdevc] Restore dart:core imports in dart:_internal patch file dart-lang/sdk@0019bf646b [migration] handle side casts, assume non-null with const guard. dart-lang/sdk@d89252d369 [nnbd_migration] make tooltips stickier dart-lang/sdk@cdab81c21a Remove CompilationUnitElement from ElementWalker.forExecutable() dart-lang/sdk@cc5976f78b Remove TypeProvider parameter from createLoadLibraryFunction() dart-lang/sdk@687ef66bfd Remove InternalAnalysisContext. dart-lang/sdk@53d3b5d780 Generate the auxiliary files in a subdirectory dart-lang/sdk@18b7580944 [analyzer] for 'Failed to handle request' errors, send the exception to crash reporting as well dart-lang/sdk@b19436061d [migration] enable typed data in migration -- fatal exception is gone. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index a4cdc4ea5046e..055f9839aaabe 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '134e0e28cda1f8110a69bad34ae70e8123475a80', + 'dart_revision': '96e7a4ff30c5850762225efca0fa1b48f28c1666', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 823176fa1a60c..8436f4637c792 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 7f0643ee25a0003b77d518e3bfb5e4df +Signature: 544b012fb76cdd196ba7ab5c8d242a54 UNUSED LICENSES: @@ -8185,7 +8185,6 @@ FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/crypto.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/http_date.dart -FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/libraries.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/async_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/core_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/isolate_patch.dart From 29b2e91f11e3e605b8b4f6f3537f35b66507b3a2 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 27 Nov 2019 11:45:03 -0800 Subject: [PATCH 279/591] Include uncommitted files when checking for code format issues. (#14035) --- ci/format.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/format.sh b/ci/format.sh index bff94d77b07dd..63818fc2d1f89 100755 --- a/ci/format.sh +++ b/ci/format.sh @@ -42,7 +42,7 @@ fi; BASE_SHA="$(git fetch $UPSTREAM master > /dev/null 2>&1 && \ (git merge-base --fork-point FETCH_HEAD HEAD || git merge-base FETCH_HEAD HEAD))" -FILES_TO_CHECK="$(git diff $DIFF_OPTS $BASE_SHA..HEAD -- $FILETYPES)" +FILES_TO_CHECK="$(git diff $DIFF_OPTS $BASE_SHA -- $FILETYPES)" FAILED_CHECKS=0 for f in $FILES_TO_CHECK; do From 7a7118d0ad5a896f44fa41fccc526ae52937389c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 27 Nov 2019 15:45:59 -0500 Subject: [PATCH 280/591] Roll src/third_party/skia 861ac61cb1a9..c96f5108df28 (10 commits) (#14059) https://skia.googlesource.com/skia.git/+log/861ac61cb1a9..c96f5108df28 git log 861ac61cb1a9..c96f5108df28 --date=short --first-parent --format='%ad %ae %s' 2019-11-27 bungeman@google.com Reland "Replace CGFontCreate with CTFontManagerCreate." 2019-11-27 brianosman@google.com Add particle entry points to externs.js to fix missing symbols 2019-11-27 bsalomon@google.com Changes to GrCoordTransform and local coords 2019-11-27 bungeman@google.com Revert "Replace CGFontCreate with CTFontManagerCreate." 2019-11-27 bungeman@google.com Replace CGFontCreate with CTFontManagerCreate. 2019-11-27 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-27 herb@google.com Cleanup unit test output 2019-11-27 bsalomon@google.com Fix final-dtor-non-final-class warning on GrDDLContext 2019-11-27 brianosman@google.com Use ResourceProvider in particles 2019-11-27 bungeman@google.com Fix housekeeper bot with some gn format. Created with: gclient setdep -r src/third_party/skia@c96f5108df28 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 055f9839aaabe..ee99f33961297 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '861ac61cb1a925074192da6efceec7385b1c6c53', + 'skia_revision': 'c96f5108df282f763a3d9da27ddd0803bd1660e3', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 312e3837d5c61..cab306daf07db 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 18ab1a9a6b2e4ad6590e56e9b7962e04 +Signature: 8f16c64f927c8f916b3da34e23611df8 UNUSED LICENSES: @@ -1361,6 +1361,7 @@ FILE: ../../../third_party/skia/modules/canvaskit/karma.bench.conf.js FILE: ../../../third_party/skia/modules/canvaskit/karma.conf.js FILE: ../../../third_party/skia/modules/canvaskit/package.json FILE: ../../../third_party/skia/modules/canvaskit/paragraph.js +FILE: ../../../third_party/skia/modules/canvaskit/particles.js FILE: ../../../third_party/skia/modules/canvaskit/perf/animation.bench.js FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/confetti.json FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/drinks.json From bc07e6a239d3a096bbbd5650208224880ad52ecf Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 27 Nov 2019 12:56:44 -0800 Subject: [PATCH 281/591] [web] Initial support for LineMetrics (#13985) --- .../lib/src/engine/text/measurement.dart | 98 ++++-- lib/web_ui/lib/src/engine/text/paragraph.dart | 144 +++++++- lib/web_ui/lib/src/engine/text/ruler.dart | 6 +- lib/web_ui/lib/src/ui/text.dart | 105 ++++-- lib/web_ui/test/paragraph_test.dart | 20 ++ lib/web_ui/test/text/measurement_test.dart | 313 +++++++++++++++--- 6 files changed, 572 insertions(+), 114 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index a6beff85acc32..20f8328e5d126 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -614,7 +614,9 @@ double _measureSubstring( int start, int end, ) { - assert(0 <= start && start <= end && end <= text.length); + assert(0 <= start); + assert(start <= end); + assert(end <= text.length); if (start == end) { return 0; @@ -624,6 +626,7 @@ double _measureSubstring( end == _lastEnd && text == _lastText && _lastStyle == style) { + // TODO(mdebbar): Explore caching all widths in a map, not only the last one. return _lastWidth; } _lastStart = start; @@ -654,7 +657,9 @@ double _roundWidth(double width) { /// The return value is the new end of the substring after excluding the /// trailing characters. int _excludeTrailing(String text, int start, int end, CharPredicate predicate) { - assert(0 <= start && start <= end && end <= text.length); + assert(0 <= start); + assert(start <= end); + assert(end <= text.length); while (start < end && predicate(text.codeUnitAt(end - 1))) { end--; @@ -676,7 +681,7 @@ class LinesCalculator { final double _maxWidth; /// The lines that have been consumed so far. - List lines = []; + List lines = []; int _lineStart = 0; int _chunkStart = 0; @@ -703,13 +708,8 @@ class LinesCalculator { // A single chunk of text could be force-broken into multiple lines if it // doesn't fit in a single line. That's why we need a loop. while (!_reachedMaxLines) { - final double lineWidth = _measureSubstring( - _canvasContext, - _style, - _text, - _lineStart, - chunkEndWithoutSpace, - ); + final double lineWidth = + measureSubstring(_lineStart, chunkEndWithoutSpace); // The current chunk doesn't reach the maximum width, so we stop here and // wait for the next line break. @@ -731,30 +731,37 @@ class LinesCalculator { // When there's an ellipsis, truncate text to leave enough space for // the ellipsis. final double availableWidth = _maxWidth - _ellipsisWidth; - final int breakingPoint = _forceBreak( - availableWidth, - _text, - _lineStart, - chunkEndWithoutSpace, + final int breakingPoint = forceBreakSubstring( + maxWidth: availableWidth, + start: _lineStart, + end: chunkEndWithoutSpace, ); - lines.add(_text.substring(_lineStart, breakingPoint) + _style.ellipsis); + lines.add(EngineLineMetrics.withText( + _text.substring(_lineStart, breakingPoint) + _style.ellipsis, + hardBreak: false, + width: measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth, + lineNumber: lines.length, + )); } else if (isChunkTooLong) { - final int breakingPoint = - _forceBreak(_maxWidth, _text, _lineStart, chunkEndWithoutSpace); + final int breakingPoint = forceBreakSubstring( + maxWidth: _maxWidth, + start: _lineStart, + end: chunkEndWithoutSpace, + ); if (breakingPoint == chunkEndWithoutSpace) { - // We could force-break the chunk any further which means we reached + // We couldn't force-break the chunk any further which means we reached // the last character and there isn't enough space for it to fit in // its own line. Since this is the last character in the chunk, we // don't do anything here and we rely on the next iteration (or the // [isHardBreak] check below) to break the line. break; } - _addLineBreak(lineEnd: breakingPoint); + _addLineBreak(lineEnd: breakingPoint, isHardBreak: false); _chunkStart = breakingPoint; } else { // The control case of current line exceeding [_maxWidth], we break the // line. - _addLineBreak(lineEnd: _chunkStart); + _addLineBreak(lineEnd: _chunkStart, isHardBreak: false); } } @@ -763,39 +770,71 @@ class LinesCalculator { } if (isHardBreak) { - _addLineBreak(lineEnd: chunkEnd); + _addLineBreak(lineEnd: chunkEnd, isHardBreak: true); } _chunkStart = chunkEnd; } - void _addLineBreak({@required int lineEnd}) { - final int indexWithoutNewlines = _excludeTrailing( + void _addLineBreak({ + @required int lineEnd, + @required bool isHardBreak, + }) { + final int endWithoutNewlines = _excludeTrailing( _text, _lineStart, lineEnd, _newlinePredicate, ); - lines.add(_text.substring(_lineStart, indexWithoutNewlines)); + final int endWithoutSpace = _excludeTrailing( + _text, + _lineStart, + endWithoutNewlines, + _whitespacePredicate, + ); + final int lineNumber = lines.length; + final EngineLineMetrics metrics = EngineLineMetrics.withText( + _text.substring(_lineStart, endWithoutNewlines), + hardBreak: isHardBreak, + width: measureSubstring(_lineStart, endWithoutSpace), + lineNumber: lineNumber, + ); + lines.add(metrics); _lineStart = lineEnd; if (lines.length == _style.maxLines) { _reachedMaxLines = true; } } + /// Measures the width of a substring of [_text] starting from the index + /// [start] (inclusive) to [end] (exclusive). + /// + /// This method uses [_text], [_style] and [_canvasContext] to perform the + /// measurement. + double measureSubstring(int start, int end) { + return _measureSubstring(_canvasContext, _style, _text, start, end); + } + /// In a continuous block of text, finds the point where text can be broken to /// fit in the given constraint [maxWidth]. /// /// This always returns at least one character even if there isn't enough /// space for it. - int _forceBreak(double maxWidth, String text, int start, int end) { - assert(0 <= start && start < end && end <= text.length); + int forceBreakSubstring({ + @required double maxWidth, + @required int start, + @required int end, + }) { + assert(0 <= start); + assert(start < end); + assert(end <= _text.length); + // When there's no ellipsis, the breaking point should be at least one + // character away from [start]. int low = hasEllipsis ? start : start + 1; int high = end; do { final int mid = (low + high) ~/ 2; - final double width = - _measureSubstring(_canvasContext, _style, text, start, mid); + final double width = measureSubstring(start, mid); if (width < maxWidth) { low = mid; } else if (width > maxWidth) { @@ -805,7 +844,6 @@ class LinesCalculator { } } while (high - low > 1); - // The breaking point should be at least one character away from [start]. return low; } } diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 1889a5e2416b7..e093088a6961a 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -4,6 +4,102 @@ part of engine; +class EngineLineMetrics implements ui.LineMetrics { + EngineLineMetrics({ + this.hardBreak, + this.ascent, + this.descent, + this.unscaledAscent, + this.height, + this.width, + this.left, + this.baseline, + this.lineNumber, + }) : text = null; + + EngineLineMetrics.withText( + this.text, { + @required this.hardBreak, + this.ascent, + this.descent, + this.unscaledAscent, + this.height, + @required this.width, + this.left, + this.baseline, + @required this.lineNumber, + }) : assert(text != null), + assert(hardBreak != null), + assert(width != null), + assert(lineNumber != null && lineNumber >= 0); + + /// The textual content representing this line. + final String text; + + @override + final bool hardBreak; + + @override + final double ascent; + + @override + final double descent; + + @override + final double unscaledAscent; + + @override + final double height; + + @override + final double width; + + @override + final double left; + + @override + final double baseline; + + @override + final int lineNumber; + + @override + int get hashCode => ui.hashValues( + text, + hardBreak, + ascent, + descent, + unscaledAscent, + height, + width, + left, + baseline, + lineNumber, + ); + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) { + return true; + } + + if (other.runtimeType != runtimeType) { + return false; + } + final EngineLineMetrics typedOther = other; + return text == typedOther.text && + hardBreak == typedOther.hardBreak && + ascent == typedOther.ascent && + descent == typedOther.descent && + unscaledAscent == typedOther.unscaledAscent && + height == typedOther.height && + width == typedOther.width && + left == typedOther.left && + baseline == typedOther.baseline && + lineNumber == typedOther.lineNumber; + } +} + /// The web implementation of [ui.Paragraph]. class EngineParagraph implements ui.Paragraph { /// This class is created by the engine, and should not be instantiated @@ -72,9 +168,28 @@ class EngineParagraph implements ui.Paragraph { /// Valid only after [layout] has been called. double get _lineHeight => _measurementResult?.lineHeight ?? 0; - // TODO(flutter_web): see https://github.com/flutter/flutter/issues/33613. @override - double get longestLine => 0; + double get longestLine { + if (_measurementResult.lines != null) { + double maxWidth = 0.0; + for (ui.LineMetrics metrics in _measurementResult.lines) { + if (maxWidth < metrics.width) { + maxWidth = metrics.width; + } + } + return maxWidth; + } + + // In the single-line case, the longest line is equal to the maximum + // intrinsic width of the paragraph. + if (_measurementResult.isSingleLine) { + return _measurementResult.maxIntrinsicWidth; + } + + // If we don't have any line metrics information, there's no way to know the + // longest line in a multi-line paragraph. + return 0.0; + } @override double get minIntrinsicWidth => _measurementResult?.minIntrinsicWidth ?? 0; @@ -101,7 +216,25 @@ class EngineParagraph implements ui.Paragraph { /// If not null, this list would contain the strings representing each line /// in the paragraph. - List get _lines => _measurementResult?.lines; + /// + /// Avoid repetitively accessing this field as it generates a new list every + /// time. + List get _lines { + if (_plainText == null) { + return null; + } + + final List metricsList = _measurementResult.lines; + if (metricsList == null) { + return null; + } + + final List lines = []; + for (EngineLineMetrics metrics in metricsList) { + lines.add(metrics.text); + } + return lines; + } @override void layout(ui.ParagraphConstraints constraints) { @@ -161,7 +294,7 @@ class EngineParagraph implements ui.Paragraph { bool get _drawOnCanvas { bool canDrawTextOnCanvas; if (TextMeasurementService.enableExperimentalCanvasImplementation) { - canDrawTextOnCanvas = _lines != null; + canDrawTextOnCanvas = _measurementResult.lines != null; } else { canDrawTextOnCanvas = _measurementResult.isSingleLine && _plainText != null && @@ -303,8 +436,7 @@ class EngineParagraph implements ui.Paragraph { @override List computeLineMetrics() { - // TODO(flutter_web): https://github.com/flutter/flutter/issues/39537 - return null; + return _measurementResult.lines; } } diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index d03ab80773075..d33ee463614cd 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -838,9 +838,9 @@ class MeasurementResult { /// {@macro dart.ui.paragraph.ideographicBaseline} final double ideographicBaseline; - /// Substrings that represent how the text should wrap into multiple lines to - /// satisfy [constraintWidth], - final List lines; + /// The full list of [EngineLineMetrics] that describe in detail the various metrics + /// of each laid out line. + final List lines; const MeasurementResult( this.constraintWidth, { diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index b85ef0d14e03b..52f95eda38e35 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1149,48 +1149,101 @@ enum BoxWidthStyle { max, } -class LineMetrics { - LineMetrics({ - this.hardBreak, - this.ascent, - this.descent, - this.unscaledAscent, - this.height, - this.width, - this.left, - this.baseline, - this.lineNumber, - }); - - @pragma('vm:entry-point') - LineMetrics._( - this.hardBreak, - this.ascent, - this.descent, - this.unscaledAscent, - this.height, - this.width, - this.left, - this.baseline, - this.lineNumber, - ); - +abstract class LineMetrics { + factory LineMetrics({ + bool hardBreak, + double ascent, + double descent, + double unscaledAscent, + double height, + double width, + double left, + double baseline, + int lineNumber, + }) = engine.EngineLineMetrics; + + /// {@template dart.ui.LineMetrics.hardBreak} + /// True if this line ends with an explicit line break (e.g. '\n') or is the end + /// of the paragraph. False otherwise. + /// {@endtemplate} final bool hardBreak; + /// {@template dart.ui.LineMetrics.ascent} + /// The rise from the [baseline] as calculated from the font and style for this line. + /// + /// This is the final computed ascent and can be impacted by the strut, height, scaling, + /// as well as outlying runs that are very tall. + /// + /// The [ascent] is provided as a positive value, even though it is typically defined + /// in fonts as negative. This is to ensure the signage of operations with these + /// metrics directly reflects the intended signage of the value. For example, + /// the y coordinate of the top edge of the line is `baseline - ascent`. + /// {@endtemplate} final double ascent; + /// {@template dart.ui.LineMetrics.descent} + /// The drop from the [baseline] as calculated from the font and style for this line. + /// + /// This is the final computed ascent and can be impacted by the strut, height, scaling, + /// as well as outlying runs that are very tall. + /// + /// The y coordinate of the bottom edge of the line is `baseline + descent`. + /// {@endtemplate} final double descent; + /// {@template dart.ui.LineMetrics.unscaledAscent} + /// The rise from the [baseline] as calculated from the font and style for this line + /// ignoring the [TextStyle.height]. + /// + /// The [unscaledAscent] is provided as a positive value, even though it is typically + /// defined in fonts as negative. This is to ensure the signage of operations with + /// these metrics directly reflects the intended signage of the value. + /// {@endtemplate} final double unscaledAscent; + /// {@template dart.ui.LineMetrics.height} + /// Total height of the line from the top edge to the bottom edge. + /// + /// This is equivalent to `round(ascent + descent)`. This value is provided + /// separately due to rounding causing sub-pixel differences from the unrounded + /// values. + /// {@endtemplate} final double height; + /// {@template dart.ui.LineMetrics.width} + /// Width of the line from the left edge of the leftmost glyph to the right + /// edge of the rightmost glyph. + /// + /// This is not the same as the width of the pargraph. + /// + /// See also: + /// + /// * [Paragraph.width], the max width passed in during layout. + /// * [Paragraph.longestLine], the width of the longest line in the paragraph. + /// {@endtemplate} final double width; + /// {@template dart.ui.LineMetrics.left} + /// The x coordinate of left edge of the line. + /// + /// The right edge can be obtained with `left + width`. + /// {@endtemplate} final double left; + /// {@template dart.ui.LineMetrics.baseline} + /// The y coordinate of the baseline for this line from the top of the paragraph. + /// + /// The bottom edge of the paragraph up to and including this line may be obtained + /// through `baseline + descent`. + /// {@endtemplate} final double baseline; + /// {@template dart.ui.LineMetrics.lineNumber} + /// The number of this line in the overall paragraph, with the first line being + /// index zero. + /// + /// For example, the first line is line 0, second line is line 1. + /// {@endtemplate} final int lineNumber; } diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart index b53010600258b..fce82efd3561a 100644 --- a/lib/web_ui/test/paragraph_test.dart +++ b/lib/web_ui/test/paragraph_test.dart @@ -164,4 +164,24 @@ void main() async { paragraph.layout(const ParagraphConstraints(width: 1000)); expect(paragraph.getBoxesForRange(0, 0), isEmpty); }); + + test('longestLine', () { + // [Paragraph.longestLine] is only supported by canvas-based measurement. + TextMeasurementService.enableExperimentalCanvasImplementation = true; + TextMeasurementService.initialize(rulerCacheCapacity: 2); + + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 10, + )); + builder.addText('abcd\nabcde abc'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 80.0)); + expect(paragraph.longestLine, 50.0); + + TextMeasurementService.clearCache(); + TextMeasurementService.enableExperimentalCanvasImplementation = false; + }); } diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index 0b448a14bfe4a..1119ffef2bc84 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -155,7 +155,11 @@ void main() async { expect(result.maxIntrinsicWidth, 60); expect(result.minIntrinsicWidth, 30); expect(result.height, 10); - expectLines(instance, result, [' abc']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line(' abc', hardBreak: true, width: 60.0, lineNumber: 0), + ]); + } // trailing whitespaces text = build(ahemStyle, 'abc '); @@ -163,7 +167,11 @@ void main() async { expect(result.maxIntrinsicWidth, 60); expect(result.minIntrinsicWidth, 30); expect(result.height, 10); - expectLines(instance, result, ['abc ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), + ]); + } // mixed whitespaces text = build(ahemStyle, ' ab c '); @@ -171,7 +179,11 @@ void main() async { expect(result.maxIntrinsicWidth, 100); expect(result.minIntrinsicWidth, 20); expect(result.height, 10); - expectLines(instance, result, [' ab c ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line(' ab c ', hardBreak: true, width: 80.0, lineNumber: 0), + ]); + } // single whitespace text = build(ahemStyle, ' '); @@ -179,7 +191,11 @@ void main() async { expect(result.maxIntrinsicWidth, 10); expect(result.minIntrinsicWidth, 0); expect(result.height, 10); - expectLines(instance, result, [' ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line(' ', hardBreak: true, width: 0.0, lineNumber: 0), + ]); + } // whitespace only text = build(ahemStyle, ' '); @@ -187,7 +203,11 @@ void main() async { expect(result.maxIntrinsicWidth, 50); expect(result.minIntrinsicWidth, 0); expect(result.height, 10); - expectLines(instance, result, [' ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line(' ', hardBreak: true, width: 0.0, lineNumber: 0), + ]); + } }, ); @@ -203,7 +223,11 @@ void main() async { expect(result.minIntrinsicWidth, 50); expect(result.width, 50); expect(result.height, 10); - expectLines(instance, result, ['12345']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('12345', hardBreak: true, width: 50.0, lineNumber: 0), + ]); + } }, ); @@ -221,7 +245,12 @@ void main() async { expect(result.minIntrinsicWidth, 30); expect(result.width, 70); expect(result.height, 20); - expectLines(instance, result, ['foo bar ', 'baz']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('foo bar ', hardBreak: false, width: 70.0, lineNumber: 0), + line('baz', hardBreak: true, width: 30.0, lineNumber: 1), + ]); + } }, ); @@ -237,7 +266,12 @@ void main() async { expect(result.minIntrinsicWidth, 100); expect(result.width, 50); expect(result.height, 20); - expectLines(instance, result, ['12345', '67890']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('12345', hardBreak: false, width: 50.0, lineNumber: 0), + line('67890', hardBreak: true, width: 50.0, lineNumber: 1), + ]); + } // The first word is force-broken twice. result = @@ -247,7 +281,13 @@ void main() async { expect(result.minIntrinsicWidth, 110); expect(result.width, 50); expect(result.height, 30); - expectLines(instance, result, ['abcde', 'fghij', 'k lm']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abcde', hardBreak: false, width: 50.0, lineNumber: 0), + line('fghij', hardBreak: false, width: 50.0, lineNumber: 1), + line('k lm', hardBreak: true, width: 40.0, lineNumber: 2), + ]); + } // Constraints aren't enough even for a single character. In this case, // we show a minimum of one character per line. @@ -259,7 +299,12 @@ void main() async { expect(result.minIntrinsicWidth, 20); expect(result.width, 8); expect(result.height, 20); - expectLines(instance, result, ['A', 'A']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('A', hardBreak: false, width: 10.0, lineNumber: 0), + line('A', hardBreak: true, width: 10.0, lineNumber: 1), + ]); + } // Extremely narrow constraints with new line in the middle. result = instance.measure(build(ahemStyle, 'AA\nA'), narrowConstraints); @@ -267,11 +312,16 @@ void main() async { expect(result.maxIntrinsicWidth, 20); expect(result.minIntrinsicWidth, 20); expect(result.width, 8); - // This can only be done correctly by the canvas-based implementation. if (instance is CanvasTextMeasurementService) { + // This can only be done correctly by the canvas-based implementation. expect(result.height, 30); + + expect(result.lines, [ + line('A', hardBreak: false, width: 10.0, lineNumber: 0), + line('A', hardBreak: true, width: 10.0, lineNumber: 1), + line('A', hardBreak: true, width: 10.0, lineNumber: 2), + ]); } - expectLines(instance, result, ['A', 'A', 'A']); // Extremely narrow constraints with new line in the end. result = instance.measure(build(ahemStyle, 'AAA\n'), narrowConstraints); @@ -280,7 +330,14 @@ void main() async { expect(result.minIntrinsicWidth, 30); expect(result.width, 8); expect(result.height, 40); - expectLines(instance, result, ['A', 'A', 'A', '']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('A', hardBreak: false, width: 10.0, lineNumber: 0), + line('A', hardBreak: false, width: 10.0, lineNumber: 1), + line('A', hardBreak: true, width: 10.0, lineNumber: 2), + line('', hardBreak: true, width: 0.0, lineNumber: 3), + ]); + } }, ); @@ -296,7 +353,12 @@ void main() async { expect(result.minIntrinsicWidth, 20); expect(result.width, 50); expect(result.height, 20); - expectLines(instance, result, ['12', '34']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('12', hardBreak: true, width: 20.0, lineNumber: 0), + line('34', hardBreak: true, width: 20.0, lineNumber: 1), + ]); + } }, ); @@ -308,14 +370,26 @@ void main() async { expect(result.maxIntrinsicWidth, 40); expect(result.minIntrinsicWidth, 40); expect(result.height, 30); - expectLines(instance, result, ['', '', '1234']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('', hardBreak: true, width: 0.0, lineNumber: 0), + line('', hardBreak: true, width: 0.0, lineNumber: 1), + line('1234', hardBreak: true, width: 40.0, lineNumber: 2), + ]); + } // Empty lines in the middle. result = instance.measure(build(ahemStyle, '12\n\n345'), constraints); expect(result.maxIntrinsicWidth, 30); expect(result.minIntrinsicWidth, 30); expect(result.height, 30); - expectLines(instance, result, ['12', '', '345']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('12', hardBreak: true, width: 20.0, lineNumber: 0), + line('', hardBreak: true, width: 0.0, lineNumber: 1), + line('345', hardBreak: true, width: 30.0, lineNumber: 2), + ]); + } // Empty lines in the end. result = instance.measure(build(ahemStyle, '1234\n\n'), constraints); @@ -324,7 +398,11 @@ void main() async { if (instance is CanvasTextMeasurementService) { // This can only be done correctly in the canvas-based implementation. expect(result.height, 30); - expectLines(instance, result, ['1234', '', '']); + expect(result.lines, [ + line('1234', hardBreak: true, width: 40.0, lineNumber: 0), + line('', hardBreak: true, width: 0.0, lineNumber: 1), + line('', hardBreak: true, width: 0.0, lineNumber: 2), + ]); } }); @@ -380,27 +458,55 @@ void main() async { // Simple case. result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); expect(result.minIntrinsicWidth, 40); - expectLines(instance, result, ['abc ', 'de ', 'fghi']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), + line('de ', hardBreak: false, width: 20.0, lineNumber: 1), + line('fghi', hardBreak: true, width: 40.0, lineNumber: 2), + ]); + } // With new lines. result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); expect(result.minIntrinsicWidth, 40); - expectLines(instance, result, ['abcd', 'ef', 'ghi']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abcd', hardBreak: true, width: 40.0, lineNumber: 0), + line('ef', hardBreak: true, width: 20.0, lineNumber: 1), + line('ghi', hardBreak: true, width: 30.0, lineNumber: 2), + ]); + } // With trailing whitespace. result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); expect(result.minIntrinsicWidth, 40); - expectLines(instance, result, ['abcd ', 'efg']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), + line('efg', hardBreak: true, width: 30.0, lineNumber: 1), + ]); + } // With trailing whitespace and new lines. result = instance.measure(build(ahemStyle, 'abc \ndefg'), constraints); expect(result.minIntrinsicWidth, 40); - expectLines(instance, result, ['abc ', 'defg']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), + line('defg', hardBreak: true, width: 40.0, lineNumber: 1), + ]); + } // Very long text. result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); expect(result.minIntrinsicWidth, 120); - expectLines(instance, result, ['AAAAA', 'AAAAA', 'AA']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 0), + line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), + line('AA', hardBreak: true, width: 20.0, lineNumber: 2), + ]); + } }); testMeasurements('maxIntrinsicWidth', (TextMeasurementService instance) { @@ -409,32 +515,65 @@ void main() async { // Simple case. result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); expect(result.maxIntrinsicWidth, 110); - expectLines(instance, result, ['abc ', 'de ', 'fghi']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), + line('de ', hardBreak: false, width: 20.0, lineNumber: 1), + line('fghi', hardBreak: true, width: 40.0, lineNumber: 2), + ]); + } // With new lines. result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); expect(result.maxIntrinsicWidth, 40); - expectLines(instance, result, ['abcd', 'ef', 'ghi']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abcd', hardBreak: true, width: 40.0, lineNumber: 0), + line('ef', hardBreak: true, width: 20.0, lineNumber: 1), + line('ghi', hardBreak: true, width: 30.0, lineNumber: 2), + ]); + } // With long whitespace. result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); expect(result.maxIntrinsicWidth, 100); - expectLines(instance, result, ['abcd ', 'efg']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), + line('efg', hardBreak: true, width: 30.0, lineNumber: 1), + ]); + } // With trailing whitespace. result = instance.measure(build(ahemStyle, 'abc def '), constraints); expect(result.maxIntrinsicWidth, 100); - expectLines(instance, result, ['abc ', 'def ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), + line('def ', hardBreak: true, width: 30.0, lineNumber: 1), + ]); + } // With trailing whitespace and new lines. result = instance.measure(build(ahemStyle, 'abc \ndef '), constraints); expect(result.maxIntrinsicWidth, 60); - expectLines(instance, result, ['abc ', 'def ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), + line('def ', hardBreak: true, width: 30.0, lineNumber: 1), + ]); + } // Very long text. result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); expect(result.maxIntrinsicWidth, 120); - expectLines(instance, result, ['AAAAA', 'AAAAA', 'AA']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 0), + line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), + line('AA', hardBreak: true, width: 20.0, lineNumber: 2), + ]); + } }); testMeasurements( @@ -458,7 +597,11 @@ void main() async { expect(result.minIntrinsicWidth, 480); expect(result.maxIntrinsicWidth, 480); expect(result.height, 10); - expectLines(instance, result, ['AA...']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('AA...', hardBreak: false, width: 50.0, lineNumber: 0), + ]); + } // The short prefix should make the text break into two lines, but the // second line should remain unbroken. @@ -470,7 +613,12 @@ void main() async { expect(result.minIntrinsicWidth, 450); expect(result.maxIntrinsicWidth, 450); expect(result.height, 20); - expectLines(instance, result, ['AAA', 'AA...']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('AAA', hardBreak: true, width: 30.0, lineNumber: 0), + line('AA...', hardBreak: false, width: 50.0, lineNumber: 1), + ]); + } // Tiny constraints. const ui.ParagraphConstraints tinyConstraints = @@ -480,7 +628,11 @@ void main() async { expect(result.minIntrinsicWidth, 40); expect(result.maxIntrinsicWidth, 40); expect(result.height, 10); - expectLines(instance, result, ['...']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('...', hardBreak: false, width: 30.0, lineNumber: 0), + ]); + } // Tinier constraints (not enough for the ellipsis). const ui.ParagraphConstraints tinierConstraints = @@ -490,7 +642,11 @@ void main() async { expect(result.maxIntrinsicWidth, 40); expect(result.height, 10); // TODO(flutter_web): https://github.com/flutter/flutter/issues/34346 - // expectLines(instance, result, ['.']); + // if (instance is CanvasTextMeasurementService) { + // expect(result.lines, [ + // line('.', hardBreak: false, width: 10.0, lineNumber: 0), + // ]); + // } }, ); @@ -507,14 +663,23 @@ void main() async { final ui.Paragraph oneline = build(maxlinesStyle, 'One line'); result = instance.measure(oneline, infiniteConstraints); expect(result.height, 10); - expectLines(instance, result, ['One line']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('One line', hardBreak: true, width: 80.0, lineNumber: 0), + ]); + } // The height should respect max lines and be limited to two lines here. final ui.Paragraph threelines = build(maxlinesStyle, 'First\nSecond\nThird'); result = instance.measure(threelines, infiniteConstraints); expect(result.height, 20); - expectLines(instance, result, ['First', 'Second']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('First', hardBreak: true, width: 50.0, lineNumber: 0), + line('Second', hardBreak: true, width: 60.0, lineNumber: 1), + ]); + } // The height should respect max lines and be limited to two lines here. final ui.Paragraph veryLong = build( @@ -523,7 +688,12 @@ void main() async { ); result = instance.measure(veryLong, constraints); expect(result.height, 20); - expectLines(instance, result, ['Lorem ', 'ipsum ']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('Lorem ', hardBreak: false, width: 50.0, lineNumber: 0), + line('ipsum ', hardBreak: false, width: 50.0, lineNumber: 1), + ]); + } // Case when last line is a long unbreakable word. final ui.Paragraph veryLongLastLine = build( @@ -532,7 +702,12 @@ void main() async { ); result = instance.measure(veryLongLastLine, constraints); expect(result.height, 20); - expectLines(instance, result, ['AAA ', 'AAAAA']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('AAA ', hardBreak: false, width: 30.0, lineNumber: 0), + line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), + ]); + } }); testMeasurements( @@ -560,19 +735,31 @@ void main() async { p = build(onelineStyle, 'abcdef'); result = instance.measure(p, constraints); expect(result.height, 10); - expectLines(instance, result, ['abcdef']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abcdef', hardBreak: true, width: 60.0, lineNumber: 0), + ]); + } // Simple overflow case. p = build(onelineStyle, 'abcd efg'); result = instance.measure(p, constraints); expect(result.height, 10); - expectLines(instance, result, ['abc...']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('abc...', hardBreak: false, width: 60.0, lineNumber: 0), + ]); + } // Another simple overflow case. p = build(onelineStyle, 'a bcde fgh'); result = instance.measure(p, constraints); expect(result.height, 10); - expectLines(instance, result, ['a b...']); + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('a b...', hardBreak: false, width: 60.0, lineNumber: 0), + ]); + } // The ellipsis is supposed to go on the second line, but because the // 2nd line doesn't overflow, no ellipsis is shown. @@ -581,8 +768,12 @@ void main() async { // This can only be done correctly in the canvas-based implementation. if (instance is CanvasTextMeasurementService) { expect(result.height, 20); + + expect(result.lines, [ + line('abcdef ', hardBreak: false, width: 60.0, lineNumber: 0), + line('ghijkl', hardBreak: true, width: 60.0, lineNumber: 1), + ]); } - expectLines(instance, result, ['abcdef ', 'ghijkl']); // But when the 2nd line is long enough, the ellipsis is shown. p = build(multilineStyle, 'abcd efghijkl'); @@ -590,8 +781,12 @@ void main() async { // This can only be done correctly in the canvas-based implementation. if (instance is CanvasTextMeasurementService) { expect(result.height, 20); + + expect(result.lines, [ + line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), + line('efg...', hardBreak: false, width: 60.0, lineNumber: 1), + ]); } - expectLines(instance, result, ['abcd ', 'efg...']); // Even if the second line can be broken, we don't break it, we just // insert the ellipsis. @@ -600,8 +795,12 @@ void main() async { // This can only be done correctly in the canvas-based implementation. if (instance is CanvasTextMeasurementService) { expect(result.height, 20); + + expect(result.lines, [ + line('abcde ', hardBreak: false, width: 50.0, lineNumber: 0), + line('f g...', hardBreak: false, width: 60.0, lineNumber: 1), + ]); } - expectLines(instance, result, ['abcde ', 'f g...']); // First line overflows but second line doesn't. p = build(multilineStyle, 'abcdefg hijk'); @@ -609,8 +808,12 @@ void main() async { // This can only be done correctly in the canvas-based implementation. if (instance is CanvasTextMeasurementService) { expect(result.height, 20); + + expect(result.lines, [ + line('abcdef', hardBreak: false, width: 60.0, lineNumber: 0), + line('g hijk', hardBreak: true, width: 60.0, lineNumber: 1), + ]); } - expectLines(instance, result, ['abcdef', 'g hijk']); // Both first and second lines overflow. p = build(multilineStyle, 'abcdefg hijklmnop'); @@ -618,16 +821,28 @@ void main() async { // This can only be done correctly in the canvas-based implementation. if (instance is CanvasTextMeasurementService) { expect(result.height, 20); + + expect(result.lines, [ + line('abcdef', hardBreak: false, width: 60.0, lineNumber: 0), + line('g h...', hardBreak: false, width: 60.0, lineNumber: 1), + ]); } - expectLines(instance, result, ['abcdef', 'g h...']); }, ); }); } -void expectLines(TextMeasurementService instance, MeasurementResult result, - List lines) { - if (instance is CanvasTextMeasurementService) { - expect(result.lines, lines); - } +/// Shortcut to avoid many line wraps in the tests above. +EngineLineMetrics line( + String text, { + double width, + int lineNumber, + bool hardBreak, +}) { + return EngineLineMetrics.withText( + text, + hardBreak: hardBreak, + width: width, + lineNumber: lineNumber, + ); } From bcb826759d4a6f84596c5d257e0d19272f4f76a5 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Thu, 28 Nov 2019 07:34:40 +0900 Subject: [PATCH 282/591] Revert "Do not default to downstream affinity on iOS insertText (#13852)" (#14053) This reverts commit 0d60e1a324c8f17617855f4cfe7287368e15f3dd. --- .../darwin/ios/framework/Source/FlutterTextInputPlugin.mm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index f22fb51b42dfb..ad39e710eeb50 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -621,9 +621,7 @@ - (BOOL)hasText { } - (void)insertText:(NSString*)text { - // The affinity is unknown here. Set to "" so that Flutter interprets it - // as ambiguous and uses a fallback affinity. - _selectionAffinity = ""; + _selectionAffinity = _kTextAffinityDownstream; [self replaceRange:_selectedTextRange withText:text]; } From 4bdd15cd54384cede14ddfb0a726ce58109505bf Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Wed, 27 Nov 2019 14:53:48 -0800 Subject: [PATCH 283/591] more tests for enabling semantics (#14060) * more tests for enabling semantics * addressing pr comments --- lib/web_ui/lib/src/engine/dom_renderer.dart | 2 +- .../lib/src/engine/semantics/semantics.dart | 4 ++-- .../engine/semantics/semantics_helper.dart | 18 ++++++++++----- lib/web_ui/pubspec.yaml | 1 + .../test/engine/semantics/semantics_test.dart | 23 +++++++++++++++++++ 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 90c10b40d55e0..a7f6ac1d4cc21 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -390,7 +390,7 @@ flt-glass-pane * { _glassPaneElement.append(_sceneHostElement); final html.Element _accesibilityPlaceholder = EngineSemanticsOwner - .instance.enableSemantics + .instance.semanticsHelper .prepareAccesibilityPlaceholder(); // Insert the semantics placeholder after the scene host. For all widgets diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 17f30262939d5..f1f55f0c719e4 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -1198,7 +1198,7 @@ class EngineSemanticsOwner { _now = () => DateTime.now(); } - final SemanticsHelper enableSemantics = SemanticsHelper(); + final SemanticsHelper semanticsHelper = SemanticsHelper(); /// Whether the user has requested that [updateSemantics] be called when /// the semantic contents of window changes. @@ -1346,7 +1346,7 @@ class EngineSemanticsOwner { _temporarilyDisableBrowserGestureMode(); } - return enableSemantics.shouldEnableSemantics(event); + return semanticsHelper.shouldEnableSemantics(event); } /// Callbacks called when the [GestureMode] changes. diff --git a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart index bdcc87f05ac42..d7ce9ae9c65df 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart @@ -36,19 +36,25 @@ String placeholderMessage = 'Enable accessibility'; /// /// See [DesktopSemanticsEnabler], [MobileSemanticsEnabler]. class SemanticsHelper { - final _SemanticsEnabler _enableSemantics = + SemanticsEnabler _semanticsEnabler = isDesktop ? DesktopSemanticsEnabler() : MobileSemanticsEnabler(); + @visibleForTesting + set semanticsEnabler(SemanticsEnabler semanticsEnabler) { + this._semanticsEnabler = semanticsEnabler; + } + bool shouldEnableSemantics(html.Event event) { - return _enableSemantics.shouldEnableSemantics(event); + return _semanticsEnabler.shouldEnableSemantics(event); } html.Element prepareAccesibilityPlaceholder() { - return _enableSemantics.prepareAccesibilityPlaceholder(); + return _semanticsEnabler.prepareAccesibilityPlaceholder(); } } -abstract class _SemanticsEnabler { +@visibleForTesting +abstract class SemanticsEnabler { /// Whether to enable semantics. /// /// Semantics should be enabled if the web engine is no longer waiting for @@ -89,7 +95,7 @@ abstract class _SemanticsEnabler { } @visibleForTesting -class DesktopSemanticsEnabler extends _SemanticsEnabler { +class DesktopSemanticsEnabler extends SemanticsEnabler { /// We do not immediately enable semantics when the user requests it, but /// instead wait for a short period of time before doing it. This is because /// the request comes as an event targeted on the [_semanticsPlaceholder]. @@ -212,7 +218,7 @@ class DesktopSemanticsEnabler extends _SemanticsEnabler { } @visibleForTesting -class MobileSemanticsEnabler extends _SemanticsEnabler { +class MobileSemanticsEnabler extends SemanticsEnabler { /// We do not immediately enable semantics when the user requests it, but /// instead wait for a short period of time before doing it. This is because /// the request comes as an event targeted on the [_semanticsPlaceholder]. diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index 15c6e8790559f..b74bacbf80933 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: dev_dependencies: http: 0.12.0+2 image: 2.1.4 + mockito: 4.1.1 path: 1.6.4 test: 1.6.5 quiver: 2.0.5 diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index c2c2395b7fe05..892e19b247f0b 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:html' as html; import 'dart:typed_data'; +import 'package:mockito/mockito.dart'; import 'package:quiver/testing/async.dart'; import 'package:test/test.dart'; @@ -190,8 +191,30 @@ void _testEngineSemanticsOwner() { semantics().semanticsEnabled = false; }); }); + test('checks shouldEnableSemantics for every global event', () { + final MockSemanticsEnabler mockSemanticsEnabler = MockSemanticsEnabler(); + semantics().semanticsHelper.semanticsEnabler = mockSemanticsEnabler; + final html.Event pointerEvent = html.Event('pointermove'); + + semantics().receiveGlobalEvent(pointerEvent); + + // Verify the interactions. + verify(mockSemanticsEnabler.shouldEnableSemantics(pointerEvent)); + }); + + test('Forward events to framewors if shouldEnableSemantics', () { + final MockSemanticsEnabler mockSemanticsEnabler = MockSemanticsEnabler(); + semantics().semanticsHelper.semanticsEnabler = mockSemanticsEnabler; + final html.Event pointerEvent = html.Event('pointermove'); + when(mockSemanticsEnabler.shouldEnableSemantics(pointerEvent)) + .thenReturn(true); + + expect(semantics().receiveGlobalEvent(pointerEvent), isTrue); + }); } +class MockSemanticsEnabler extends Mock implements SemanticsEnabler {} + void _testHeader() { test('renders heading role for headers', () { semantics() From a86ef946563b020108320bbfb974bf7343284fd3 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 27 Nov 2019 15:01:25 -0800 Subject: [PATCH 284/591] Dynamically determine whether to use offscreen surface based on need (#13976) Only use an offscreen surface on iOS if a layer which reads back from the destination surface, such as BackdropFilter, is present. --- ci/licenses_golden/licenses_flutter | 1 + flow/BUILD.gn | 1 + flow/layers/backdrop_filter_layer.cc | 4 +- flow/layers/clip_path_layer.cc | 1 + flow/layers/clip_rect_layer.cc | 1 + flow/layers/clip_rrect_layer.cc | 1 + flow/layers/color_filter_layer.cc | 4 +- flow/layers/container_layer.cc | 37 +++- flow/layers/container_layer.h | 23 ++- flow/layers/layer.cc | 27 ++- flow/layers/layer.h | 33 +++ flow/layers/layer_tree.h | 4 + flow/layers/layer_unittests.cc | 188 ++++++++++++++++++ flow/layers/opacity_layer.cc | 8 +- flow/layers/physical_shape_layer.cc | 1 + flow/layers/shader_mask_layer.cc | 4 +- shell/common/rasterizer.cc | 3 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 22 +- shell/gpu/gpu_surface_gl.h | 9 +- shell/gpu/gpu_surface_gl_delegate.cc | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 6 +- shell/gpu/gpu_surface_metal.h | 3 +- shell/gpu/gpu_surface_metal.mm | 3 +- shell/gpu/gpu_surface_software.cc | 3 +- shell/gpu/gpu_surface_software.h | 4 +- shell/gpu/gpu_surface_vulkan.cc | 3 +- shell/gpu/gpu_surface_vulkan.h | 4 +- .../framework/Source/FlutterPlatformViews.mm | 2 +- shell/platform/darwin/ios/ios_surface_gl.h | 2 +- shell/platform/darwin/ios/ios_surface_gl.mm | 7 +- shell/platform/fuchsia/flutter/surface.cc | 3 +- shell/platform/fuchsia/flutter/surface.h | 3 +- 33 files changed, 386 insertions(+), 36 deletions(-) create mode 100644 flow/layers/layer_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c296ebe416fbd..1e81f43602348 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -46,6 +46,7 @@ FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h FILE: ../../../flutter/flow/layers/layer_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h +FILE: ../../../flutter/flow/layers/layer_unittests.cc FILE: ../../../flutter/flow/layers/opacity_layer.cc FILE: ../../../flutter/flow/layers/opacity_layer.h FILE: ../../../flutter/flow/layers/performance_overlay_layer.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index c2f0c98415a3e..c99f2177e772f 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -109,6 +109,7 @@ executable("flow_unittests") { "flow_run_all_unittests.cc", "flow_test_utils.cc", "flow_test_utils.h", + "layers/layer_unittests.cc", "layers/performance_overlay_layer_unittests.cc", "layers/physical_shape_layer_unittests.cc", "matrix_decomposition_unittests.cc", diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index 573db97f191a2..cf6f5cf522b96 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -7,7 +7,9 @@ namespace flutter { BackdropFilterLayer::BackdropFilterLayer(sk_sp filter) - : filter_(std::move(filter)) {} + : filter_(std::move(filter)) { + set_layer_reads_surface(filter_.get() != nullptr); +} BackdropFilterLayer::~BackdropFilterLayer() = default; diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index d08c19b34eeb9..0c63b2331d9f5 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -15,6 +15,7 @@ namespace flutter { ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) : clip_path_(clip_path), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); + set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } ClipPathLayer::~ClipPathLayer() = default; diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index de7590624e408..5ee6046b19a73 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -9,6 +9,7 @@ namespace flutter { ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) : clip_rect_(clip_rect), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); + set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } ClipRectLayer::~ClipRectLayer() = default; diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 9899a1658732d..037d91ddcb5ff 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -9,6 +9,7 @@ namespace flutter { ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) : clip_rrect_(clip_rrect), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); + set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } ClipRRectLayer::~ClipRRectLayer() = default; diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index f838b0612b2e5..6f355f4419ab4 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -7,7 +7,9 @@ namespace flutter { ColorFilterLayer::ColorFilterLayer(sk_sp filter) - : filter_(std::move(filter)) {} + : filter_(std::move(filter)) { + set_renders_to_save_layer(true); +} ColorFilterLayer::~ColorFilterLayer() = default; diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index d5c6a2a03a34a..1fa7056c5454f 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -6,13 +6,46 @@ namespace flutter { -ContainerLayer::ContainerLayer() {} +ContainerLayer::ContainerLayer() + : child_needs_screen_readback_(false), renders_to_save_layer_(false) {} ContainerLayer::~ContainerLayer() = default; void ContainerLayer::Add(std::shared_ptr layer) { - layer->set_parent(this); + Layer* the_layer = layer.get(); + the_layer->set_parent(this); layers_.push_back(std::move(layer)); + if (the_layer->tree_reads_surface()) { + NotifyChildReadback(the_layer); + } +} + +void ContainerLayer::ClearChildren() { + layers_.clear(); + if (child_needs_screen_readback_) { + child_needs_screen_readback_ = false; + UpdateTreeReadsSurface(); + } +} + +void ContainerLayer::set_renders_to_save_layer(bool value) { + if (renders_to_save_layer_ != value) { + renders_to_save_layer_ = value; + UpdateTreeReadsSurface(); + } +} + +void ContainerLayer::NotifyChildReadback(const Layer* layer) { + if (child_needs_screen_readback_ || !layer->tree_reads_surface()) { + return; + } + child_needs_screen_readback_ = true; + UpdateTreeReadsSurface(); +} + +bool ContainerLayer::ComputeTreeReadsSurface() const { + return layer_reads_surface() || + (!renders_to_save_layer_ && child_needs_screen_readback_); } void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index ef1c03328d1df..1e37f0164cf40 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -25,22 +25,43 @@ class ContainerLayer : public Layer { const std::vector>& layers() const { return layers_; } + // Called when the layer, which must be a child of this container, + // changes its tree_reads_surface() result from false to true. + void NotifyChildReadback(const Layer* layer); + protected: void PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds); void PaintChildren(PaintContext& context) const; + virtual bool ComputeTreeReadsSurface() const override; + #if defined(OS_FUCHSIA) void UpdateSceneChildren(SceneUpdateContext& context); #endif // defined(OS_FUCHSIA) + // Specify whether or not the container has its children render + // to a SaveLayer which will prevent many rendering anomalies + // from propagating to the parent - such as if the children + // read back from the surface on which they render, or if the + // children perform non-associative rendering. Those children + // will now be performing those operations on the SaveLayer + // rather than the layer that this container renders onto. + void set_renders_to_save_layer(bool value); + // For OpacityLayer to restructure to have a single child. - void ClearChildren() { layers_.clear(); } + void ClearChildren(); private: std::vector> layers_; + // child_needs_screen_readback_ is maintained even if the + // renders_to_save_layer_ property is set in case both + // parameters are dynamically and independently determined. + bool child_needs_screen_readback_; + bool renders_to_save_layer_; + FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index b729f582a0a9a..052b70fac9827 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/flow/layers/layer.h" +#include "flutter/flow/layers/container_layer.h" #include "flutter/flow/paint_utils.h" #include "third_party/skia/include/core/SkColorFilter.h" @@ -13,7 +14,9 @@ Layer::Layer() : parent_(nullptr), needs_system_composite_(false), paint_bounds_(SkRect::MakeEmpty()), - unique_id_(NextUniqueID()) {} + unique_id_(NextUniqueID()), + tree_reads_surface_(false), + layer_reads_surface_(false) {} Layer::~Layer() = default; @@ -26,6 +29,28 @@ uint64_t Layer::NextUniqueID() { return id; } +void Layer::set_layer_reads_surface(bool value) { + if (layer_reads_surface_ != value) { + layer_reads_surface_ = value; + UpdateTreeReadsSurface(); + } +} + +bool Layer::ComputeTreeReadsSurface() const { + return layer_reads_surface_; +} + +void Layer::UpdateTreeReadsSurface() { + bool new_tree_reads_surface = ComputeTreeReadsSurface(); + + if (tree_reads_surface_ != new_tree_reads_surface) { + tree_reads_surface_ = new_tree_reads_surface; + if (parent_ != nullptr) { + parent_->NotifyChildReadback(this); + } + } +} + void Layer::Preroll(PrerollContext* context, const SkMatrix& matrix) {} #if defined(OS_FUCHSIA) diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 66944376e8ce8..ef114b4c381c3 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -145,14 +145,47 @@ class Layer { bool needs_painting() const { return !paint_bounds_.isEmpty(); } + // True iff the layer, or some descendant of the layer, performs an + // operation which depends on (i.e. must read back from) the surface + // on which it is rendered in any way beyond the functionality of + // BlendMode. This value has no setter as it is computed from other + // flags and properties on the layer. + // For an example see |BackdropFilterLayer|. + // + // See |UpdateTreeReadsSurface| + // See |set_layer_reads_surface| + // See |ContainerLayer::set_renders_to_save_layer| + // See |ContainerLayer::NotifyChildReadback| + bool tree_reads_surface() const { return tree_reads_surface_; } + uint64_t unique_id() const { return unique_id_; } + protected: + // Compute a new value for tree_reads_surface_ from all of the various + // properties of this layer. + // Used by |UpdateTreeReadsSurface| + virtual bool ComputeTreeReadsSurface() const; + + // Update the tree_reads_surface_ value and propagate changes to + // ancestors if needed. + // Uses |ComputeTreeReadsSurface| + void UpdateTreeReadsSurface(); + + // True iff the layer itself (not a child or other descendant) performs + // an operation which reads from the surface on which it is rendered. + bool layer_reads_surface() const { return layer_reads_surface_; } + + void set_layer_reads_surface(bool value); + private: ContainerLayer* parent_; bool needs_system_composite_; SkRect paint_bounds_; uint64_t unique_id_; + bool tree_reads_surface_; + bool layer_reads_surface_; + static uint64_t NextUniqueID(); FML_DISALLOW_COPY_AND_ASSIGN(Layer); diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 124b8a85dea45..99908740eccc3 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -43,6 +43,10 @@ class LayerTree { root_layer_ = std::move(root_layer); } + bool root_needs_screen_readback() const { + return root_layer_ && root_layer_->tree_reads_surface(); + } + const SkISize& frame_size() const { return frame_size_; } void set_frame_size(const SkISize& frame_size) { frame_size_ = frame_size; } diff --git a/flow/layers/layer_unittests.cc b/flow/layers/layer_unittests.cc new file mode 100644 index 0000000000000..360dba5acd5dc --- /dev/null +++ b/flow/layers/layer_unittests.cc @@ -0,0 +1,188 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/backdrop_filter_layer.h" +#include "flutter/flow/layers/clip_path_layer.h" +#include "flutter/flow/layers/clip_rect_layer.h" +#include "flutter/flow/layers/clip_rrect_layer.h" +#include "flutter/flow/layers/color_filter_layer.h" +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/opacity_layer.h" +#include "flutter/flow/layers/physical_shape_layer.h" +#include "flutter/flow/layers/shader_mask_layer.h" + +#include "third_party/skia/include/effects/SkBlurImageFilter.h" + +#include "gtest/gtest.h" + +namespace flutter { + +class ReadbackLayer : public ContainerLayer { + public: + ReadbackLayer(const bool reads, const bool saves) { + set_layer_reads_surface(reads); + set_renders_to_save_layer(saves); + } + ~ReadbackLayer() override = default; + + static std::shared_ptr Make(const bool reads, + const bool saves) { + return std::make_shared(reads, saves); + } + + void set_read(const bool reads) { set_layer_reads_surface(reads); } + + void Paint(PaintContext& context) const override {} +}; + +void TestLayerFlag(bool reads, bool saves) { + EXPECT_EQ(ReadbackLayer(reads, saves).tree_reads_surface(), reads); +} + +void TestChildFlag(bool child_reads, bool uses_save_layer, bool ret) { + ReadbackLayer parent = ReadbackLayer(false, uses_save_layer); + parent.Add(ReadbackLayer::Make(child_reads, false)); + EXPECT_EQ(parent.tree_reads_surface(), ret); +} + +TEST(Layer, ReadbackFalse) { + TestLayerFlag(false, false); + TestLayerFlag(false, true); +} + +TEST(Layer, ReadbackTrue) { + TestLayerFlag(true, false); + TestLayerFlag(true, true); +} + +TEST(Layer, NoReadbackNoSaveLayer) { + TestChildFlag(false, false, false); +} + +TEST(Layer, NoReadbackButSaveLayer) { + TestChildFlag(false, true, false); +} + +TEST(Layer, ReadbackNoSaveLayer) { + TestChildFlag(true, false, true); +} + +TEST(Layer, ReadbackButSaveLayer) { + TestChildFlag(true, true, false); +} + +TEST(Layer, AddedChildReadback) { + ReadbackLayer parent = ReadbackLayer(false, false); + EXPECT_FALSE(parent.tree_reads_surface()); + parent.Add(ReadbackLayer::Make(false, false)); + EXPECT_FALSE(parent.tree_reads_surface()); + parent.Add(ReadbackLayer::Make(true, false)); + EXPECT_TRUE(parent.tree_reads_surface()); +} + +TEST(Layer, ChildChangesToReadback) { + ReadbackLayer parent = ReadbackLayer(false, false); + EXPECT_FALSE(parent.tree_reads_surface()); + parent.Add(ReadbackLayer::Make(false, false)); + EXPECT_FALSE(parent.tree_reads_surface()); + std::shared_ptr child = ReadbackLayer::Make(false, false); + parent.Add(child); + EXPECT_FALSE(parent.tree_reads_surface()); + child->set_read(true); + EXPECT_TRUE(parent.tree_reads_surface()); +} + +TEST(Layer, BackdropFilterLayer) { + sk_sp filter = SkBlurImageFilter::Make( + 5.0f, 5.0f, nullptr, nullptr, SkBlurImageFilter::kClamp_TileMode); + EXPECT_TRUE(BackdropFilterLayer(filter).tree_reads_surface()); + filter.reset(); + EXPECT_FALSE(BackdropFilterLayer(filter).tree_reads_surface()); +} + +void TestClipRect(Clip clip_behavior, bool ret) { + ClipRectLayer layer = ClipRectLayer(SkRect::MakeWH(5, 5), clip_behavior); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_EQ(layer.tree_reads_surface(), ret); +} + +TEST(Layer, ClipRectSaveLayer) { + // TestClipRect(Clip::none, true); // ClipRectLayer asserts !Clip::none + TestClipRect(Clip::hardEdge, true); + TestClipRect(Clip::antiAlias, true); + TestClipRect(Clip::antiAliasWithSaveLayer, false); +} + +void TestClipRRect(Clip clip_behavior, bool ret) { + SkRRect r_rect = SkRRect::MakeRect(SkRect::MakeWH(5, 5)); + ClipRRectLayer layer = ClipRRectLayer(r_rect, clip_behavior); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_EQ(layer.tree_reads_surface(), ret); +} + +TEST(Layer, ClipRRectSaveLayer) { + // TestClipRRect(Clip::none, true); // ClipRRectLayer asserts !Clip::none + TestClipRRect(Clip::hardEdge, true); + TestClipRRect(Clip::antiAlias, true); + TestClipRRect(Clip::antiAliasWithSaveLayer, false); +} + +void TestClipPath(Clip clip_behavior, bool ret) { + SkPath path = SkPath(); + path.moveTo(0, 0); + path.lineTo(5, 0); + path.lineTo(0, 5); + path.close(); + ClipPathLayer layer = ClipPathLayer(path, clip_behavior); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_EQ(layer.tree_reads_surface(), ret); +} + +TEST(Layer, ClipPathSaveLayer) { + // TestClipPath(Clip::none, true); // ClipRRectLayer asserts !Clip::none + TestClipPath(Clip::hardEdge, true); + TestClipPath(Clip::antiAlias, true); + TestClipPath(Clip::antiAliasWithSaveLayer, false); +} + +TEST(Layer, ColorFilterSaveLayer) { + sk_sp filter = SkColorFilters::LinearToSRGBGamma(); + ColorFilterLayer layer = ColorFilterLayer(filter); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_FALSE(layer.tree_reads_surface()); +} + +TEST(Layer, OpacitySaveLayer) { + OpacityLayer layer = OpacityLayer(10, SkPoint::Make(0, 0)); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_FALSE(layer.tree_reads_surface()); +} + +void TestPhysicalShapeLayer(Clip clip_behavior, bool ret) { + SkPath path = SkPath(); + path.moveTo(0, 0); + path.lineTo(5, 0); + path.lineTo(0, 5); + path.close(); + PhysicalShapeLayer layer = PhysicalShapeLayer( + SK_ColorRED, SK_ColorBLUE, 1.0f, 100.0f, 10.0f, path, clip_behavior); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_EQ(layer.tree_reads_surface(), ret); +} + +TEST(Layer, PhysicalShapeSaveLayer) { + TestPhysicalShapeLayer(Clip::none, true); + TestPhysicalShapeLayer(Clip::hardEdge, true); + TestPhysicalShapeLayer(Clip::antiAlias, true); + TestPhysicalShapeLayer(Clip::antiAliasWithSaveLayer, false); +} + +TEST(Layer, ShaderMaskSaveLayer) { + ShaderMaskLayer layer = ShaderMaskLayer( + SkShaders::Empty(), SkRect::MakeWH(5, 5), SkBlendMode::kSrcOver); + layer.Add(ReadbackLayer::Make(true, false)); + EXPECT_FALSE(layer.tree_reads_surface()); +} + +} // namespace flutter diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 6257700ffbddf..54f8e19d76eea 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -9,7 +9,13 @@ namespace flutter { OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset) - : alpha_(alpha), offset_(offset) {} + : alpha_(alpha), offset_(offset) { + // This type of layer either renders via a SaveLayer or it renders + // from a raster cache image. In either case, it does not pass + // any rendering from a child through to the surface so it is + // effectively "renders to save layer" in all cases. + set_renders_to_save_layer(true); +} OpacityLayer::~OpacityLayer() = default; diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 0a607a88c23b0..428e9fb8e4b2b 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -46,6 +46,7 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, // an SkPath. frameRRect_ = SkRRect::MakeRect(path.getBounds()); } + set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } PhysicalShapeLayer::~PhysicalShapeLayer() = default; diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index 36e7b7332aeae..064e0e1b2ad8b 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -9,7 +9,9 @@ namespace flutter { ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode) - : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {} + : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) { + set_renders_to_save_layer(true); +} ShaderMaskLayer::~ShaderMaskLayer() = default; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 11fdd3f6ee6ad..fa25b0d7933cf 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -297,7 +297,8 @@ RasterStatus Rasterizer::DoDraw( RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { FML_DCHECK(surface_); - auto frame = surface_->AcquireFrame(layer_tree.frame_size()); + auto frame = surface_->AcquireFrame(layer_tree.frame_size(), + layer_tree.root_needs_screen_readback()); if (frame == nullptr) { return RasterStatus::kFailed; diff --git a/shell/common/surface.h b/shell/common/surface.h index 14f898fad3fe1..661f21f04cb05 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -50,7 +50,9 @@ class Surface { virtual bool IsValid() = 0; - virtual std::unique_ptr AcquireFrame(const SkISize& size) = 0; + virtual std::unique_ptr AcquireFrame( + const SkISize& size, + const bool needs_readback) = 0; virtual SkMatrix GetRootTransformation() const = 0; diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5f30a48375d33..ac81687f84037 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -193,12 +193,17 @@ static sk_sp CreateOffscreenSurface(GrContext* context, &surface_props); } -bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { +bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size, + const bool needs_readback) { + bool needs_offscreen = delegate_->UseOffscreenSurface(needs_readback); if (onscreen_surface_ != nullptr && size == SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height())) { // Surface size appears unchanged. So bail. - return true; + bool has_offscreen = offscreen_surface_ != nullptr; + if (needs_offscreen == has_offscreen) { + return true; + } } // We need to do some updates. @@ -228,7 +233,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { return false; } - if (delegate_->UseOffscreenSurface()) { + if (needs_offscreen) { offscreen_surface = CreateOffscreenSurface(context_.get(), size); if (offscreen_surface == nullptr) { FML_LOG(ERROR) << "Could not create offscreen surface."; @@ -248,7 +253,9 @@ SkMatrix GPUSurfaceGL::GetRootTransformation() const { } // |Surface| -std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { +std::unique_ptr GPUSurfaceGL::AcquireFrame( + const SkISize& size, + const bool needs_readback) { if (delegate_ == nullptr) { return nullptr; } @@ -271,7 +278,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { const auto root_surface_transformation = GetRootTransformation(); sk_sp surface = - AcquireRenderSurface(size, root_surface_transformation); + AcquireRenderSurface(size, root_surface_transformation, needs_readback); if (surface == nullptr) { return nullptr; @@ -335,14 +342,15 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { sk_sp GPUSurfaceGL::AcquireRenderSurface( const SkISize& untransformed_size, - const SkMatrix& root_surface_transformation) { + const SkMatrix& root_surface_transformation, + const bool needs_readback) { const auto transformed_rect = root_surface_transformation.mapRect( SkRect::MakeWH(untransformed_size.width(), untransformed_size.height())); const auto transformed_size = SkISize::Make(transformed_rect.width(), transformed_rect.height()); - if (!CreateOrUpdateSurfaces(transformed_size)) { + if (!CreateOrUpdateSurfaces(transformed_size, needs_readback)) { return nullptr; } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 97325569bfd16..b7a26e1a057b7 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -32,7 +32,9 @@ class GPUSurfaceGL : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame( + const SkISize& size, + const bool needs_readback) override; // |Surface| SkMatrix GetRootTransformation() const override; @@ -60,11 +62,12 @@ class GPUSurfaceGL : public Surface { bool valid_ = false; fml::WeakPtrFactory weak_factory_; - bool CreateOrUpdateSurfaces(const SkISize& size); + bool CreateOrUpdateSurfaces(const SkISize& size, const bool needs_readback); sk_sp AcquireRenderSurface( const SkISize& untransformed_size, - const SkMatrix& root_surface_transformation); + const SkMatrix& root_surface_transformation, + const bool needs_readback); bool PresentSurface(SkCanvas* canvas); diff --git a/shell/gpu/gpu_surface_gl_delegate.cc b/shell/gpu/gpu_surface_gl_delegate.cc index 1ef969fe8acc5..89c660688b3cc 100644 --- a/shell/gpu/gpu_surface_gl_delegate.cc +++ b/shell/gpu/gpu_surface_gl_delegate.cc @@ -12,7 +12,8 @@ bool GPUSurfaceGLDelegate::GLContextFBOResetAfterPresent() const { return false; } -bool GPUSurfaceGLDelegate::UseOffscreenSurface() const { +bool GPUSurfaceGLDelegate::UseOffscreenSurface( + const bool needs_readback) const { return false; } diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dfe0ce7f468db..7a222c3567909 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -36,8 +36,10 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // subsequent frames. virtual bool GLContextFBOResetAfterPresent() const; - // Create an offscreen surface to render into before onscreen composition. - virtual bool UseOffscreenSurface() const; + // Create an offscreen surface to render into before onscreen composition + // based on whether or not the frame will perform any operations that will + // require readback from the rendering target. + virtual bool UseOffscreenSurface(const bool needs_readback) const; // A transformation applied to the onscreen surface before the canvas is // flushed. diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index fc6b0964766ce..30f8fe2fa35e1 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -37,7 +37,8 @@ class GPUSurfaceMetal : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size, + const bool needs_readback) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 81abf740d48f6..bc71337dcb94f 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -83,7 +83,8 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& size) { +std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& size, + const bool needs_readback) { if (!IsValid()) { FML_LOG(ERROR) << "Metal surface was invalid."; return nullptr; diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index 346857ac47e6a..7bec87eed1948 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -24,7 +24,8 @@ bool GPUSurfaceSoftware::IsValid() { // |Surface| std::unique_ptr GPUSurfaceSoftware::AcquireFrame( - const SkISize& logical_size) { + const SkISize& logical_size, + const bool needs_readback) { // TODO(38466): Refactor GPU surface APIs take into account the fact that an // external view embedder may want to render to the root surface. if (!render_to_surface_) { diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index af4d1cb3c107f..66b8b90500463 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -23,7 +23,9 @@ class GPUSurfaceSoftware : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame( + const SkISize& size, + const bool needs_readback) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index fbc9696b15d52..7a494849bdfcf 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -22,7 +22,8 @@ bool GPUSurfaceVulkan::IsValid() { // |Surface| std::unique_ptr GPUSurfaceVulkan::AcquireFrame( - const SkISize& size) { + const SkISize& size, + const bool needs_readback) { auto surface = window_.AcquireSurface(); if (surface == nullptr) { diff --git a/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index 7c410dd526cf7..58c805936017e 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -26,7 +26,9 @@ class GPUSurfaceVulkan : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame( + const SkISize& size, + const bool needs_readback) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33ca14d9fabea..4837ef22f4a42 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -369,7 +369,7 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { EnsureOverlayInitialized(view_id, gl_context, gr_context); - auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); + auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_, true); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); canvas->flush(); diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index c1019bb442bb0..73148f34ce9ed 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -48,7 +48,7 @@ class IOSSurfaceGL final : public IOSSurface, intptr_t GLContextFBO() const override; - bool UseOffscreenSurface() const override; + bool UseOffscreenSurface(const bool needs_readback) const override; // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 48e70e00a4e7a..1130375758831 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -49,11 +49,12 @@ return IsValid() ? render_target_->framebuffer() : GL_NONE; } -bool IOSSurfaceGL::UseOffscreenSurface() const { +bool IOSSurfaceGL::UseOffscreenSurface(const bool needs_readback) const { // The onscreen surface wraps a GL renderbuffer, which is extremely slow to read. // Certain filter effects require making a copy of the current destination, so we - // always render to an offscreen surface, which will be much quicker to read/copy. - return true; + // render to an offscreen surface, which will be much quicker to read/copy, if + // they are present. + return needs_readback; } bool IOSSurfaceGL::GLContextMakeCurrent() { diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index 29fbbc7294cc1..1fda6a50f54d5 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -24,7 +24,8 @@ bool Surface::IsValid() { // |flutter::Surface| std::unique_ptr Surface::AcquireFrame( - const SkISize& size) { + const SkISize& size, + const bool needs_readback) { return std::make_unique( nullptr, [](const flutter::SurfaceFrame& surface_frame, SkCanvas* canvas) { return true; }); diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index 66220f079b3e5..87d6d09320c08 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -29,7 +29,8 @@ class Surface final : public flutter::Surface { // |flutter::Surface| std::unique_ptr AcquireFrame( - const SkISize& size) override; + const SkISize& size, + const bool needs_readback) override; // |flutter::Surface| GrContext* GetContext() override; From 0fc7867ae5e5201e9616a9feb455856580aca027 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 27 Nov 2019 15:46:04 -0800 Subject: [PATCH 285/591] Roll src/third_party/dart 96e7a4ff30..73fdf19b56 (3 commits) (#14063) dart-lang/sdk@73fdf19b56 [dart2js] Add variance table to RTI and updated subtyping wrt variance. dart-lang/sdk@58dde43b5e [analysis_server] don't create futures for stalled plugins. dart-lang/sdk@5aa86eb123 [vm/concurrency] Run all isolate related tests with/without enabling isolate groups --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ee99f33961297..8372c0628a746 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '96e7a4ff30c5850762225efca0fa1b48f28c1666', + 'dart_revision': '73fdf19b5644b94447e8d489eb17955cc48fad67', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 8436f4637c792..986952872a01d 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 544b012fb76cdd196ba7ab5c8d242a54 +Signature: 29612380ad0a4881092ca5b0f0638f18 UNUSED LICENSES: From 6c605f8a9624a99573c6801395f03bff7ee8cc4c Mon Sep 17 00:00:00 2001 From: David Worsham Date: Wed, 27 Nov 2019 16:04:30 -0800 Subject: [PATCH 286/591] Fix fml_unittests (#14062) --- fml/BUILD.gn | 4 ++++ runtime/BUILD.gn | 18 ++++++--------- shell/platform/fuchsia/dart_runner/BUILD.gn | 25 +++++---------------- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 68b9725fa6b2e..408942435ab83 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -253,6 +253,10 @@ executable("fml_unittests") { "$flutter_root/runtime:libdart", "$flutter_root/testing", ] + + if (is_fuchsia && using_fuchsia_sdk) { + libs = [ "${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/lib/libzircon.so" ] + } } if (is_fuchsia) { diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 5e41f19f031e2..ff2510513fc76 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -28,17 +28,13 @@ source_set("test_font") { group("libdart") { public_deps = [] - if (!(is_fuchsia && using_fuchsia_sdk)) { - if (flutter_runtime_mode == "profile" || - flutter_runtime_mode == "release") { - public_deps += - [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] - } else { - public_deps += [ - "$flutter_root/lib/snapshot", - "//third_party/dart/runtime:libdart_jit", - ] - } + if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") { + public_deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] + } else { + public_deps += [ + "$flutter_root/lib/snapshot", + "//third_party/dart/runtime:libdart_jit", + ] } } diff --git a/shell/platform/fuchsia/dart_runner/BUILD.gn b/shell/platform/fuchsia/dart_runner/BUILD.gn index 735a5dbfab716..ce7de986e450e 100644 --- a/shell/platform/fuchsia/dart_runner/BUILD.gn +++ b/shell/platform/fuchsia/dart_runner/BUILD.gn @@ -17,7 +17,10 @@ template("runner") { invoker_output_name = invoker.output_name extra_defines = invoker.extra_defines - extra_deps = invoker.extra_deps + extra_deps = [] + if (defined(invoker.extra_deps)) { + extra_deps += invoker.extra_deps + } executable(target_name) { output_name = invoker_output_name @@ -77,20 +80,12 @@ runner("dart_jit_runner_bin") { if (flutter_runtime_mode == "profile") { extra_defines += [ "FLUTTER_PROFILE" ] } - extra_deps = [ - "//third_party/dart/runtime:libdart_jit", - "//third_party/dart/runtime/platform:libdart_platform_jit", - ] } runner("dart_jit_product_runner_bin") { output_name = "dart_jit_product_runner" product = true extra_defines = [ "DART_PRODUCT" ] - extra_deps = [ - "//third_party/dart/runtime:libdart_jit_product", - "//third_party/dart/runtime/platform:libdart_platform_jit_product", - ] } runner("dart_aot_runner_bin") { @@ -100,11 +95,7 @@ runner("dart_aot_runner_bin") { if (flutter_runtime_mode == "profile") { extra_defines += [ "FLUTTER_PROFILE" ] } - extra_deps = [ - "embedder:dart_aot_snapshot_cc", - "//third_party/dart/runtime:libdart_precompiled_runtime", - "//third_party/dart/runtime/platform:libdart_platform_precompiled_runtime", - ] + extra_deps = [ "embedder:dart_aot_snapshot_cc" ] } runner("dart_aot_product_runner_bin") { @@ -114,11 +105,7 @@ runner("dart_aot_product_runner_bin") { "AOT_RUNTIME", "DART_PRODUCT", ] - extra_deps = [ - "embedder:dart_aot_product_snapshot_cc", - "//third_party/dart/runtime:libdart_precompiled_runtime_product", - "//third_party/dart/runtime/platform:libdart_platform_precompiled_runtime_product", - ] + extra_deps = [ "embedder:dart_aot_product_snapshot_cc" ] } template("aot_runner_package") { From 8d6fa7e85c1a4567fcf0d8a878db4345cae77adc Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Wed, 27 Nov 2019 17:20:28 -0800 Subject: [PATCH 287/591] Fallback to Roboto if no suitable font is found (#14061) --- lib/web_ui/lib/src/engine/compositor/fonts.dart | 17 +++++++++++++++++ lib/web_ui/lib/src/engine/compositor/text.dart | 13 +++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/web_ui/lib/src/engine/compositor/fonts.dart b/lib/web_ui/lib/src/engine/compositor/fonts.dart index 3c74dc49778e9..9e40600d338a9 100644 --- a/lib/web_ui/lib/src/engine/compositor/fonts.dart +++ b/lib/web_ui/lib/src/engine/compositor/fonts.dart @@ -4,9 +4,14 @@ part of engine; +const String _robotoUrl = + 'http://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf'; + class SkiaFontCollection { final List> _loadingFontBuffers = >[]; + final Set registeredFamilies = {}; + Future ensureFontsLoaded() async { final List fontBuffers = (await Future.wait(_loadingFontBuffers)) @@ -46,6 +51,8 @@ class SkiaFontCollection { final String family = fontFamily['family']; final List fontAssets = fontFamily['fonts']; + registeredFamilies.add(family); + for (dynamic fontAssetItem in fontAssets) { final Map fontAsset = fontAssetItem; final String asset = fontAsset['asset']; @@ -54,6 +61,16 @@ class SkiaFontCollection { .then((dynamic fetchResult) => fetchResult.arrayBuffer())); } } + + /// We need a default fallback font for CanvasKit, in order to + /// avoid crashing while laying out text with an unregistered font. We chose + /// Roboto to match Android. + if (!registeredFamilies.contains('Roboto')) { + // Download Roboto and add it to the font buffers. + _loadingFontBuffers.add(html.window + .fetch(_robotoUrl) + .then((dynamic fetchResult) => fetchResult.arrayBuffer())); + } } js.JsObject skFontMgr; diff --git a/lib/web_ui/lib/src/engine/compositor/text.dart b/lib/web_ui/lib/src/engine/compositor/text.dart index f598c182f2cd3..051c6ce06d6c4 100644 --- a/lib/web_ui/lib/src/engine/compositor/text.dart +++ b/lib/web_ui/lib/src/engine/compositor/text.dart @@ -9,17 +9,12 @@ part of engine; /// fonts. CanvasKit reads the font name from the font's bytes. So, we map /// some common family names to how they are registered in the Gallery app. const Map _fontFamilyOverrides = { - 'Roboto': 'Google Sans', 'GoogleSans': 'Google Sans', 'GoogleSansDisplay': 'Google Sans Display', 'MaterialIcons': 'Material Icons', - 'LibreFranklin': 'LibreFranklin', + 'LibreFranklin': 'Libre Franklin', 'AbrilFatface': 'Abril Fatface', 'packages/cupertino_icons/CupertinoIcons': 'CupertinoIcons', - '.SF Pro Text': 'Google Sans', - '.SF Pro Display': 'Google Sans', - '.SF UI Text': 'Google Sans', - '.SF UI Display': 'Google Sans', }; class SkParagraphStyle implements ui.ParagraphStyle { @@ -71,7 +66,8 @@ class SkParagraphStyle implements ui.ParagraphStyle { skTextStyle['fontSize'] = fontSize; } - if (fontFamily == null) { + if (fontFamily == null || + !skiaFontCollection.registeredFamilies.contains(fontFamily)) { fontFamily = 'Roboto'; } if (_fontFamilyOverrides.containsKey(fontFamily)) { @@ -205,7 +201,8 @@ class SkTextStyle implements ui.TextStyle { style['fontSize'] = fontSize; } - if (fontFamily == null) { + if (fontFamily == null || + !skiaFontCollection.registeredFamilies.contains(fontFamily)) { fontFamily = 'Roboto'; } From 8672e79af8737a254bfe4bd384ccf79775dff35d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 27 Nov 2019 20:54:49 -0500 Subject: [PATCH 288/591] Roll src/third_party/skia c96f5108df28..73beaaa48fcc (2 commits) (#14065) https://skia.googlesource.com/skia.git/+log/c96f5108df28..73beaaa48fcc git log c96f5108df28..73beaaa48fcc --date=short --first-parent --format='%ad %ae %s' 2019-11-27 benjaminwagner@google.com [infra] Upgrade Radeon driver 2019-11-27 benjaminwagner@google.com [infra] Update Win10 in Skolo Created with: gclient setdep -r src/third_party/skia@73beaaa48fcc If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 8372c0628a746..52f1960010568 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c96f5108df282f763a3d9da27ddd0803bd1660e3', + 'skia_revision': '73beaaa48fcc2e77d01164704a6f8789433f1b19', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index cab306daf07db..d9fc88e5fea68 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8f16c64f927c8f916b3da34e23611df8 +Signature: 03d74cc257a9c6614d170dc0424963e0 UNUSED LICENSES: From fea39c45b7086b01d155ca7f4fe07ab6d1433222 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 28 Nov 2019 07:43:05 -0500 Subject: [PATCH 289/591] Roll src/third_party/skia 73beaaa48fcc..e8c96d8e6116 (4 commits) (#14066) https://skia.googlesource.com/skia.git/+log/73beaaa48fcc..e8c96d8e6116 git log 73beaaa48fcc..e8c96d8e6116 --date=short --first-parent --format='%ad %ae %s' 2019-11-28 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 c373dfd84194..fc3ec57ddf27 (13 commits) 2019-11-28 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src c493edcc78ba..43fc40aebfd8 (368 commits) 2019-11-28 herb@google.com Reland "Naive CPU large emoji" 2019-11-28 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 663dcefa22ea..d6e903bdc9ef (4 commits) Created with: gclient setdep -r src/third_party/skia@e8c96d8e6116 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 52f1960010568..4e4369ee6026d 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '73beaaa48fcc2e77d01164704a6f8789433f1b19', + 'skia_revision': 'e8c96d8e611606ece3c296677933ac2a59e3c1f9', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index d9fc88e5fea68..5f5e74e13c918 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 03d74cc257a9c6614d170dc0424963e0 +Signature: 9d1761d4c962afdd4bee0a02f996717a UNUSED LICENSES: From 407eb42057c6ccb12cb0175fc7f892250d0bc757 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 2 Dec 2019 10:11:46 -0800 Subject: [PATCH 290/591] Roll src/third_party/dart 73fdf19b56..077795b3e5 (15 commits) (#14068) dart-lang/sdk@077795b3e5 [co19] Add .gitignore to new co19 directory dart-lang/sdk@8d874a5bd8 [cfe] Avoid cascading errors from part versioning mismatch dart-lang/sdk@cd229de42b [cfe] Increase unit test suites time out to 15 minutes dart-lang/sdk@4ba61bbbbc Fix exception in ML ranking (issue 39537) dart-lang/sdk@1069289aeb [ package:vm_service ] Raise an RPCError exception instead of a String exception for in-flight requests that have been canceled via VmService.dispose dart-lang/sdk@3204295ce1 [analyzer] send the plugin name with crash reports dart-lang/sdk@507bdd583c Revert "[ VM / Service ] Add --object-id-ring-size to unblock UX studies for memory profiling" dart-lang/sdk@98d1e99986 Relax UP for required named parameters. dart-lang/sdk@764c07ec38 [vm] Fix late_modifier_final_field_test in AST mode dart-lang/sdk@0bbcf079f3 Issue 39532. Support for question mark in typedef generic function type. dart-lang/sdk@6f8fa3cbb3 Fix an NPE when using ml without available suggestions (issue 39535) dart-lang/sdk@a17809ad18 [kernel] Add TypeEnvironment.forInElementType dart-lang/sdk@ba222dac23 [vm] Fix late_modifier_non_final_field_test in AST mode dart-lang/sdk@e56857fcce Stop using FeatureSet in TypeSystem. dart-lang/sdk@eddfaff322 Issue 39509. Support for configurable imports in AnalysisDriver. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4e4369ee6026d..ebf622b80d0a1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '73fdf19b5644b94447e8d489eb17955cc48fad67', + 'dart_revision': '077795b3e527bb4868f46ca1b0f52e6ac645e069', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 986952872a01d..7b0ae0239f028 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 29612380ad0a4881092ca5b0f0638f18 +Signature: 4ce27405521b707b43c2d0e6efdc8d5b UNUSED LICENSES: From 9cda1eb97853313355cd291c3d2afc5aeadba67c Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 2 Dec 2019 11:07:33 -0800 Subject: [PATCH 291/591] Roll src/third_party/dart 077795b3e5..3f32196958 (29 commits) (#14072) dart-lang/sdk@3f32196958 Make LateInitialziationError abstract and uninstantiable. dart-lang/sdk@5013ce6e5d [cfe] Use correct types and errors in late lowering dart-lang/sdk@46a5f35da7 [infra] Bump VERSION to 2.8.0 dart-lang/sdk@97d6ac21d5 [CFE] Spell check on kernel src; presubmit on kernel & _fe_analyzer_shared dart-lang/sdk@1454a67e09 [cfe] Don't use multiple null-aware guards in opt-out dart-lang/sdk@12d9bedb33 [CFE] Let spell checker report all reported words at the end dart-lang/sdk@88630cc235 Move real resolveConstructors from ClassBuilderImpl to SourceClassBuilder dart-lang/sdk@05406716bd [infra] Add count of test result messages sent to Pub/Sub dart-lang/sdk@c1d21c368a Move a few methods from ClassBuilderImpl to SourceClassBuilder dart-lang/sdk@1c3468a646 [kernel] Remove Library.isExternal dart-lang/sdk@3557973a75 Move 5 verifier classes out of resolver.dart, into their own libraries. dart-lang/sdk@3de7487a2c Report an error for 'late final' fields in classes with const constructors. dart-lang/sdk@59d3a2d143 Unify the names of two codes for documentation purposes dart-lang/sdk@0a575d8681 Unify two diagnostics into instantiate_abstract_class dart-lang/sdk@7707c2a84c Issue 39563. Expression body always exists. dart-lang/sdk@c6326de855 Improve the highlight region for variable_type_mismatch diagnostic dart-lang/sdk@f7549f6dd7 [cfe] Test suites runner: improve format of timing information dart-lang/sdk@de36aa0f1a [dartfuzz] Temporarily disable Float32x4.reciprocal() and Float32x4.reciprocalSqrt() dart-lang/sdk@59c41f21d6 [vm/compiler] Avoid dead global pool entries due to bailouts dart-lang/sdk@63333deed6 [vm/cfe] Elaborate for-in statements during async transform dart-lang/sdk@5b31b1e26d Improve the selection range when reporting that an invoked constructor is not marked as const dart-lang/sdk@46c042a118 Improve a diagnostic message by unwrapping Future (issue 39561) dart-lang/sdk@078806851c [vm] Use static type of captured variables. dart-lang/sdk@47ce35faf8 [benchmark] Exclude large front end tests and fix tensorflow exclusion. dart-lang/sdk@27bdabe02e [SDK] Stop injecting when async hasn't yielded yet. dart-lang/sdk@8023accf3f [vm/ffi] Fix source information in generated AST nodes - part 2 dart-lang/sdk@f9cedb6813 [vm/ffi] Fix source information in generated AST nodes dart-lang/sdk@4d95ec1360 [vm] Enable multiple entry-points for unoptimized calls. dart-lang/sdk@659b32ffc8 [cfe] Handle chained null shorting and null aware if-null set --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ebf622b80d0a1..d01a36d6f09a4 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '077795b3e527bb4868f46ca1b0f52e6ac645e069', + 'dart_revision': '3f32196958970682b1acba6abb73bb891e23709e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 7b0ae0239f028..7c31542cf3dc8 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 4ce27405521b707b43c2d0e6efdc8d5b +Signature: 69a0bc4edef486cababeace42b42336a UNUSED LICENSES: @@ -7699,6 +7699,7 @@ FILE: ../../../third_party/dart/sdk/lib/ffi/dynamic_library.dart FILE: ../../../third_party/dart/sdk/lib/ffi/ffi.dart FILE: ../../../third_party/dart/sdk/lib/ffi/native_type.dart FILE: ../../../third_party/dart/sdk/lib/ffi/struct.dart +FILE: ../../../third_party/dart/sdk/lib/internal/errors.dart FILE: ../../../third_party/dart/sdk/lib/wasm/wasm.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/rti.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart @@ -7711,6 +7712,7 @@ FILE: ../../../third_party/dart/sdk_nnbd/lib/ffi/dynamic_library.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/ffi/ffi.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/ffi/native_type.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/ffi/struct.dart +FILE: ../../../third_party/dart/sdk_nnbd/lib/internal/errors.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/wasm/wasm.dart ---------------------------------------------------------------------------------------------------- Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file From 81c7e61745efe7c3e4c57062326e732867979f93 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 2 Dec 2019 15:50:58 -0500 Subject: [PATCH 292/591] Roll fuchsia/sdk/core/linux-amd64 from mSEnz... to 2Nav3... (#14067) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d01a36d6f09a4..f30db6549286d 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'mSEnzCU3uCmYqqAOmKbv26c56DPvGn4mXFgY889ClgIC' + 'version': '2Nav3tcxx2Ds1aiVVybULj9D8HgFjiGX1mDizCy5Vh4C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index c408c96b0b2ed..64262b6ecf14a 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: c1e1c0eb7d9d96272430cfb588eeeff6 +Signature: 1a927dbf378d689cf7aed0c567181af4 UNUSED LICENSES: @@ -509,6 +509,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/meta.json @@ -1285,9 +1286,12 @@ FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/ssh.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/storage.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/test.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/common.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/cpu_metrics.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/drm_fps.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/flutter_frame_stats.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/memory_metrics.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/scenic_frame_stats.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/temperature_metrics.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_results.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_spec.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_delta.dart @@ -1449,6 +1453,8 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/layout.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/modifiers.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/semantic_keys.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/lifecycle_controller.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/device_listener.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json @@ -2163,6 +2169,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/meta.json From 00368886b2ff1a136a462f9929a48ef27d03f047 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 2 Dec 2019 18:07:03 -0500 Subject: [PATCH 293/591] Roll fuchsia/sdk/core/mac-amd64 from aVdHP... to 6QxEZ... (#14080) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f30db6549286d..a38fc1615e184 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'aVdHPkA77NjC6uFR_fhWb5gjpTaUlIxQ1TE_rWB5zCQC' + 'version': '6QxEZClcJgnmYaAZ1XT9Nprnp80pAyPRbtki92Bx0o8C' } ], 'condition': 'host_os == "mac"', From 75749e04629021563d3f23f00db804e1dc7db479 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 2 Dec 2019 15:34:22 -0800 Subject: [PATCH 294/591] Roll src/third_party/dart 3f32196958..e4344a568f (1 commits) (#14073) dart-lang/sdk@e4344a568f Report INVALID_OPTIONAL_PARAMETER_TYPE when default values are not allowed. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a38fc1615e184..e7bb5bbaa2116 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '3f32196958970682b1acba6abb73bb891e23709e', + 'dart_revision': 'e4344a568f66ba122a172dd53c4b922a9c34502b', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 0bf6c7429ad2cd954d7489fba3f140030a86e417 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 2 Dec 2019 18:38:35 -0500 Subject: [PATCH 295/591] Roll src/third_party/skia e8c96d8e6116..2792515dab7c (1 commits) (#14069) https://skia.googlesource.com/skia.git/+log/e8c96d8e6116..2792515dab7c git log e8c96d8e6116..2792515dab7c --date=short --first-parent --format='%ad %ae %s' 2019-11-28 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@2792515dab7c If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bungeman@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bungeman@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e7bb5bbaa2116..7f8fd4ac92e52 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e8c96d8e611606ece3c296677933ac2a59e3c1f9', + 'skia_revision': '2792515dab7c5623e6b02dc0645ca87bc035c545', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 5f5e74e13c918..dc1ea2555d9f2 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9d1761d4c962afdd4bee0a02f996717a +Signature: 81e6c3d8cb6f315b7415188b8e0ca05a UNUSED LICENSES: From ea721ebf1ac7a7cf024e539dcfdd16910d964136 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 2 Dec 2019 15:49:15 -0800 Subject: [PATCH 296/591] Fix live region logic (#14081) --- .../android/io/flutter/view/AccessibilityBridge.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 95f2a1d88565b..48a1bcc93c37d 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -1377,16 +1377,7 @@ void updateSemantics(@NonNull ByteBuffer buffer, @NonNull String[] strings) { } sendAccessibilityEvent(event); } - if (object.hasFlag(Flag.IS_LIVE_REGION)) { - String label = object.label == null ? "" : object.label; - String previousLabel = object.previousLabel == null ? "" : object.label; - if (!label.equals(previousLabel) || !object.hadFlag(Flag.IS_LIVE_REGION)) { - sendWindowContentChangeEvent(object.id); - } - } else if (object.hasFlag(Flag.IS_TEXT_FIELD) && object.didChangeLabel() - && inputFocusedSemanticsNode != null && inputFocusedSemanticsNode.id == object.id) { - // Text fields should announce when their label changes while focused. We use a live - // region tag to do so, and this event triggers that update. + if (object.hasFlag(Flag.IS_LIVE_REGION) && object.didChangeLabel()) { sendWindowContentChangeEvent(object.id); } if (accessibilityFocusedSemanticsNode != null && accessibilityFocusedSemanticsNode.id == object.id From a68805bcf8a884ac2cb0ff2b161f261e75786719 Mon Sep 17 00:00:00 2001 From: Francisco Magdaleno Date: Mon, 2 Dec 2019 16:17:19 -0800 Subject: [PATCH 297/591] Listen to keyUp event on meta modified keys (#13984) --- .../framework/Source/FlutterViewController.mm | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 8492d902ff793..926c0cafd6589 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -100,6 +100,11 @@ @interface FlutterViewController () */ @property(nonatomic) KeyboardState keyboardState; +/** + * Event monitor for keyUp events. + */ +@property(nonatomic) id keyUpMonitor; + /** * Starts running |engine|, including any initial setup. */ @@ -241,6 +246,14 @@ - (void)viewWillAppear { if (!_engine.running) { [self launchEngine]; } + [self listenForMetaModifiedKeyUpEvents]; +} + +- (void)viewWillDisappear { + // Per Apple's documentation, it is discouraged to call removeMonitor: in dealloc, and it's + // recommended to be called earlier in the lifecycle. + [NSEvent removeMonitor:_keyUpMonitor]; + _keyUpMonitor = nil; } - (void)dealloc { @@ -268,7 +281,6 @@ - (void)addKeyResponder:(NSResponder*)responder { } - (void)removeKeyResponder:(NSResponder*)responder { - [self.additionalKeyResponders removeObject:responder]; } #pragma mark - Private methods @@ -287,6 +299,27 @@ - (BOOL)launchEngine { return YES; } +// macOS does not call keyUp: on a key while the command key is pressed. This results in a loss +// of a key event once the modified key is released. This method registers the +// ViewController as a listener for a keyUp event before it's handled by NSApplication, and should +// NOT modify the event to avoid any unexpected behavior. +- (void)listenForMetaModifiedKeyUpEvents { + NSAssert(_keyUpMonitor == nil, @"_keyUpMonitor was already created"); + FlutterViewController* __weak weakSelf = self; + _keyUpMonitor = [NSEvent + addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp + handler:^NSEvent*(NSEvent* event) { + // Intercept keyUp only for events triggered on the current + // view. + if (weakSelf.view && + ([[event window] firstResponder] == weakSelf.view) && + ([event modifierFlags] & NSEventModifierFlagCommand) && + ([event type] == NSEventTypeKeyUp)) + [weakSelf keyUp:event]; + return event; + }]; +} + - (void)configureTrackingArea { if (_mouseTrackingMode != FlutterMouseTrackingModeNone && self.view) { NSTrackingAreaOptions options = From 063efce92b5f4ba873931520c1f942f8fa45212a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 2 Dec 2019 16:33:04 -0800 Subject: [PATCH 298/591] Report backing store changes in the macOS view (#13933) --- shell/platform/darwin/macos/framework/Source/FlutterView.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index c255106a419ad..1b46ac600ed9e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -48,4 +48,9 @@ - (BOOL)acceptsFirstResponder { return YES; } +- (void)viewDidChangeBackingProperties { + [super viewDidChangeBackingProperties]; + [_reshapeListener viewDidReshape:self]; +} + @end From faa11214c09b9ce0260b3c5a386502abe95a85fc Mon Sep 17 00:00:00 2001 From: liyuqian Date: Mon, 2 Dec 2019 16:38:03 -0800 Subject: [PATCH 299/591] Run benchmarks in Cirrus (#13950) --- testing/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/run_tests.sh b/testing/run_tests.sh index d6e1a7472a83d..8cefc971a4c04 100755 --- a/testing/run_tests.sh +++ b/testing/run_tests.sh @@ -5,4 +5,4 @@ set -o pipefail -e; BUILD_VARIANT="${1:-host_debug_unopt}" CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -python "${CURRENT_DIR}/run_tests.py" --variant="${BUILD_VARIANT}" --type=engine,dart +python "${CURRENT_DIR}/run_tests.py" --variant="${BUILD_VARIANT}" --type=engine,dart,benchmarks From e3e2a3899e273f7986765b1f42565c06089e1e53 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 2 Dec 2019 16:51:30 -0800 Subject: [PATCH 300/591] Remove suprious warning (#14079) --- .../io/flutter/view/AccessibilityBridge.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 48a1bcc93c37d..796c36e92b3c7 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -1473,11 +1473,7 @@ private void sendAccessibilityEvent(int viewId, int eventType) { if (!accessibilityManager.isEnabled()) { return; } - if (viewId == ROOT_NODE_ID) { - rootAccessibilityView.sendAccessibilityEvent(eventType); - } else { - sendAccessibilityEvent(obtainAccessibilityEvent(viewId, eventType)); - } + sendAccessibilityEvent(obtainAccessibilityEvent(viewId, eventType)); } /** @@ -1491,7 +1487,9 @@ private void sendAccessibilityEvent(@NonNull AccessibilityEvent event) { if (!accessibilityManager.isEnabled()) { return; } - // TODO(mattcarroll): why are we explicitly talking to the root view's parent? + // See https://developer.android.com/reference/android/view/View.html#sendAccessibilityEvent(int) + // We just want the final part at this point, since the event parameter + // has already been correctly populated. rootAccessibilityView.getParent().requestSendAccessibilityEvent(rootAccessibilityView, event); } @@ -1542,9 +1540,6 @@ private void sendWindowContentChangeEvent(int virtualViewId) { * invoked to create an {@link AccessibilityEvent} for the {@link #rootAccessibilityView}. */ private AccessibilityEvent obtainAccessibilityEvent(int virtualViewId, int eventType) { - if (BuildConfig.DEBUG && virtualViewId == ROOT_NODE_ID) { - Log.e(TAG, "VirtualView node must not be the root node."); - } AccessibilityEvent event = AccessibilityEvent.obtain(eventType); event.setPackageName(rootAccessibilityView.getContext().getPackageName()); event.setSource(rootAccessibilityView, virtualViewId); From 1a96087747acff244e1f5a616b5bcfa29a7bdae5 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Tue, 3 Dec 2019 11:57:54 -0500 Subject: [PATCH 301/591] Update to newer path fill-type API in Skia (#14070) --- lib/ui/painting/path.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/painting/path.cc b/lib/ui/painting/path.cc index a670c79fc5928..e9f74255b5b9a 100644 --- a/lib/ui/painting/path.cc +++ b/lib/ui/painting/path.cc @@ -71,11 +71,11 @@ CanvasPath::CanvasPath() {} CanvasPath::~CanvasPath() {} int CanvasPath::getFillType() { - return path_.getFillType(); + return static_cast(path_.getFillType()); } void CanvasPath::setFillType(int fill_type) { - path_.setFillType(static_cast(fill_type)); + path_.setFillType(static_cast(fill_type)); } void CanvasPath::moveTo(float x, float y) { From a9f595fa405cf7ea6756fb1580a509515fa4c46f Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 3 Dec 2019 09:38:22 -0800 Subject: [PATCH 302/591] Roll src/third_party/dart e4344a568f..a4d799c402 (7 commits) (#14083) dart-lang/sdk@a4d799c402 Migrate corelib/ tests starting with b-d to NNBD. dart-lang/sdk@18a9b29687 Add a repro for issue #39609 dart-lang/sdk@ec28bf1fd1 [analyzer] refactor the vm status section in the analyzer diagnostics page dart-lang/sdk@bf69257c5f [dart:io] socket profiler in dart:io dart-lang/sdk@1392d61c9b [infra] Modify 2.7.0 make_version hack so that it also works on other branches dart-lang/sdk@4fe27fd444 Report an error when 'await' is used in the initializer of a late local variable. dart-lang/sdk@6305276184 Remove deprecated 'byteStore' and 'fileContentOverlay' parameters. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7f8fd4ac92e52..91bb383edb470 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'e4344a568f66ba122a172dd53c4b922a9c34502b', + 'dart_revision': 'a4d799c402efc36327cad2f42186bb48ea9cf18a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 7c31542cf3dc8..218b5ebeb1a56 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 69a0bc4edef486cababeace42b42336a +Signature: f8a490c0ed4d443c531bcee5cb11ff28 UNUSED LICENSES: From fffeb8fe34f485c65743f62687b749343bd563fe Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 3 Dec 2019 12:42:22 -0500 Subject: [PATCH 303/591] Roll fuchsia/sdk/core/mac-amd64 from 6QxEZ... to OSk8h... (#14085) Roll fuchsia/sdk/core/mac-amd64 from 6QxEZ... to OSk8h... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 91bb383edb470..576270b959494 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '6QxEZClcJgnmYaAZ1XT9Nprnp80pAyPRbtki92Bx0o8C' + 'version': 'OSk8h97wpfy2e7hem2Bg7lEkZqW-XCr80qpQGWrNRKUC' } ], 'condition': 'host_os == "mac"', From 620f5281b819f304e8e9e945222e26b17b087cc3 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Tue, 3 Dec 2019 09:43:02 -0800 Subject: [PATCH 304/591] Add flow test fixtures and tests (#13986) --- BUILD.gn | 5 +- ci/licenses_golden/licenses_flutter | 13 + flow/BUILD.gn | 74 ++- flow/embedded_views.h | 17 + flow/flow_run_all_unittests.cc | 7 + flow/layers/backdrop_filter_layer.cc | 2 - flow/layers/backdrop_filter_layer.h | 1 - .../layers/backdrop_filter_layer_unittests.cc | 184 +++++++ flow/layers/clip_path_layer.cc | 2 - flow/layers/clip_path_layer.h | 1 - flow/layers/clip_path_layer_unittests.cc | 194 ++++++++ flow/layers/clip_rect_layer.cc | 2 - flow/layers/clip_rect_layer.h | 1 - flow/layers/clip_rect_layer_unittests.cc | 192 ++++++++ flow/layers/clip_rrect_layer.cc | 2 - flow/layers/clip_rrect_layer.h | 1 - flow/layers/clip_rrect_layer_unittests.cc | 197 ++++++++ flow/layers/color_filter_layer.cc | 2 - flow/layers/color_filter_layer.h | 1 - flow/layers/color_filter_layer_unittests.cc | 197 ++++++++ flow/layers/container_layer.cc | 8 +- flow/layers/container_layer.h | 3 +- flow/layers/container_layer_unittests.cc | 200 ++++++++ flow/layers/layer_tree.cc | 2 - flow/layers/layer_tree.h | 2 - flow/layers/layer_tree_unittests.cc | 203 ++++++++ flow/layers/opacity_layer.cc | 2 - flow/layers/opacity_layer.h | 1 - flow/layers/opacity_layer_unittests.cc | 310 ++++++++++++ flow/layers/performance_overlay_layer.cc | 55 ++- flow/layers/performance_overlay_layer.h | 7 + .../performance_overlay_layer_unittests.cc | 85 +++- flow/layers/physical_shape_layer.cc | 95 ++-- flow/layers/physical_shape_layer.h | 12 +- flow/layers/physical_shape_layer_unittests.cc | 272 +++++++++-- flow/layers/picture_layer.cc | 2 - flow/layers/picture_layer.h | 1 - flow/layers/picture_layer_unittests.cc | 102 ++++ flow/layers/platform_view_layer.cc | 2 - flow/layers/platform_view_layer.h | 1 - flow/layers/platform_view_layer_unittests.cc | 38 ++ flow/layers/shader_mask_layer.cc | 2 - flow/layers/shader_mask_layer.h | 1 - flow/layers/shader_mask_layer_unittests.cc | 255 ++++++++++ flow/layers/texture_layer.cc | 2 - flow/layers/texture_layer.h | 1 - flow/layers/texture_layer_unittests.cc | 57 +++ flow/layers/transform_layer.cc | 2 - flow/layers/transform_layer.h | 1 - flow/layers/transform_layer_unittests.cc | 226 +++++++++ flow/matrix_decomposition_unittests.cc | 9 +- flow/mutators_stack_unittests.cc | 1 + flow/raster_cache_unittests.cc | 10 + flow/skia_gpu_object.h | 3 - flow/skia_gpu_object_unittests.cc | 107 +++- flow/testing/layer_test.h | 74 +++ flow/testing/mock_layer.cc | 38 ++ flow/testing/mock_layer.h | 50 ++ flow/testing/mock_layer_unittests.cc | 85 ++++ flow/testing/mock_texture.cc | 31 ++ flow/testing/mock_texture.h | 57 +++ flow/testing/mock_texture_unittests.cc | 43 ++ flow/testing/skia_gpu_object_layer_test.cc | 18 + flow/testing/skia_gpu_object_layer_test.h | 30 ++ flow/texture.cc | 10 +- flow/texture.h | 8 +- flow/texture_unittests.cc | 99 +++- fml/BUILD.gn | 5 +- runtime/runtime_test.cc | 20 +- runtime/runtime_test.h | 15 +- shell/common/shell_test.cc | 36 +- shell/common/shell_test.h | 17 +- .../platform/embedder/tests/embedder_test.cc | 15 +- shell/platform/embedder/tests/embedder_test.h | 8 - .../flutter/meta/flutter_aot_runner.cmx | 1 + .../flutter/meta/flutter_jit_runner.cmx | 1 + testing/BUILD.gn | 7 +- testing/assertions_skia.cc | 118 +++++ testing/assertions_skia.h | 84 +--- testing/canvas_test.h | 33 ++ testing/mock_canvas.cc | 457 ++++++++++++++++++ testing/mock_canvas.h | 318 ++++++++++++ testing/mock_canvas_unittests.cc | 30 ++ testing/thread_test.cc | 14 +- testing/thread_test.h | 15 +- 85 files changed, 4524 insertions(+), 388 deletions(-) create mode 100644 flow/layers/backdrop_filter_layer_unittests.cc create mode 100644 flow/layers/clip_path_layer_unittests.cc create mode 100644 flow/layers/clip_rect_layer_unittests.cc create mode 100644 flow/layers/clip_rrect_layer_unittests.cc create mode 100644 flow/layers/color_filter_layer_unittests.cc create mode 100644 flow/layers/container_layer_unittests.cc create mode 100644 flow/layers/layer_tree_unittests.cc create mode 100644 flow/layers/opacity_layer_unittests.cc create mode 100644 flow/layers/picture_layer_unittests.cc create mode 100644 flow/layers/platform_view_layer_unittests.cc create mode 100644 flow/layers/shader_mask_layer_unittests.cc create mode 100644 flow/layers/texture_layer_unittests.cc create mode 100644 flow/layers/transform_layer_unittests.cc create mode 100644 flow/testing/layer_test.h create mode 100644 flow/testing/mock_layer.cc create mode 100644 flow/testing/mock_layer.h create mode 100644 flow/testing/mock_layer_unittests.cc create mode 100644 flow/testing/mock_texture.cc create mode 100644 flow/testing/mock_texture.h create mode 100644 flow/testing/mock_texture_unittests.cc create mode 100644 flow/testing/skia_gpu_object_layer_test.cc create mode 100644 flow/testing/skia_gpu_object_layer_test.h create mode 100644 testing/assertions_skia.cc create mode 100644 testing/canvas_test.h create mode 100644 testing/mock_canvas.cc create mode 100644 testing/mock_canvas.h create mode 100644 testing/mock_canvas_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index 361a5c6f0e2f6..419ae849544a1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -95,7 +95,10 @@ group("flutter") { # Fuchsia currently only supports a subset of our unit tests if (is_fuchsia) { - public_deps += [ "$flutter_root/fml:fml_tests" ] + public_deps += [ + "$flutter_root/flow:flow_tests", + "$flutter_root/fml:fml_tests", + ] } } diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1e81f43602348..f57b00086d432 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -30,25 +30,33 @@ FILE: ../../../flutter/flow/instrumentation.cc FILE: ../../../flutter/flow/instrumentation.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h +FILE: ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/child_scene_layer.cc FILE: ../../../flutter/flow/layers/child_scene_layer.h FILE: ../../../flutter/flow/layers/clip_path_layer.cc FILE: ../../../flutter/flow/layers/clip_path_layer.h +FILE: ../../../flutter/flow/layers/clip_path_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rect_layer.cc FILE: ../../../flutter/flow/layers/clip_rect_layer.h +FILE: ../../../flutter/flow/layers/clip_rect_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.h +FILE: ../../../flutter/flow/layers/clip_rrect_layer_unittests.cc FILE: ../../../flutter/flow/layers/color_filter_layer.cc FILE: ../../../flutter/flow/layers/color_filter_layer.h +FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h +FILE: ../../../flutter/flow/layers/container_layer_unittests.cc FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h FILE: ../../../flutter/flow/layers/layer_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h +FILE: ../../../flutter/flow/layers/layer_tree_unittests.cc FILE: ../../../flutter/flow/layers/layer_unittests.cc FILE: ../../../flutter/flow/layers/opacity_layer.cc FILE: ../../../flutter/flow/layers/opacity_layer.h +FILE: ../../../flutter/flow/layers/opacity_layer_unittests.cc FILE: ../../../flutter/flow/layers/performance_overlay_layer.cc FILE: ../../../flutter/flow/layers/performance_overlay_layer.h FILE: ../../../flutter/flow/layers/performance_overlay_layer_unittests.cc @@ -57,14 +65,19 @@ FILE: ../../../flutter/flow/layers/physical_shape_layer.h FILE: ../../../flutter/flow/layers/physical_shape_layer_unittests.cc FILE: ../../../flutter/flow/layers/picture_layer.cc FILE: ../../../flutter/flow/layers/picture_layer.h +FILE: ../../../flutter/flow/layers/picture_layer_unittests.cc FILE: ../../../flutter/flow/layers/platform_view_layer.cc FILE: ../../../flutter/flow/layers/platform_view_layer.h +FILE: ../../../flutter/flow/layers/platform_view_layer_unittests.cc FILE: ../../../flutter/flow/layers/shader_mask_layer.cc FILE: ../../../flutter/flow/layers/shader_mask_layer.h +FILE: ../../../flutter/flow/layers/shader_mask_layer_unittests.cc FILE: ../../../flutter/flow/layers/texture_layer.cc FILE: ../../../flutter/flow/layers/texture_layer.h +FILE: ../../../flutter/flow/layers/texture_layer_unittests.cc FILE: ../../../flutter/flow/layers/transform_layer.cc FILE: ../../../flutter/flow/layers/transform_layer.h +FILE: ../../../flutter/flow/layers/transform_layer_unittests.cc FILE: ../../../flutter/flow/matrix_decomposition.cc FILE: ../../../flutter/flow/matrix_decomposition.h FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index c99f2177e772f..50981bb316e16 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -4,8 +4,8 @@ if (is_fuchsia) { import("//build/fuchsia/sdk.gni") + import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") } - import("$flutter_root/testing/testing.gni") source_set("flow") { @@ -102,6 +102,26 @@ test_fixtures("flow_fixtures") { fixtures = [] } +source_set("flow_testing") { + testonly = true + + sources = [ + "testing/layer_test.h", + "testing/mock_layer.cc", + "testing/mock_layer.h", + "testing/mock_texture.cc", + "testing/mock_texture.h", + "testing/skia_gpu_object_layer_test.cc", + "testing/skia_gpu_object_layer_test.h", + ] + + public_deps = [ + ":flow", + "$flutter_root/testing:skia", + "//third_party/googletest:gtest", + ] +} + executable("flow_unittests") { testonly = true @@ -109,23 +129,75 @@ executable("flow_unittests") { "flow_run_all_unittests.cc", "flow_test_utils.cc", "flow_test_utils.h", + "layers/backdrop_filter_layer_unittests.cc", + "layers/clip_path_layer_unittests.cc", + "layers/clip_rect_layer_unittests.cc", + "layers/clip_rrect_layer_unittests.cc", + "layers/color_filter_layer_unittests.cc", + "layers/container_layer_unittests.cc", + "layers/layer_tree_unittests.cc", "layers/layer_unittests.cc", + "layers/opacity_layer_unittests.cc", "layers/performance_overlay_layer_unittests.cc", "layers/physical_shape_layer_unittests.cc", + "layers/picture_layer_unittests.cc", + "layers/platform_view_layer_unittests.cc", + "layers/shader_mask_layer_unittests.cc", + "layers/texture_layer_unittests.cc", + "layers/transform_layer_unittests.cc", "matrix_decomposition_unittests.cc", "mutators_stack_unittests.cc", "raster_cache_unittests.cc", "skia_gpu_object_unittests.cc", + "testing/mock_layer_unittests.cc", + "testing/mock_texture_unittests.cc", "texture_unittests.cc", ] deps = [ ":flow", ":flow_fixtures", + ":flow_testing", "$flutter_root/fml", + "$flutter_root/testing:skia", "$flutter_root/testing:testing_lib", "//third_party/dart/runtime:libdart_jit", # for tracing "//third_party/googletest:gtest", "//third_party/skia", ] } + +if (is_fuchsia) { + fuchsia_archive("flow_tests") { + testonly = true + + deps = [ + ":flow_unittests", + ] + + binary = "flow_unittests" + + libraries = common_libs + + meta_dir = "$flutter_root/testing/fuchsia/meta" + cmx_file = "$meta_dir/fuchsia_test.cmx" + + resources = [ + { + path = rebase_path( + "$flutter_root/testing/resources/performance_overlay_gold_60fps.png") + dest = "flutter/testing/resources/performance_overlay_gold_60fps.png" + }, + { + path = rebase_path( + "$flutter_root/testing/resources/performance_overlay_gold_90fps.png") + dest = "flutter/testing/resources/performance_overlay_gold_90fps.png" + }, + { + path = rebase_path( + "$flutter_root/testing/resources/performance_overlay_gold_120fps.png") + dest = "flutter/testing/resources/performance_overlay_gold_120fps.png" + }, + ] + } +} diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 919ee83a8bb86..030eb88c8a06d 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -143,6 +143,7 @@ class MutatorsStack { // Returns an iterator pointing to the bottom of the stack. const std::vector>::const_reverse_iterator Bottom() const; + bool is_empty() const { return vector_.empty(); } bool operator==(const MutatorsStack& other) const { if (vector_.size() != other.vector_.size()) { @@ -156,10 +157,26 @@ class MutatorsStack { return true; } + bool operator==(const std::vector& other) const { + if (vector_.size() != other.size()) { + return false; + } + for (size_t i = 0; i < vector_.size(); i++) { + if (*vector_[i] != other[i]) { + return false; + } + } + return true; + } + bool operator!=(const MutatorsStack& other) const { return !operator==(other); } + bool operator!=(const std::vector& other) const { + return !operator==(other); + } + private: std::vector> vector_; }; // MutatorsStack diff --git a/flow/flow_run_all_unittests.cc b/flow/flow_run_all_unittests.cc index 4cf0ba3d7fcdf..39963730172ee 100644 --- a/flow/flow_run_all_unittests.cc +++ b/flow/flow_run_all_unittests.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "flutter/fml/build_config.h" #include "flutter/fml/command_line.h" #include "flutter/fml/logging.h" #include "gtest/gtest.h" @@ -23,8 +24,14 @@ int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); fml::CommandLine cmd = fml::CommandLineFromArgcArgv(argc, argv); + +#if defined(OS_FUCHSIA) + flutter::SetGoldenDir(cmd.GetOptionValueWithDefault( + "golden-dir", "/pkg/data/flutter/testing/resources")); +#else flutter::SetGoldenDir( cmd.GetOptionValueWithDefault("golden-dir", "flutter/testing/resources")); +#endif flutter::SetFontFile(cmd.GetOptionValueWithDefault( "font-file", "flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf")); diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index cf6f5cf522b96..c69b7fcb3d8d1 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -11,8 +11,6 @@ BackdropFilterLayer::BackdropFilterLayer(sk_sp filter) set_layer_reads_surface(filter_.get() != nullptr); } -BackdropFilterLayer::~BackdropFilterLayer() = default; - void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index ede9ceeef41f8..732a1ac27e89a 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -14,7 +14,6 @@ namespace flutter { class BackdropFilterLayer : public ContainerLayer { public: BackdropFilterLayer(sk_sp filter); - ~BackdropFilterLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc new file mode 100644 index 0000000000000..b0582300da91f --- /dev/null +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -0,0 +1,184 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/backdrop_filter_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/effects/SkImageFilters.h" + +namespace flutter { +namespace testing { + +using BackdropFilterLayerTest = LayerTest; + +TEST_F(BackdropFilterLayerTest, EmptyLayer) { + auto layer = std::make_shared(sk_sp()); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(BackdropFilterLayerTest, PaintBeforePreroll) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(sk_sp()); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(BackdropFilterLayerTest, EmptyFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(nullptr); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(BackdropFilterLayerTest, SimpleFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + layer_filter, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(BackdropFilterLayerTest, MultipleChildren) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + layer_filter, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(BackdropFilterLayerTest, Nested) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray)); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer1 = std::make_shared(layer_filter1); + auto layer2 = std::make_shared(layer_filter2); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), children_bounds); + EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + layer1->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + layer_filter1, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), + SkPaint(), layer_filter2, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 0c63b2331d9f5..acc6f17dd5433 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -18,8 +18,6 @@ ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } -ClipPathLayer::~ClipPathLayer() = default; - void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_path_bounds = clip_path_.getBounds(); diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index fd4d56f0db7f0..c21e53c34e76e 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -12,7 +12,6 @@ namespace flutter { class ClipPathLayer : public ContainerLayer { public: ClipPathLayer(const SkPath& clip_path, Clip clip_behavior = Clip::antiAlias); - ~ClipPathLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc new file mode 100644 index 0000000000000..68b5fe9b48bad --- /dev/null +++ b/flow/layers/clip_path_layer_unittests.cc @@ -0,0 +1,194 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/clip_path_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ClipPathLayerTest = LayerTest; + +TEST_F(ClipPathLayerTest, ClipNone) { + EXPECT_DEATH_IF_SUPPORTED( + auto clip = std::make_shared(SkPath(), Clip::none), + "clip_behavior != Clip::none"); +} + +TEST_F(ClipPathLayerTest, EmptyLayer) { + auto layer = std::make_shared(SkPath(), Clip::hardEdge); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipPathLayerTest, PaintBeforePreroll) { + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath layer_path = SkPath().addRect(layer_bounds); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipPathLayerTest, CulledLayer) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + preroll_context()->cull_rect = kEmptyRect; // Cull everything + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipPathLayerTest, ChildOutsideBounds) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipPathLayerTest, FullyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipPathLayerTest, PartiallyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index 5ee6046b19a73..141ad8a009694 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -12,8 +12,6 @@ ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } -ClipRectLayer::~ClipRectLayer() = default; - void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; if (context->cull_rect.intersect(clip_rect_)) { diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index 76c5a3f01c873..50eef22a46e2f 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -12,7 +12,6 @@ namespace flutter { class ClipRectLayer : public ContainerLayer { public: ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior); - ~ClipRectLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc new file mode 100644 index 0000000000000..c5ee39f2e02e8 --- /dev/null +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -0,0 +1,192 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/clip_rect_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ClipRectLayerTest = LayerTest; + +TEST_F(ClipRectLayerTest, ClipNone) { + EXPECT_DEATH_IF_SUPPORTED( + auto clip = std::make_shared(kEmptyRect, Clip::none), + "clip_behavior != Clip::none"); +} + +TEST_F(ClipRectLayerTest, EmptyLayer) { + auto layer = std::make_shared(kEmptyRect, Clip::hardEdge); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRectLayerTest, PaintBeforePreroll) { + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRectLayerTest, CulledLayer) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + preroll_context()->cull_rect = kEmptyRect; // Cull everything + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRectLayerTest, ChildOutsideBounds) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRectLayerTest, FullyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRectLayerTest, PartiallyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 037d91ddcb5ff..dd4fdd09f03d4 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -12,8 +12,6 @@ ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } -ClipRRectLayer::~ClipRRectLayer() = default; - void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_rrect_bounds = clip_rrect_.getBounds(); diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index 53f74f30a0776..ce1cca2b568de 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -12,7 +12,6 @@ namespace flutter { class ClipRRectLayer : public ContainerLayer { public: ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior); - ~ClipRRectLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc new file mode 100644 index 0000000000000..4eb506eec5ecc --- /dev/null +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -0,0 +1,197 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/clip_rrect_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ClipRRectLayerTest = LayerTest; + +TEST_F(ClipRRectLayerTest, ClipNone) { + const SkRRect layer_rrect = SkRRect::MakeEmpty(); + EXPECT_DEATH_IF_SUPPORTED( + auto clip = std::make_shared(layer_rrect, Clip::none), + "clip_behavior != Clip::none"); +} + +TEST_F(ClipRRectLayerTest, EmptyLayer) { + const SkRRect layer_rrect = SkRRect::MakeEmpty(); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRRectLayerTest, PaintBeforePreroll) { + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRRectLayerTest, CulledLayer) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + preroll_context()->cull_rect = kEmptyRect; // Cull everything + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRRectLayerTest, ChildOutsideBounds) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRRectLayerTest, FullyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index 6f355f4419ab4..d37c938784dc4 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -11,8 +11,6 @@ ColorFilterLayer::ColorFilterLayer(sk_sp filter) set_renders_to_save_layer(true); } -ColorFilterLayer::~ColorFilterLayer() = default; - void ColorFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ColorFilterLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index cf1de4cb610fc..628c5b17f6e1c 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -14,7 +14,6 @@ namespace flutter { class ColorFilterLayer : public ContainerLayer { public: ColorFilterLayer(sk_sp filter); - ~ColorFilterLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc new file mode 100644 index 0000000000000..d85dadb642a7b --- /dev/null +++ b/flow/layers/color_filter_layer_unittests.cc @@ -0,0 +1,197 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/color_filter_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/effects/SkColorMatrixFilter.h" + +namespace flutter { +namespace testing { + +using ColorFilterLayerTest = LayerTest; + +TEST_F(ColorFilterLayerTest, EmptyLayer) { + auto layer = std::make_shared(sk_sp()); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ColorFilterLayerTest, PaintBeforePreroll) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(sk_sp()); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ColorFilterLayerTest, EmptyFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(nullptr); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setColorFilter(nullptr); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ColorFilterLayerTest, SimpleFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setColorFilter(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ColorFilterLayerTest, MultipleChildren) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setColorFilter(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, + filter_paint, nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ColorFilterLayerTest, Nested) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter1 = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto layer_filter2 = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorMAGENTA, SK_ColorBLUE); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer1 = std::make_shared(layer_filter1); + auto layer2 = std::make_shared(layer_filter2); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), children_bounds); + EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint1, filter_paint2; + filter_paint1.setColorFilter(layer_filter1); + filter_paint2.setColorFilter(layer_filter2); + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, + filter_paint1, nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), + filter_paint2, nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 1fa7056c5454f..1c81262b549f7 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -9,8 +9,6 @@ namespace flutter { ContainerLayer::ContainerLayer() : child_needs_screen_readback_(false), renders_to_save_layer_(false) {} -ContainerLayer::~ContainerLayer() = default; - void ContainerLayer::Add(std::shared_ptr layer) { Layer* the_layer = layer.get(); the_layer->set_parent(this); @@ -56,6 +54,12 @@ void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(child_paint_bounds); } +void ContainerLayer::Paint(PaintContext& context) const { + FML_DCHECK(needs_painting()); + + PaintChildren(context); +} + void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index 1e37f0164cf40..431bd12b8dda5 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -13,12 +13,11 @@ namespace flutter { class ContainerLayer : public Layer { public: ContainerLayer(); - ~ContainerLayer() override; void Add(std::shared_ptr layer); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; - + void Paint(PaintContext& context) const override; #if defined(OS_FUCHSIA) void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc new file mode 100644 index 0000000000000..f4724b7c0949e --- /dev/null +++ b/flow/layers/container_layer_unittests.cc @@ -0,0 +1,200 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/container_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ContainerLayerTest = LayerTest; + +TEST_F(ContainerLayerTest, LayerWithParentHasPlatformView) { + auto layer = std::make_shared(); + + preroll_context()->has_platform_view = true; + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), + "!context->has_platform_view"); +} + +TEST_F(ContainerLayerTest, EmptyLayer) { + auto layer = std::make_shared(); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ContainerLayerTest, PaintBeforePreroll) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ContainerLayerTest, Simple) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPaint child_paint(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, child_paint}}})); +} + +TEST_F(ContainerLayerTest, Multiple) { + SkPath child_path1; + child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child_path2; + child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); + SkPaint child_paint1(SkColors::kGray); + SkPaint child_paint2(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, true /* fake_has_platform_view */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_TRUE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), + kGiantRect); // Siblings are independent + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +TEST_F(ContainerLayerTest, MultipleWithEmpty) { + SkPath child_path1; + child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPaint child_paint1(SkColors::kGray); + SkPaint child_paint2(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(SkPath(), child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); + EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_FALSE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); +} + +TEST_F(ContainerLayerTest, NeedsSystemComposite) { + SkPath child_path1; + child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child_path2; + child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); + SkPaint child_paint1(SkColors::kGray); + SkPaint child_paint2(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, false /* fake_has_platform_view */, + true /* fake_needs_system_composite */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_TRUE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index f0e37c9bed565..caa5531a79c06 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -17,8 +17,6 @@ LayerTree::LayerTree() checkerboard_raster_cache_images_(false), checkerboard_offscreen_layers_(false) {} -LayerTree::~LayerTree() = default; - void LayerTree::RecordBuildTime(fml::TimePoint start) { build_start_ = start; build_finish_ = fml::TimePoint::Now(); diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 99908740eccc3..d1a22be02c43e 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -22,8 +22,6 @@ class LayerTree { public: LayerTree(); - ~LayerTree(); - void Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false); diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc new file mode 100644 index 0000000000000..e640e92c84b89 --- /dev/null +++ b/flow/layers/layer_tree_unittests.cc @@ -0,0 +1,203 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/layer_tree.h" + +#include "flutter/flow/compositor_context.h" +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/canvas_test.h" +#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +class LayerTreeTest : public CanvasTest { + public: + void SetUp() override { + root_transform_ = SkMatrix::MakeTrans(1.0f, 1.0f); + scoped_frame_ = compositor_context_.AcquireFrame( + nullptr, &mock_canvas(), nullptr, root_transform_, false, nullptr); + } + + void TearDown() override { scoped_frame_ = nullptr; } + + LayerTree& layer_tree() { return layer_tree_; } + CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); } + const SkMatrix& root_transform() { return root_transform_; } + + private: + LayerTree layer_tree_; + CompositorContext compositor_context_; + SkMatrix root_transform_; + std::unique_ptr scoped_frame_; +}; + +TEST_F(LayerTreeTest, EmptyLayer) { + auto layer = std::make_shared(); + + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + + layer_tree().Paint(frame()); +} + +TEST_F(LayerTreeTest, PaintBeforePreroll) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child_path; + child_path.addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + layer_tree().set_root_layer(layer); + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + + layer_tree().Paint(frame()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +TEST_F(LayerTreeTest, Simple) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kCyan); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), root_transform()); + + layer_tree().Paint(frame()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, child_paint}}})); +} + +TEST_F(LayerTreeTest, Multiple) { + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path2 = SkPath().addRect(8.0f, 2.0f, 16.5f, 14.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, true /* fake_has_platform_view */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), + kGiantRect); // Siblings are independent + + layer_tree().Paint(frame()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +TEST_F(LayerTreeTest, MultipleWithEmpty) { + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(SkPath(), child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); + EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_FALSE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer_tree().Paint(frame()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); +} + +TEST_F(LayerTreeTest, NeedsSystemComposite) { + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path2 = SkPath().addRect(8.0f, 2.0f, 16.5f, 14.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, false /* fake_has_platform_view */, + true /* fake_needs_system_composite */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_TRUE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer_tree().Paint(frame()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 54f8e19d76eea..6fd5f1692d809 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -17,8 +17,6 @@ OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset) set_renders_to_save_layer(true); } -OpacityLayer::~OpacityLayer() = default; - void OpacityLayer::EnsureSingleChild() { FML_DCHECK(layers().size() > 0); // OpacityLayer should never be a leaf diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 795d8841ba6ed..f1c18c51918e7 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -26,7 +26,6 @@ class OpacityLayer : public ContainerLayer { // to many leaf layers. Therefore we try to capture that offset here to stop // the propagation as repainting the OpacityLayer is expensive. OpacityLayer(int alpha, const SkPoint& offset); - ~OpacityLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc new file mode 100644 index 0000000000000..84b2221a2e3d0 --- /dev/null +++ b/flow/layers/opacity_layer_unittests.cc @@ -0,0 +1,310 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/opacity_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using OpacityLayerTest = LayerTest; + +TEST_F(OpacityLayerTest, LeafLayer) { + auto layer = + std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); + + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), + "layers\\(\\)\\.size\\(\\) > 0"); +} + +TEST_F(OpacityLayerTest, EmptyLayer) { + auto mock_layer = std::make_shared(SkPath()); + auto layer = + std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds()); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(OpacityLayerTest, PaintBeforePreroll) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path); + auto layer = + std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(OpacityLayerTest, FullyOpaque) { + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer_transform = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif + const SkPaint child_paint = SkPaint(SkColors::kGreen); + const SkRect expected_layer_bounds = + layer_transform.mapRect(child_path.getBounds()); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(SK_AlphaOPAQUE, layer_offset); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform), Mutator(SK_AlphaOPAQUE)})); + + const SkPaint opacity_paint = SkPaint(SkColors::kBlack); // A = 1.0f + SkRect opacity_bounds; + expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) + .roundOut(&opacity_bounds); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, + 2}}, + MockCanvas::DrawCall{2, + MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(OpacityLayerTest, FullyTransparent) { + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer_transform = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif + const SkPaint child_paint = SkPaint(SkColors::kGreen); + const SkRect expected_layer_bounds = + layer_transform.mapRect(child_path.getBounds()); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = + std::make_shared(SK_AlphaTRANSPARENT, layer_offset); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ( + mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform), Mutator(SK_AlphaTRANSPARENT)})); + + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{2, + MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(OpacityLayerTest, HalfTransparent) { + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer_transform = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif + const SkPaint child_paint = SkPaint(SkColors::kGreen); + const SkRect expected_layer_bounds = + layer_transform.mapRect(child_path.getBounds()); + const SkAlpha alpha_half = 255 / 2; + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(alpha_half, layer_offset); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform), Mutator(alpha_half)})); + + const SkPaint opacity_paint = + SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha_half))); + SkRect opacity_bounds; + expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) + .roundOut(&opacity_bounds); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, + 2}}, + MockCanvas::DrawCall{2, + MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(OpacityLayerTest, Nested) { + const SkPath child1_path = SkPath().addRect(SkRect::MakeWH(5.0f, 6.0f)); + const SkPath child2_path = SkPath().addRect(SkRect::MakeWH(2.0f, 7.0f)); + const SkPath child3_path = SkPath().addRect(SkRect::MakeWH(6.0f, 6.0f)); + const SkPoint layer1_offset = SkPoint::Make(0.5f, 1.5f); + const SkPoint layer2_offset = SkPoint::Make(2.5f, 0.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer1_transform = + SkMatrix::MakeTrans(layer1_offset.fX, layer1_offset.fY); + const SkMatrix layer2_transform = + SkMatrix::MakeTrans(layer2_offset.fX, layer2_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer1_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer1_transform)); + const SkMatrix integral_layer2_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); +#endif + const SkPaint child1_paint = SkPaint(SkColors::kRed); + const SkPaint child2_paint = SkPaint(SkColors::kBlue); + const SkPaint child3_paint = SkPaint(SkColors::kGreen); + const SkAlpha alpha1 = 155; + const SkAlpha alpha2 = 224; + auto mock_layer1 = std::make_shared(child1_path, child1_paint); + auto mock_layer2 = std::make_shared(child2_path, child2_paint); + auto mock_layer3 = std::make_shared(child3_path, child3_paint); + auto layer1 = std::make_shared(alpha1, layer1_offset); + auto layer2 = std::make_shared(alpha2, layer2_offset); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + layer1->Add(mock_layer3); // Ensure something is processed after recursion + + const SkRect expected_layer2_bounds = + layer2_transform.mapRect(child2_path.getBounds()); + SkRect expected_layer1_bounds = expected_layer2_bounds; + expected_layer1_bounds.join(child1_path.getBounds()); + expected_layer1_bounds.join(child3_path.getBounds()); + expected_layer1_bounds = layer1_transform.mapRect(expected_layer1_bounds); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds()); + EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); + EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(mock_layer3->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), + SkMatrix::Concat(initial_transform, layer1_transform)); + // EXPECT_EQ(mock_layer1->parent_mutators(), + // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); + EXPECT_EQ( + mock_layer2->parent_matrix(), + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); + // EXPECT_EQ(mock_layer2->parent_mutators(), + // std::vector({Mutator(layer1_transform), Mutator(alpha1), + // Mutator(layer2_transform), Mutator(alpha2)})); + EXPECT_EQ(mock_layer3->parent_matrix(), + SkMatrix::Concat(initial_transform, layer1_transform)); + // EXPECT_EQ(mock_layer3->parent_mutators(), + // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); + + const SkPaint opacity1_paint = + SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha1))); + const SkPaint opacity2_paint = + SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha2))); + SkRect opacity1_bounds, opacity2_bounds; + expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY) + .roundOut(&opacity1_bounds); + expected_layer2_bounds.makeOffset(-layer2_offset.fX, -layer2_offset.fY) + .roundOut(&opacity2_bounds); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer1_transform}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint, + nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child1_path, child1_paint}}, + MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, + MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 3, MockCanvas::SetMatrixData{integral_layer2_transform}}, +#endif + MockCanvas::DrawCall{ + 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint, + nullptr, 4}}, + MockCanvas::DrawCall{ + 4, MockCanvas::DrawPathData{child2_path, child2_paint}}, + MockCanvas::DrawCall{4, MockCanvas::RestoreData{3}}, + MockCanvas::DrawCall{3, MockCanvas::RestoreData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child3_path, child3_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer1->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index ebb279c966866..ef7b6f2c6194c 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -8,26 +8,11 @@ #include "flutter/flow/layers/performance_overlay_layer.h" #include "third_party/skia/include/core/SkFont.h" +#include "third_party/skia/include/core/SkTextBlob.h" namespace flutter { namespace { -void DrawStatisticsText(SkCanvas& canvas, - const std::string& string, - int x, - int y, - const std::string& font_path) { - SkFont font; - if (font_path != "") { - font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); - } - font.setSize(15); - SkPaint paint; - paint.setColor(SK_ColorGRAY); - canvas.drawSimpleText(string.c_str(), string.size(), SkTextEncoding::kUTF8, x, - y, font, paint); -} - void VisualizeStopWatch(SkCanvas& canvas, const Stopwatch& stopwatch, SkScalar x, @@ -47,21 +32,39 @@ void VisualizeStopWatch(SkCanvas& canvas, } if (show_labels) { - double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); - double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); - std::stringstream stream; - stream.setf(std::ios::fixed | std::ios::showpoint); - stream << std::setprecision(1); - stream << label_prefix << " " - << "max " << max_ms_per_frame << " ms/frame, " - << "avg " << average_ms_per_frame << " ms/frame"; - DrawStatisticsText(canvas, stream.str(), x + label_x, y + height + label_y, - font_path); + auto text = PerformanceOverlayLayer::MakeStatisticsText( + stopwatch, label_prefix, font_path); + SkPaint paint; + paint.setColor(SK_ColorGRAY); + canvas.drawTextBlob(text, x + label_x, y + height + label_y, paint); } } } // namespace +sk_sp PerformanceOverlayLayer::MakeStatisticsText( + const Stopwatch& stopwatch, + const std::string& label_prefix, + const std::string& font_path) { + SkFont font; + if (font_path != "") { + font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); + } + font.setSize(15); + + double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); + double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); + std::stringstream stream; + stream.setf(std::ios::fixed | std::ios::showpoint); + stream << std::setprecision(1); + stream << label_prefix << " " + << "max " << max_ms_per_frame << " ms/frame, " + << "avg " << average_ms_per_frame << " ms/frame"; + auto text = stream.str(); + return SkTextBlob::MakeFromText(text.c_str(), text.size(), font, + SkTextEncoding::kUTF8); +} + PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options, const char* font_path) : options_(options) { diff --git a/flow/layers/performance_overlay_layer.h b/flow/layers/performance_overlay_layer.h index b5c3370d2055a..b1434a221e688 100644 --- a/flow/layers/performance_overlay_layer.h +++ b/flow/layers/performance_overlay_layer.h @@ -7,9 +7,12 @@ #include +#include "flutter/flow/instrumentation.h" #include "flutter/flow/layers/layer.h" #include "flutter/fml/macros.h" +class SkTextBlob; + namespace flutter { const int kDisplayRasterizerStatistics = 1 << 0; @@ -19,6 +22,10 @@ const int kVisualizeEngineStatistics = 1 << 3; class PerformanceOverlayLayer : public Layer { public: + static sk_sp MakeStatisticsText(const Stopwatch& stopwatch, + const std::string& label_prefix, + const std::string& font_path); + explicit PerformanceOverlayLayer(uint64_t options, const char* font_path = nullptr); diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 605717c870ee3..11d7218f2c5aa 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -2,18 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/flow_test_utils.h" #include "flutter/flow/layers/performance_overlay_layer.h" + +#include "flutter/flow/flow_test_utils.h" #include "flutter/flow/raster_cache.h" +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/build_config.h" - +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/utils/SkBase64.h" -#include "gtest/gtest.h" - +#include #include +namespace flutter { +namespace testing { +namespace { + // To get the size of kMockedTimes in compile time. template constexpr int size(const T (&array)[N]) noexcept { @@ -77,7 +88,7 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; -#if !OS_LINUX +#if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX const bool golden_data_matches = golden_data->equals(snapshot_data.get()); @@ -97,12 +108,71 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Golden file mismatch. Please check " << "the difference between " << golden_file_path << " and " << new_golden_file_path << ", and replace the former " - << "with the latter if the difference looks good.\n\n" + << "with the latter if the difference looks good.\nS\n" << "See also the base64 encoded " << new_golden_file_path << ":\n" << b64_char; } } +} // namespace + +using PerformanceOverlayLayerTest = LayerTest; + +TEST_F(PerformanceOverlayLayerTest, EmptyLayerGraph) { + const uint64_t overlay_opts = kVisualizeRasterizerStatistics; + auto layer = std::make_shared(overlay_opts); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), ""); +} + +TEST_F(PerformanceOverlayLayerTest, InvalidOptions) { + const SkRect layer_bounds = SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f); + const uint64_t overlay_opts = 0; + auto layer = std::make_shared(overlay_opts); + + // TODO(): Note calling code has to call set_paint_bounds right now. Make + // this a constructor parameter and move the set_paint_bounds into Preroll + layer->set_paint_bounds(layer_bounds); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), layer_bounds); + EXPECT_TRUE(layer->needs_painting()); + + // Nothing is drawn if options are invalid (0). + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { + const SkRect layer_bounds = SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f); + const uint64_t overlay_opts = kDisplayRasterizerStatistics; + auto layer = std::make_shared(overlay_opts); + + // TODO(): Note calling code has to call set_paint_bounds right now. Make + // this a constructor parameter and move the set_paint_bounds into Preroll + layer->set_paint_bounds(layer_bounds); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), layer_bounds); + EXPECT_TRUE(layer->needs_painting()); + + layer->Paint(paint_context()); + auto overlay_text = PerformanceOverlayLayer::MakeStatisticsText( + paint_context().raster_time, "GPU", ""); + auto overlay_text_data = overlay_text->serialize(SkSerialProcs{}); + SkPaint text_paint; + text_paint.setColor(SK_ColorGRAY); + SkPoint text_position = SkPoint::Make(16.0f, 22.0f); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawTextData{overlay_text_data, text_paint, + text_position}}})); +} + TEST(PerformanceOverlayLayerDefault, Gold) { TestPerformanceOverlayLayerGold(60); } @@ -114,3 +184,6 @@ TEST(PerformanceOverlayLayer90fps, Gold) { TEST(PerformanceOverlayLayer120fps, Gold) { TestPerformanceOverlayLayerGold(120); } + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 428e9fb8e4b2b..d138bff80f651 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -22,7 +22,9 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, : color_(color), shadow_color_(shadow_color), device_pixel_ratio_(device_pixel_ratio), +#if defined(OS_FUCHSIA) viewport_depth_(viewport_depth), +#endif elevation_(elevation), path_(path), isRect_(false), @@ -49,8 +51,6 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } -PhysicalShapeLayer::~PhysicalShapeLayer() = default; - void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); @@ -67,48 +67,11 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, // Let the system compositor draw all shadows for us. set_needs_system_composite(true); #else - // Add some margin to the paint bounds to leave space for the shadow. - // We fill this whole region and clip children to it so we don't need to - // join the child paint bounds. - // The offset is calculated as follows: - - // .--- (kLightRadius) - // -------/ (light) - // | / - // | / - // |/ - // |O - // /| (kLightHeight) - // / | - // / | - // / | - // / | - // ------------- (layer) - // /| | - // / | | (elevation) - // A / | |B - // ------------------------------------------------ (canvas) - // --- (extent of shadow) - // - // E = lt } t = (r + w/2)/h - // } => - // r + w/2 = ht } E = (l/h)(r + w/2) - // - // Where: E = extent of shadow - // l = elevation of layer - // r = radius of the light source - // w = width of the layer - // h = light height - // t = tangent of AOB, i.e., multiplier for elevation to extent - SkRect bounds(path_.getBounds()); - // tangent for x - double tx = (kLightRadius * device_pixel_ratio_ + bounds.width() * 0.5) / - kLightHeight; - // tangent for y - double ty = (kLightRadius * device_pixel_ratio_ + bounds.height() * 0.5) / - kLightHeight; - bounds.outset(elevation_ * tx, elevation_ * ty); - set_paint_bounds(bounds); + // We will draw the shadow in Paint(), so add some margin to the paint + // bounds to leave space for the shadow. We fill this whole region and clip + // children to it so we don't need to join the child paint bounds. + set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_, + device_pixel_ratio_)); #endif // defined(OS_FUCHSIA) } } @@ -193,6 +156,50 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->restoreToCount(saveCount); } +SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkRect& bounds, + float elevation, + float pixel_ratio) { + // The shadow offset is calculated as follows: + // .--- (kLightRadius) + // -------/ (light) + // | / + // | / + // |/ + // |O + // /| (kLightHeight) + // / | + // / | + // / | + // / | + // ------------- (layer) + // /| | + // / | | (elevation) + // A / | |B + // ------------------------------------------------ (canvas) + // --- (extent of shadow) + // + // E = lt } t = (r + w/2)/h + // } => + // r + w/2 = ht } E = (l/h)(r + w/2) + // + // Where: E = extent of shadow + // l = elevation of layer + // r = radius of the light source + // w = width of the layer + // h = light height + // t = tangent of AOB, i.e., multiplier for elevation to extent + // tangent for x + double tx = + (kLightRadius * pixel_ratio + bounds.width() * 0.5) / kLightHeight; + // tangent for y + double ty = + (kLightRadius * pixel_ratio + bounds.height() * 0.5) / kLightHeight; + SkRect shadow_bounds(bounds); + shadow_bounds.outset(elevation * tx, elevation * ty); + + return shadow_bounds; +} + void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index f884fe02fc5bd..1b5564c3662b3 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -18,8 +18,10 @@ class PhysicalShapeLayer : public ContainerLayer { float elevation, const SkPath& path, Clip clip_behavior); - ~PhysicalShapeLayer() override; + static SkRect ComputeShadowBounds(const SkRect& bounds, + float elevation, + float pixel_ratio); static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, @@ -35,19 +37,21 @@ class PhysicalShapeLayer : public ContainerLayer { void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) + float total_elevation() const { return total_elevation_; } + private: SkColor color_; SkColor shadow_color_; SkScalar device_pixel_ratio_; - float viewport_depth_; +#if defined(OS_FUCHSIA) + float viewport_depth_ = 0.0f; +#endif float elevation_ = 0.0f; float total_elevation_ = 0.0f; SkPath path_; bool isRect_; SkRRect frameRRect_; Clip clip_behavior_; - - friend class PhysicalShapeLayer_TotalElevation_Test; }; } // namespace flutter diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index 972424a2fec6d..f0e22593122c9 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -4,65 +4,243 @@ #include "flutter/flow/layers/physical_shape_layer.h" -#include "gtest/gtest.h" +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" namespace flutter { +namespace testing { -TEST(PhysicalShapeLayer, TotalElevation) { - std::shared_ptr layers[4]; +using PhysicalShapeLayerTest = LayerTest; - SkColor dummy_color = 0; - SkPath dummy_path; - for (int i = 0; i < 4; i += 1) { - layers[i] = - std::make_shared(dummy_color, dummy_color, - 1.0f, // pixel ratio, - 1.0f, // depth - (float)(i + 1), // elevation - dummy_path, Clip::none); - } +TEST_F(PhysicalShapeLayerTest, EmptyLayer) { + auto layer = + std::make_shared(SK_ColorBLACK, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + SkPath(), Clip::none); - layers[0]->Add(layers[1]); - layers[0]->Add(layers[2]); - layers[2]->Add(layers[3]); + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(PhysicalShapeLayerTest, PaintBeforePreroll) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = + std::make_shared(SK_ColorBLACK, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + SkPath(), Clip::none); + layer->Add(mock_layer); - const Stopwatch unused_stopwatch; - TextureRegistry unused_texture_registry; - MutatorsStack unused_stack; - PrerollContext preroll_context{ - nullptr, // raster_cache (don't consult the cache) - nullptr, // gr_context (used for the raster cache) - nullptr, // external view embedder - unused_stack, // mutator stack - nullptr, // SkColorSpace* dst_color_space - kGiantRect, // SkRect cull_rect - unused_stopwatch, // frame time (dont care) - unused_stopwatch, // engine time (dont care) - unused_texture_registry, // texture registry (not supported) - false, // checkerboard_offscreen_layers - 0.0f, // total elevation - }; - - SkMatrix identity; - identity.setIdentity(); - - layers[0]->Preroll(&preroll_context, identity); - - // It should look like this: - // layers[0] +1.0f + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { + SkPath layer_path; + layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto layer = + std::make_shared(SK_ColorGREEN, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + layer_path, Clip::none); + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); +} + +TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) { + SkPath layer_path; + layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child1_path; + child1_path.addRect(4, 0, 12, 12).close(); + SkPath child2_path; + child2_path.addRect(3, 2, 5, 15).close(); + auto child1 = std::make_shared(SK_ColorRED, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + child1_path, Clip::none); + auto child2 = + std::make_shared(SK_ColorBLUE, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + child2_path, Clip::none); + auto layer = + std::make_shared(SK_ColorGREEN, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + layer_path, Clip::none); + layer->Add(child1); + layer->Add(child2); + + SkRect child_paint_bounds; + layer->Preroll(preroll_context(), SkMatrix()); + child_paint_bounds.join(child1->paint_bounds()); + child_paint_bounds.join(child2->paint_bounds()); + EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); + EXPECT_NE(layer->paint_bounds(), child_paint_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + SkPaint child1_paint; + child1_paint.setColor(SK_ColorRED); + child1_paint.setAntiAlias(true); + SkPaint child2_paint; + child2_paint.setColor(SK_ColorBLUE); + child2_paint.setAntiAlias(true); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child1_path, child1_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child2_path, child2_paint}}})); +} + +TEST_F(PhysicalShapeLayerTest, ElevationSimple) { + constexpr float initial_elevation = 20.0f; + SkPath layer_path; + layer_path.addRect(0, 0, 8, 8).close(); + auto layer = std::make_shared( + SK_ColorGREEN, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + initial_elevation, layer_path, Clip::none); + + layer->Preroll(preroll_context(), SkMatrix()); + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not do any painting there. +#if defined(OS_FUCHSIA) + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_system_composite()); +#else + EXPECT_EQ(layer->paint_bounds(), + PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), + initial_elevation, 1.0f)); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); +#endif + EXPECT_EQ(layer->total_elevation(), initial_elevation); + + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not use the direct |Paint()| path there. +#if !defined(OS_FUCHSIA) + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); +#endif +} + +TEST_F(PhysicalShapeLayerTest, ElevationComplex) { + // The layer tree should look like this: + // layers[0] +1.0f = 1.0f // | \ // | \ // | \ - // | layers[2] +3.0f + // | layers[2] +3.0f = 4.0f // | | - // | layers[3] +4.0f + // | layers[3] +4.0f = 8.0f // | // | - // layers[1] + 2.0f - EXPECT_EQ(layers[0]->total_elevation_, 1.0f); - EXPECT_EQ(layers[1]->total_elevation_, 3.0f); - EXPECT_EQ(layers[2]->total_elevation_, 4.0f); - EXPECT_EQ(layers[3]->total_elevation_, 8.0f); + // layers[1] + 2.0f = 3.0f + constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f}; + constexpr float total_elevations[4] = {1.0f, 3.0f, 4.0f, 8.0f}; + SkPath layer_path; + layer_path.addRect(0, 0, 80, 80).close(); + + std::shared_ptr layers[4]; + for (int i = 0; i < 4; i += 1) { + layers[i] = std::make_shared( + SK_ColorBLACK, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + initial_elevations[i], layer_path, Clip::none); + } + layers[0]->Add(layers[1]); + layers[0]->Add(layers[2]); + layers[2]->Add(layers[3]); + + layers[0]->Preroll(preroll_context(), SkMatrix()); + for (int i = 0; i < 4; i += 1) { + // On Fuchsia, the system compositor handles all elevated + // PhysicalShapeLayers and their shadows , so we do not do any painting + // there. +#if defined(OS_FUCHSIA) + EXPECT_EQ(layers[i]->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layers[i]->needs_painting()); + EXPECT_TRUE(layers[i]->needs_system_composite()); +#else + EXPECT_EQ(layers[i]->paint_bounds(), + (PhysicalShapeLayer::ComputeShadowBounds( + layer_path.getBounds(), initial_elevations[i], + 1.0f /* pixel_ratio */))); + EXPECT_TRUE(layers[i]->needs_painting()); + EXPECT_FALSE(layers[i]->needs_system_composite()); +#endif + EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]); + } + + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not use the direct |Paint()| path there. +#if !defined(OS_FUCHSIA) + SkPaint layer_paint; + layer_paint.setColor(SK_ColorBLACK); + layer_paint.setAntiAlias(true); + layers[0]->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); +#endif } +} // namespace testing } // namespace flutter diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index c4275e76c13cf..230b648f50e80 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -17,8 +17,6 @@ PictureLayer::PictureLayer(const SkPoint& offset, is_complex_(is_complex), will_change_(will_change) {} -PictureLayer::~PictureLayer() = default; - void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkPicture* sk_picture = picture(); diff --git a/flow/layers/picture_layer.h b/flow/layers/picture_layer.h index 9c40cbef37cbd..e733e7455ca6c 100644 --- a/flow/layers/picture_layer.h +++ b/flow/layers/picture_layer.h @@ -19,7 +19,6 @@ class PictureLayer : public Layer { SkiaGPUObject picture, bool is_complex, bool will_change); - ~PictureLayer() override; SkPicture* picture() const { return picture_.get().get(); } diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc new file mode 100644 index 0000000000000..7c88845c61be7 --- /dev/null +++ b/flow/layers/picture_layer_unittests.cc @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/flow/layers/picture_layer.h" + +#include "flutter/flow/testing/skia_gpu_object_layer_test.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkPicture.h" + +#ifndef SUPPORT_FRACTIONAL_TRANSLATION +#include "flutter/flow/raster_cache.h" +#endif + +namespace flutter { +namespace testing { + +using PictureLayerTest = SkiaGPUObjectLayerTest; + +TEST_F(PictureLayerTest, InvalidPicture) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(), false, false); + + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); +} + +TEST_F(PictureLayerTest, PaintBeforePrerollInvalidPicture) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(), false, false); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "picture_\\.get\\(\\)"); +} + +TEST_F(PictureLayerTest, PaintBeforePreroll) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); + + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(PictureLayerTest, EmptyLayer) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkRect picture_bounds = SkRect::MakeEmpty(); + auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(PictureLayerTest, SimplePicture) { + const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); + const SkMatrix layer_offset_matrix = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), + picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_EQ(layer->picture(), mock_picture.get()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + layer->Paint(paint_context()); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, + MockCanvas::ConcatMatrixData{layer_offset_matrix}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( + layer_offset_matrix)}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), + SkMatrix()}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 3f72993f97d66..81541b7a0cde8 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -11,8 +11,6 @@ PlatformViewLayer::PlatformViewLayer(const SkPoint& offset, int64_t view_id) : offset_(offset), size_(size), view_id_(view_id) {} -PlatformViewLayer::~PlatformViewLayer() = default; - void PlatformViewLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), diff --git a/flow/layers/platform_view_layer.h b/flow/layers/platform_view_layer.h index 7ce7ccb58a856..242b3734dd3b1 100644 --- a/flow/layers/platform_view_layer.h +++ b/flow/layers/platform_view_layer.h @@ -14,7 +14,6 @@ namespace flutter { class PlatformViewLayer : public Layer { public: PlatformViewLayer(const SkPoint& offset, const SkSize& size, int64_t view_id); - ~PlatformViewLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/platform_view_layer_unittests.cc b/flow/layers/platform_view_layer_unittests.cc new file mode 100644 index 0000000000000..123f9ab9925f6 --- /dev/null +++ b/flow/layers/platform_view_layer_unittests.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/platform_view_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using PlatformViewLayerTest = LayerTest; + +TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(8.0f, 8.0f); + const int64_t view_id = 0; + auto layer = + std::make_shared(layer_offset, layer_size, view_id); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(layer->paint_bounds(), + SkRect::MakeSize(layer_size) + .makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + layer->Paint(paint_context()); + EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index 064e0e1b2ad8b..a1590ec8c9e9a 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -13,8 +13,6 @@ ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, set_renders_to_save_layer(true); } -ShaderMaskLayer::~ShaderMaskLayer() = default; - void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index 01836f4f2fb54..7f633c0372d45 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -16,7 +16,6 @@ class ShaderMaskLayer : public ContainerLayer { ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode); - ~ShaderMaskLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc new file mode 100644 index 0000000000000..4194747863ff4 --- /dev/null +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -0,0 +1,255 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/shader_mask_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkShader.h" +#include "third_party/skia/include/effects/SkPerlinNoiseShader.h" + +namespace flutter { +namespace testing { + +using ShaderMaskLayerTest = LayerTest; + +TEST_F(ShaderMaskLayerTest, EmptyLayer) { + auto layer = + std::make_shared(nullptr, kEmptyRect, SkBlendMode::kSrc); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ShaderMaskLayerTest, PaintBeforePreroll) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = + std::make_shared(nullptr, kEmptyRect, SkBlendMode::kSrc); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ShaderMaskLayerTest, EmptyFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(nullptr, layer_bounds, + SkBlendMode::kSrc); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setBlendMode(SkBlendMode::kSrc); + filter_paint.setShader(nullptr); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH( + layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ShaderMaskLayerTest, SimpleFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_filter, layer_bounds, + SkBlendMode::kSrc); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setBlendMode(SkBlendMode::kSrc); + filter_paint.setShader(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH( + layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ShaderMaskLayerTest, MultipleChildren) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(layer_filter, layer_bounds, + SkBlendMode::kSrc); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setBlendMode(SkBlendMode::kSrc); + filter_paint.setShader(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH( + layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ShaderMaskLayerTest, Nested) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 7.5f, 8.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter1 = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto layer_filter2 = + SkPerlinNoiseShader::MakeImprovedNoise(2.0f, 2.0f, 2, 2.0f); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer1 = std::make_shared(layer_filter1, layer_bounds, + SkBlendMode::kSrc); + auto layer2 = std::make_shared(layer_filter2, layer_bounds, + SkBlendMode::kSrc); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), children_bounds); + EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint1, filter_paint2; + filter_paint1.setBlendMode(SkBlendMode::kSrc); + filter_paint2.setBlendMode(SkBlendMode::kSrc); + filter_paint1.setShader(layer_filter1); + filter_paint2.setShader(layer_filter2); + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, + 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(), + nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 2, + MockCanvas::DrawRectData{ + SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), + filter_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, + MockCanvas::DrawRectData{ + SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), + filter_paint1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index c7716dd59bc29..848f69c8a115a 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -14,8 +14,6 @@ TextureLayer::TextureLayer(const SkPoint& offset, bool freeze) : offset_(offset), size_(size), texture_id_(texture_id), freeze_(freeze) {} -TextureLayer::~TextureLayer() = default; - void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); diff --git a/flow/layers/texture_layer.h b/flow/layers/texture_layer.h index 7c04471afa0c1..20f6c709d6107 100644 --- a/flow/layers/texture_layer.h +++ b/flow/layers/texture_layer.h @@ -17,7 +17,6 @@ class TextureLayer : public Layer { const SkSize& size, int64_t texture_id, bool freeze); - ~TextureLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/texture_layer_unittests.cc b/flow/layers/texture_layer_unittests.cc new file mode 100644 index 0000000000000..d8a1a2bee8aa5 --- /dev/null +++ b/flow/layers/texture_layer_unittests.cc @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/texture_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/flow/testing/mock_texture.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using TextureLayerTest = LayerTest; + +TEST_F(TextureLayerTest, InvalidTexture) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(8.0f, 8.0f); + auto layer = + std::make_shared(layer_offset, layer_size, 0, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), + (SkRect::MakeSize(layer_size) + .makeOffset(layer_offset.fX, layer_offset.fY))); + EXPECT_TRUE(layer->needs_painting()); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +TEST_F(TextureLayerTest, EmptyLayer) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(0.0f, 0.0f); + const int64_t texture_id = 0; + auto mock_texture = std::make_shared(texture_id); + auto layer = std::make_shared(layer_offset, layer_size, + texture_id, false); + + // Ensure the texture is located by the Layer. + preroll_context()->texture_registry.RegisterTexture(mock_texture); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_texture->paint_calls(), + std::vector({MockTexture::PaintCall{ + mock_canvas(), layer->paint_bounds(), false, nullptr}})); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 5a7af132c68f2..9513e8bc0fec6 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -24,8 +24,6 @@ TransformLayer::TransformLayer(const SkMatrix& transform) } } -TransformLayer::~TransformLayer() = default; - void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix child_matrix; child_matrix.setConcat(matrix, transform_); diff --git a/flow/layers/transform_layer.h b/flow/layers/transform_layer.h index f19a963ced9fe..a21e7d4f10c5b 100644 --- a/flow/layers/transform_layer.h +++ b/flow/layers/transform_layer.h @@ -14,7 +14,6 @@ namespace flutter { class TransformLayer : public ContainerLayer { public: TransformLayer(const SkMatrix& transform); - ~TransformLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/transform_layer_unittests.cc b/flow/layers/transform_layer_unittests.cc new file mode 100644 index 0000000000000..8d196db279c4b --- /dev/null +++ b/flow/layers/transform_layer_unittests.cc @@ -0,0 +1,226 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/transform_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using TransformLayerTest = LayerTest; + +TEST_F(TransformLayerTest, EmptyLayer) { + auto layer = std::make_shared(SkMatrix()); // identity + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(TransformLayerTest, PaintBeforePreroll) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = std::make_shared(SkMatrix()); // identity + layer->Add(mock_layer); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(TransformLayerTest, Identity) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = std::make_shared(SkMatrix()); // identity + layer->Add(mock_layer); + + preroll_context()->cull_rect = cull_rect; + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity + EXPECT_EQ(mock_layer->parent_cull_rect(), cull_rect); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(SkMatrix())})); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, SkPaint()}}})); +} + +TEST_F(TransformLayerTest, Simple) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + SkMatrix layer_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix inverse_layer_transform; + EXPECT_TRUE(layer_transform.invert(&inverse_layer_transform)); + + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = std::make_shared(layer_transform); + layer->Add(mock_layer); + + preroll_context()->cull_rect = cull_rect; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), + layer_transform.mapRect(mock_layer->paint_bounds())); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ(mock_layer->parent_cull_rect(), + inverse_layer_transform.mapRect(cull_rect)); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, SkPaint()}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(TransformLayerTest, Nested) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + SkMatrix layer1_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix layer2_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix inverse_layer1_transform, inverse_layer2_transform; + EXPECT_TRUE(layer1_transform.invert(&inverse_layer1_transform)); + EXPECT_TRUE(layer2_transform.invert(&inverse_layer2_transform)); + + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer1 = std::make_shared(layer1_transform); + auto layer2 = std::make_shared(layer2_transform); + layer1->Add(layer2); + layer2->Add(mock_layer); + + preroll_context()->cull_rect = cull_rect; + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer2->paint_bounds(), + layer2_transform.mapRect(mock_layer->paint_bounds())); + EXPECT_EQ(layer1->paint_bounds(), + layer1_transform.mapRect(layer2->paint_bounds())); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_EQ( + mock_layer->parent_matrix(), + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); + EXPECT_EQ(mock_layer->parent_cull_rect(), + inverse_layer2_transform.mapRect( + inverse_layer1_transform.mapRect(cull_rect))); + EXPECT_EQ( + mock_layer->parent_mutators(), + std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); + + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer1_transform}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{layer2_transform}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, SkPaint()}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(TransformLayerTest, NestedSeparated) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + SkMatrix layer1_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix layer2_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix inverse_layer1_transform, inverse_layer2_transform; + EXPECT_TRUE(layer1_transform.invert(&inverse_layer1_transform)); + EXPECT_TRUE(layer2_transform.invert(&inverse_layer2_transform)); + + auto mock_layer1 = + std::make_shared(child_path, SkPaint(SkColors::kBlue)); + auto mock_layer2 = + std::make_shared(child_path, SkPaint(SkColors::kGreen)); + auto layer1 = std::make_shared(layer1_transform); + auto layer2 = std::make_shared(layer2_transform); + layer1->Add(mock_layer1); + layer1->Add(layer2); + layer2->Add(mock_layer2); + + preroll_context()->cull_rect = cull_rect; + layer1->Preroll(preroll_context(), initial_transform); + SkRect expected_layer1_bounds = layer2->paint_bounds(); + expected_layer1_bounds.join(mock_layer1->paint_bounds()); + layer1_transform.mapRect(&expected_layer1_bounds); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer2->paint_bounds(), + layer2_transform.mapRect(mock_layer2->paint_bounds())); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), + SkMatrix::Concat(initial_transform, layer1_transform)); + EXPECT_EQ( + mock_layer2->parent_matrix(), + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); + EXPECT_EQ(mock_layer1->parent_cull_rect(), + inverse_layer1_transform.mapRect(cull_rect)); + EXPECT_EQ(mock_layer2->parent_cull_rect(), + inverse_layer2_transform.mapRect( + inverse_layer1_transform.mapRect(cull_rect))); + EXPECT_EQ(mock_layer1->parent_mutators(), + std::vector({Mutator(layer1_transform)})); + EXPECT_EQ( + mock_layer2->parent_mutators(), + std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); + + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer1_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, + SkPaint(SkColors::kBlue)}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{layer2_transform}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, + SkPaint(SkColors::kGreen)}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/matrix_decomposition_unittests.cc b/flow/matrix_decomposition_unittests.cc index cf96025276737..8aa511e4a0a97 100644 --- a/flow/matrix_decomposition_unittests.cc +++ b/flow/matrix_decomposition_unittests.cc @@ -12,6 +12,9 @@ #include "flutter/flow/matrix_decomposition.h" #include "gtest/gtest.h" +namespace flutter { +namespace testing { + TEST(MatrixDecomposition, Rotation) { SkMatrix44 matrix = SkMatrix44::I(); @@ -93,7 +96,8 @@ TEST(MatrixDecomposition, Combination) { } TEST(MatrixDecomposition, ScaleFloatError) { - for (float scale = 0.0001f; scale < 2.0f; scale += 0.000001f) { + constexpr float scale_increment = 0.00001f; + for (float scale = 0.0001f; scale < 2.0f; scale += scale_increment) { SkMatrix44 matrix = SkMatrix44::I(); matrix.setScale(scale, scale, 1.0f); @@ -152,3 +156,6 @@ TEST(MatrixDecomposition, ScaleFloatError) { ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[1]); ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[2]); } + +} // namespace testing +} // namespace flutter diff --git a/flow/mutators_stack_unittests.cc b/flow/mutators_stack_unittests.cc index 97cfe9a54a7c7..1d31a81623307 100644 --- a/flow/mutators_stack_unittests.cc +++ b/flow/mutators_stack_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/flow/embedded_views.h" + #include "gtest/gtest.h" namespace flutter { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 64f4405ebe5a0..bd83d807875f2 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -3,10 +3,15 @@ // found in the LICENSE file. #include "flutter/flow/raster_cache.h" + #include "gtest/gtest.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +namespace flutter { +namespace testing { +namespace { + sk_sp GetSamplePicture() { SkPictureRecorder recorder; recorder.beginRecording(SkRect::MakeWH(150, 100)); @@ -17,6 +22,8 @@ sk_sp GetSamplePicture() { return recorder.finishRecordingAsPicture(); } +} // namespace + TEST(RasterCache, SimpleInitialization) { flutter::RasterCache cache; ASSERT_TRUE(true); @@ -93,3 +100,6 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) { ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true, false)); // 5 } + +} // namespace testing +} // namespace flutter diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 4823ec14208c5..2a09f982a4386 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -54,14 +54,11 @@ class SkiaGPUObject { using SkiaObjectType = T; SkiaGPUObject() = default; - SkiaGPUObject(sk_sp object, fml::RefPtr queue) : object_(std::move(object)), queue_(std::move(queue)) { FML_DCHECK(object_); } - SkiaGPUObject(SkiaGPUObject&&) = default; - ~SkiaGPUObject() { reset(); } SkiaGPUObject& operator=(SkiaGPUObject&&) = default; diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index aa259a6909eec..9df82ad11c312 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/flow/skia_gpu_object.h" + #include "flutter/fml/message_loop.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" @@ -13,8 +14,6 @@ namespace flutter { namespace testing { -using SkiaGpuObjectTest = flutter::testing::ThreadTest; - class TestSkObject : public SkRefCnt { public: TestSkObject(std::shared_ptr latch, @@ -22,7 +21,9 @@ class TestSkObject : public SkRefCnt { : latch_(latch), dtor_task_queue_id_(dtor_task_queue_id) {} ~TestSkObject() { - *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); + if (dtor_task_queue_id_) { + *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); + } latch_->Signal(); } @@ -31,19 +32,107 @@ class TestSkObject : public SkRefCnt { fml::TaskQueueId* dtor_task_queue_id_; }; -TEST_F(SkiaGpuObjectTest, UnrefQueue) { - fml::RefPtr task_runner = CreateNewThread(); - fml::RefPtr queue = fml::MakeRefCounted( - task_runner, fml::TimeDelta::FromSeconds(0)); +class SkiaGpuObjectTest : public ThreadTest { + public: + SkiaGpuObjectTest() + : unref_task_runner_(CreateNewThread()), + unref_queue_(fml::MakeRefCounted( + unref_task_runner(), + fml::TimeDelta::FromSeconds(0))), + delayed_unref_queue_(fml::MakeRefCounted( + unref_task_runner(), + fml::TimeDelta::FromSeconds(3))) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + } + + fml::RefPtr unref_task_runner() { + return unref_task_runner_; + } + fml::RefPtr unref_queue() { return unref_queue_; } + fml::RefPtr delayed_unref_queue() { + return delayed_unref_queue_; + } + + private: + fml::RefPtr unref_task_runner_; + fml::RefPtr unref_queue_; + fml::RefPtr delayed_unref_queue_; +}; +TEST_F(SkiaGpuObjectTest, QueueSimple) { std::shared_ptr latch = std::make_shared(); fml::TaskQueueId dtor_task_queue_id(0); SkRefCnt* ref_object = new TestSkObject(latch, &dtor_task_queue_id); - queue->Unref(ref_object); + unref_queue()->Unref(ref_object); + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectDestructor) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + + { + auto object = sk_make_sp(latch, &dtor_task_queue_id); + SkiaGPUObject sk_object(object, unref_queue()); + ASSERT_EQ(sk_object.get(), object); + ASSERT_EQ(dtor_task_queue_id, 0); + } + + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectReset) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + SkiaGPUObject sk_object( + sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); + + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectResetBeforeDestructor) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + + { + auto object = sk_make_sp(latch, &dtor_task_queue_id); + SkiaGPUObject sk_object(object, unref_queue()); + ASSERT_EQ(sk_object.get(), object); + ASSERT_EQ(dtor_task_queue_id, 0); + + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + } + + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectResetTwice) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + SkiaGPUObject sk_object( + sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); + + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, task_runner->GetTaskQueueId()); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); } } // namespace testing diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h new file mode 100644 index 0000000000000..e38d690a2eeb7 --- /dev/null +++ b/flow/testing/layer_test.h @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLOW_TESTING_LAYER_TEST_H_ +#define FLOW_TESTING_LAYER_TEST_H_ + +#include "flutter/flow/layers/layer.h" + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/testing/canvas_test.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" + +namespace flutter { +namespace testing { + +// This fixture allows generating tests which can |Paint()| and |Preroll()| +// |Layer|'s. +// |LayerTest| is a default implementation based on |::testing::Test|. +// +// |BaseT| should be the base test type, such as |::testing::Test| below. +template +class LayerTestBase : public CanvasTestBase { + using TestT = CanvasTestBase; + + public: + LayerTestBase() + : preroll_context_({ + nullptr, /* raster_cache */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ + mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), + kGiantRect, /* cull_rect */ + raster_time_, ui_time_, texture_registry_, + false, /* checkerboard_offscreen_layers */ + 0.0f /* total_elevation */ + }), + paint_context_({ + TestT::mock_canvas().internal_canvas(), /* internal_nodes_canvas */ + &TestT::mock_canvas(), /* leaf_nodes_canvas */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ + raster_time_, ui_time_, texture_registry_, + nullptr, /* raster_cache */ + false, /* checkerboard_offscreen_layers */ + }) {} + + TextureRegistry& texture_regitry() { return texture_registry_; } + PrerollContext* preroll_context() { return &preroll_context_; } + Layer::PaintContext& paint_context() { return paint_context_; } + + private: + Stopwatch raster_time_; + Stopwatch ui_time_; + MutatorsStack mutators_stack_; + TextureRegistry texture_registry_; + + PrerollContext preroll_context_; + Layer::PaintContext paint_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(LayerTestBase); +}; +using LayerTest = LayerTestBase<::testing::Test>; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_LAYER_TEST_H_ diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc new file mode 100644 index 0000000000000..1065f43054674 --- /dev/null +++ b/flow/testing/mock_layer.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_layer.h" + +namespace flutter { +namespace testing { + +MockLayer::MockLayer(SkPath path, + SkPaint paint, + bool fake_has_platform_view, + bool fake_needs_system_composite) + : fake_paint_path_(path), + fake_paint_(paint), + fake_has_platform_view_(fake_has_platform_view), + fake_needs_system_composite_(fake_needs_system_composite) {} + +void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + parent_mutators_ = context->mutators_stack; + parent_matrix_ = matrix; + parent_cull_rect_ = context->cull_rect; + parent_elevation_ = context->total_elevation; + parent_has_platform_view_ = context->has_platform_view; + + context->has_platform_view = fake_has_platform_view_; + set_paint_bounds(fake_paint_path_.getBounds()); + set_needs_system_composite(fake_needs_system_composite_); +} + +void MockLayer::Paint(PaintContext& context) const { + FML_DCHECK(needs_painting()); + + context.leaf_nodes_canvas->drawPath(fake_paint_path_, fake_paint_); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h new file mode 100644 index 0000000000000..b55452a37812c --- /dev/null +++ b/flow/testing/mock_layer.h @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLOW_TESTING_MOCK_LAYER_H_ +#define FLOW_TESTING_MOCK_LAYER_H_ + +#include "flutter/flow/layers/layer.h" + +namespace flutter { +namespace testing { + +// Mock implementation of the |Layer| interface that does nothing but paint +// the specified |path| into the canvas. It records the |PrerollContext| and +// |PaintContext| data passed in by its parent |Layer|, so the test can later +// verify the data against expected values. +class MockLayer : public Layer { + public: + MockLayer(SkPath path, + SkPaint paint = SkPaint(), + bool fake_has_platform_view = false, + bool fake_needs_system_composite = false); + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Paint(PaintContext& context) const override; + + const MutatorsStack& parent_mutators() { return parent_mutators_; } + const SkMatrix& parent_matrix() { return parent_matrix_; } + const SkRect& parent_cull_rect() { return parent_cull_rect_; } + float parent_elevation() { return parent_elevation_; } + bool parent_has_platform_view() { return parent_has_platform_view_; } + + private: + MutatorsStack parent_mutators_; + SkMatrix parent_matrix_; + SkRect parent_cull_rect_ = SkRect::MakeEmpty(); + SkPath fake_paint_path_; + SkPaint fake_paint_; + float parent_elevation_ = 0; + bool parent_has_platform_view_ = false; + bool fake_has_platform_view_ = false; + bool fake_needs_system_composite_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(MockLayer); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_MOCK_LAYER_H_ diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc new file mode 100644 index 0000000000000..ab2518edec108 --- /dev/null +++ b/flow/testing/mock_layer_unittests.cc @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using MockLayerTest = LayerTest; + +TEST_F(MockLayerTest, PaintBeforePreroll) { + SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto layer = std::make_shared(path, SkPaint()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(MockLayerTest, EmptyLayer) { + auto layer = std::make_shared(SkPath(), SkPaint()); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkPath().getBounds()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(MockLayerTest, SimpleParams) { + const SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPaint paint = SkPaint(SkColors::kBlue); + const SkMatrix start_matrix = SkMatrix::MakeTrans(1.0f, 2.0f); + const SkMatrix scale_matrix = SkMatrix::MakeScale(0.5f, 0.5f); + const SkRect cull_rect = SkRect::MakeWH(5.0f, 5.0f); + const float parent_elevation = 5.0f; + const bool parent_has_platform_view = true; + auto layer = std::make_shared(path, paint); + + preroll_context()->mutators_stack.PushTransform(scale_matrix); + preroll_context()->cull_rect = cull_rect; + preroll_context()->total_elevation = parent_elevation; + preroll_context()->has_platform_view = parent_has_platform_view; + layer->Preroll(preroll_context(), start_matrix); + EXPECT_EQ(preroll_context()->has_platform_view, false); + EXPECT_EQ(layer->paint_bounds(), path.getBounds()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(layer->parent_mutators(), std::vector{Mutator(scale_matrix)}); + EXPECT_EQ(layer->parent_matrix(), start_matrix); + EXPECT_EQ(layer->parent_cull_rect(), cull_rect); + EXPECT_EQ(layer->parent_elevation(), parent_elevation); + EXPECT_EQ(layer->parent_has_platform_view(), parent_has_platform_view); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{path, paint}}})); +} + +TEST_F(MockLayerTest, FakePlatformView) { + auto layer = std::make_shared(SkPath(), SkPaint(), + true /* fake_has_platform_view */); + EXPECT_EQ(preroll_context()->has_platform_view, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->has_platform_view, true); +} + +TEST_F(MockLayerTest, FakeSystemComposite) { + auto layer = std::make_shared( + SkPath(), SkPaint(), false /* fake_has_platform_view */, + true /* fake_needs_system_composite */); + EXPECT_EQ(layer->needs_system_composite(), false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->needs_system_composite(), true); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_texture.cc b/flow/testing/mock_texture.cc new file mode 100644 index 0000000000000..26e49b764cdaf --- /dev/null +++ b/flow/testing/mock_texture.cc @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_texture.h" + +namespace flutter { +namespace testing { + +MockTexture::MockTexture(int64_t textureId) : Texture(textureId) {} + +void MockTexture::Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) { + paint_calls_.emplace_back(PaintCall{canvas, bounds, freeze, context}); +} + +bool operator==(const MockTexture::PaintCall& a, + const MockTexture::PaintCall& b) { + return &a.canvas == &b.canvas && a.bounds == b.bounds && + a.context == b.context && a.freeze == b.freeze; +} + +std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) { + return os << &data.canvas << " " << data.bounds << " " << data.context << " " + << data.freeze; +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_texture.h b/flow/testing/mock_texture.h new file mode 100644 index 0000000000000..c5339ebb77a66 --- /dev/null +++ b/flow/testing/mock_texture.h @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/texture.h" +#include "flutter/testing/assertions_skia.h" + +#include +#include + +namespace flutter { +namespace testing { + +// Mock implementation of the |Texture| interface that does not interact with +// the GPU. It simply records the list of various calls made so the test can +// later verify them against expected data. +class MockTexture : public Texture { + public: + struct PaintCall { + SkCanvas& canvas; + SkRect bounds; + bool freeze; + GrContext* context; + }; + + explicit MockTexture(int64_t textureId); + + // Called from GPU thread. + void Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) override; + + void OnGrContextCreated() override { gr_context_created_ = true; } + void OnGrContextDestroyed() override { gr_context_destroyed_ = true; } + void MarkNewFrameAvailable() override {} + void OnTextureUnregistered() override { unregistered_ = true; } + + const std::vector& paint_calls() { return paint_calls_; } + bool gr_context_created() { return gr_context_created_; } + bool gr_context_destroyed() { return gr_context_destroyed_; } + bool unregistered() { return unregistered_; } + + private: + std::vector paint_calls_; + bool gr_context_created_ = false; + bool gr_context_destroyed_ = false; + bool unregistered_ = false; +}; + +extern bool operator==(const MockTexture::PaintCall& a, + const MockTexture::PaintCall& b); +extern std::ostream& operator<<(std::ostream& os, + const MockTexture::PaintCall& data); + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_texture_unittests.cc b/flow/testing/mock_texture_unittests.cc new file mode 100644 index 0000000000000..107eb76307928 --- /dev/null +++ b/flow/testing/mock_texture_unittests.cc @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_texture.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(MockTextureTest, Callbacks) { + auto texture = std::make_shared(0); + + ASSERT_FALSE(texture->gr_context_created()); + texture->OnGrContextCreated(); + ASSERT_TRUE(texture->gr_context_created()); + + ASSERT_FALSE(texture->gr_context_destroyed()); + texture->OnGrContextDestroyed(); + ASSERT_TRUE(texture->gr_context_destroyed()); + + ASSERT_FALSE(texture->unregistered()); + texture->OnTextureUnregistered(); + ASSERT_TRUE(texture->unregistered()); +} + +TEST(MockTextureTest, PaintCalls) { + SkCanvas canvas; + const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); + const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); + const auto expected_paint_calls = + std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr}, + MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr}}; + auto texture = std::make_shared(0); + + texture->Paint(canvas, paint_bounds1, false, nullptr); + texture->Paint(canvas, paint_bounds2, true, nullptr); + EXPECT_EQ(texture->paint_calls(), expected_paint_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/skia_gpu_object_layer_test.cc b/flow/testing/skia_gpu_object_layer_test.cc new file mode 100644 index 0000000000000..1fca8ec8f3b81 --- /dev/null +++ b/flow/testing/skia_gpu_object_layer_test.cc @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/skia_gpu_object_layer_test.h" + +#include "flutter/fml/time/time_delta.h" + +namespace flutter { +namespace testing { + +SkiaGPUObjectLayerTest::SkiaGPUObjectLayerTest() + : unref_queue_(fml::MakeRefCounted( + GetCurrentTaskRunner(), + fml::TimeDelta::FromSeconds(0))) {} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/skia_gpu_object_layer_test.h b/flow/testing/skia_gpu_object_layer_test.h new file mode 100644 index 0000000000000..d573ac0b41007 --- /dev/null +++ b/flow/testing/skia_gpu_object_layer_test.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ +#define FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ + +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/flow/testing/layer_test.h" +#include "flutter/testing/thread_test.h" + +namespace flutter { +namespace testing { + +// This fixture allows generating tests that create |SkiaGPUObject|'s which +// are destroyed on a |SkiaUnrefQueue|. +class SkiaGPUObjectLayerTest : public LayerTestBase { + public: + SkiaGPUObjectLayerTest(); + + fml::RefPtr unref_queue() { return unref_queue_; } + + private: + fml::RefPtr unref_queue_; +}; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ diff --git a/flow/texture.cc b/flow/texture.cc index 6f25c6df89593..15c93d360366e 100644 --- a/flow/texture.cc +++ b/flow/texture.cc @@ -6,9 +6,11 @@ namespace flutter { -TextureRegistry::TextureRegistry() = default; +Texture::Texture(int64_t id) : id_(id) {} -TextureRegistry::~TextureRegistry() = default; +Texture::~Texture() = default; + +TextureRegistry::TextureRegistry() = default; void TextureRegistry::RegisterTexture(std::shared_ptr texture) { mapping_[texture->Id()] = texture; @@ -36,8 +38,4 @@ std::shared_ptr TextureRegistry::GetTexture(int64_t id) { return it != mapping_.end() ? it->second : nullptr; } -Texture::Texture(int64_t id) : id_(id) {} - -Texture::~Texture() = default; - } // namespace flutter diff --git a/flow/texture.h b/flow/texture.h index 6e06445884b66..812588d382bb1 100644 --- a/flow/texture.h +++ b/flow/texture.h @@ -14,12 +14,9 @@ namespace flutter { class Texture { - protected: - Texture(int64_t id); - public: - // Called from GPU thread. - virtual ~Texture(); + Texture(int64_t id); // Called from UI or GPU thread. + virtual ~Texture(); // Called from GPU thread. // Called from GPU thread. virtual void Paint(SkCanvas& canvas, @@ -50,7 +47,6 @@ class Texture { class TextureRegistry { public: TextureRegistry(); - ~TextureRegistry(); // Called from GPU thread. void RegisterTexture(std::shared_ptr texture); diff --git a/flow/texture_unittests.cc b/flow/texture_unittests.cc index d292e3965af87..f3eb0fc0931ac 100644 --- a/flow/texture_unittests.cc +++ b/flow/texture_unittests.cc @@ -2,44 +2,97 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/flow/testing/mock_texture.h" #include "flutter/flow/texture.h" + #include "gtest/gtest.h" namespace flutter { namespace testing { -class MockTexture : public Texture { - public: - MockTexture(int64_t textureId) : Texture(textureId) {} +TEST(TextureRegistryTest, UnregisterTextureCallbackTriggered) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(1); + + registry.RegisterTexture(mock_texture1); + registry.RegisterTexture(mock_texture2); + ASSERT_EQ(registry.GetTexture(0), mock_texture1); + ASSERT_EQ(registry.GetTexture(1), mock_texture2); + ASSERT_FALSE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); + + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); + + registry.UnregisterTexture(1); + ASSERT_EQ(registry.GetTexture(1), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_TRUE(mock_texture2->unregistered()); +} + +TEST(TextureRegistryTest, GrContextCallbackTriggered) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(1); + + registry.RegisterTexture(mock_texture1); + registry.RegisterTexture(mock_texture2); + ASSERT_FALSE(mock_texture1->gr_context_created()); + ASSERT_FALSE(mock_texture2->gr_context_created()); + ASSERT_FALSE(mock_texture1->gr_context_destroyed()); + ASSERT_FALSE(mock_texture2->gr_context_destroyed()); - ~MockTexture() override = default; + registry.OnGrContextCreated(); + ASSERT_TRUE(mock_texture1->gr_context_created()); + ASSERT_TRUE(mock_texture2->gr_context_created()); - // Called from GPU thread. - void Paint(SkCanvas& canvas, - const SkRect& bounds, - bool freeze, - GrContext* context) override {} + registry.UnregisterTexture(0); + registry.OnGrContextDestroyed(); + ASSERT_FALSE(mock_texture1->gr_context_destroyed()); + ASSERT_TRUE(mock_texture2->gr_context_created()); +} - void OnGrContextCreated() override {} +TEST(TextureRegistryTest, RegisterTextureTwice) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(0); - void OnGrContextDestroyed() override {} + registry.RegisterTexture(mock_texture1); + ASSERT_EQ(registry.GetTexture(0), mock_texture1); + registry.RegisterTexture(mock_texture2); + ASSERT_EQ(registry.GetTexture(0), mock_texture2); + ASSERT_FALSE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); + + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_FALSE(mock_texture1->unregistered()); + ASSERT_TRUE(mock_texture2->unregistered()); +} - void MarkNewFrameAvailable() override {} +TEST(TextureRegistryTest, ReuseSameTextureSlot) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(0); - void OnTextureUnregistered() override { unregistered_ = true; } + registry.RegisterTexture(mock_texture1); + ASSERT_EQ(registry.GetTexture(0), mock_texture1); - bool unregistered() { return unregistered_; } + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); - private: - bool unregistered_ = false; -}; + registry.RegisterTexture(mock_texture2); + ASSERT_EQ(registry.GetTexture(0), mock_texture2); -TEST(TextureRegistry, UnregisterTextureCallbackTriggered) { - TextureRegistry textureRegistry; - std::shared_ptr mockTexture = std::make_shared(0); - textureRegistry.RegisterTexture(mockTexture); - textureRegistry.UnregisterTexture(0); - ASSERT_TRUE(mockTexture->unregistered()); + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_TRUE(mock_texture2->unregistered()); } } // namespace testing diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 408942435ab83..94c22c5d9a673 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -2,12 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/fuchsia/sdk.gni") -import("$flutter_root/testing/testing.gni") - if (is_fuchsia) { + import("//build/fuchsia/sdk.gni") import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") } +import("$flutter_root/testing/testing.gni") source_set("fml") { sources = [ diff --git a/runtime/runtime_test.cc b/runtime/runtime_test.cc index 455a52c95a67a..295d3daf2bc0e 100644 --- a/runtime/runtime_test.cc +++ b/runtime/runtime_test.cc @@ -11,9 +11,10 @@ namespace flutter { namespace testing { RuntimeTest::RuntimeTest() - : native_resolver_(std::make_shared()) {} - -RuntimeTest::~RuntimeTest() = default; + : native_resolver_(std::make_shared()), + assets_dir_(fml::OpenDirectory(GetFixturesPath(), + false, + fml::FilePermission::kRead)) {} void RuntimeTest::SetSnapshotsAndAssets(Settings& settings) { if (!assets_dir_.is_valid()) { @@ -67,19 +68,6 @@ Settings RuntimeTest::CreateSettingsForFixture() { return settings; } -// |testing::ThreadTest| -void RuntimeTest::SetUp() { - assets_dir_ = - fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); - ThreadTest::SetUp(); -} - -// |testing::ThreadTest| -void RuntimeTest::TearDown() { - ThreadTest::TearDown(); - assets_dir_.reset(); -} - void RuntimeTest::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_resolver_->AddNativeCallback(std::move(name), callback); diff --git a/runtime/runtime_test.h b/runtime/runtime_test.h index 8c60a6e858a6c..abce2e94970f9 100644 --- a/runtime/runtime_test.h +++ b/runtime/runtime_test.h @@ -19,24 +19,17 @@ class RuntimeTest : public ThreadTest { public: RuntimeTest(); - ~RuntimeTest(); - Settings CreateSettingsForFixture(); void AddNativeCallback(std::string name, Dart_NativeFunction callback); - protected: - // |testing::ThreadTest| - void SetUp() override; - - // |testing::ThreadTest| - void TearDown() override; - private: - fml::UniqueFD assets_dir_; + void SetSnapshotsAndAssets(Settings& settings); + std::shared_ptr native_resolver_; + fml::UniqueFD assets_dir_; - void SetSnapshotsAndAssets(Settings& settings); + FML_DISALLOW_COPY_AND_ASSIGN(RuntimeTest); }; } // namespace testing diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index ed7f1febaf51c..97a382dcacbbd 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -19,9 +19,13 @@ namespace flutter { namespace testing { ShellTest::ShellTest() - : native_resolver_(std::make_shared()) {} - -ShellTest::~ShellTest() = default; + : native_resolver_(std::make_shared()), + thread_host_("io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::Platform | ThreadHost::Type::IO | + ThreadHost::Type::UI | ThreadHost::Type::GPU), + assets_dir_(fml::OpenDirectory(GetFixturesPath(), + false, + fml::FilePermission::kRead)) {} void ShellTest::SendEnginePlatformMessage( Shell* shell, @@ -225,10 +229,10 @@ Settings ShellTest::CreateSettingsForFixture() { TaskRunners ShellTest::GetTaskRunnersForFixture() { return { "test", - thread_host_->platform_thread->GetTaskRunner(), // platform - thread_host_->gpu_thread->GetTaskRunner(), // gpu - thread_host_->ui_thread->GetTaskRunner(), // ui - thread_host_->io_thread->GetTaskRunner() // io + thread_host_.platform_thread->GetTaskRunner(), // platform + thread_host_.gpu_thread->GetTaskRunner(), // gpu + thread_host_.ui_thread->GetTaskRunner(), // ui + thread_host_.io_thread->GetTaskRunner() // io }; } @@ -267,24 +271,6 @@ void ShellTest::DestroyShell(std::unique_ptr shell, latch.Wait(); } -// |testing::ThreadTest| -void ShellTest::SetUp() { - ThreadTest::SetUp(); - assets_dir_ = - fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); - thread_host_ = std::make_unique( - "io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI | - ThreadHost::Type::GPU); -} - -// |testing::ThreadTest| -void ShellTest::TearDown() { - ThreadTest::TearDown(); - assets_dir_.reset(); - thread_host_.reset(); -} - void ShellTest::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_resolver_->AddNativeCallback(std::move(name), callback); diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 270ea812dbf8a..0ae092a1c91bf 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -26,8 +26,6 @@ class ShellTest : public ThreadTest { public: ShellTest(); - ~ShellTest(); - Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); @@ -78,19 +76,14 @@ class ShellTest : public ThreadTest { // is unpredictive. static int UnreportedTimingsCount(Shell* shell); - protected: - // |testing::ThreadTest| - void SetUp() override; - - // |testing::ThreadTest| - void TearDown() override; - private: - fml::UniqueFD assets_dir_; + void SetSnapshotsAndAssets(Settings& settings); + std::shared_ptr native_resolver_; - std::unique_ptr thread_host_; + ThreadHost thread_host_; + fml::UniqueFD assets_dir_; - void SetSnapshotsAndAssets(Settings& settings); + FML_DISALLOW_COPY_AND_ASSIGN(ShellTest); }; class ShellTestVsyncClock { diff --git a/shell/platform/embedder/tests/embedder_test.cc b/shell/platform/embedder/tests/embedder_test.cc index cc699c26e463a..bca2e0eb5f258 100644 --- a/shell/platform/embedder/tests/embedder_test.cc +++ b/shell/platform/embedder/tests/embedder_test.cc @@ -9,14 +9,12 @@ namespace testing { EmbedderTest::EmbedderTest() = default; -EmbedderTest::~EmbedderTest() = default; - std::string EmbedderTest::GetFixturesDirectory() const { return GetFixturesPath(); } EmbedderTestContext& EmbedderTest::GetEmbedderContext() { - // Setup the embedder context lazily instead of in the SetUp method because we + // Setup the embedder context lazily instead of in the constructor because we // don't to do all the work if the test won't end up using context. if (!embedder_context_) { embedder_context_ = @@ -25,16 +23,5 @@ EmbedderTestContext& EmbedderTest::GetEmbedderContext() { return *embedder_context_; } -// |testing::Test| -void EmbedderTest::SetUp() { - ThreadTest::SetUp(); -} - -// |testing::Test| -void EmbedderTest::TearDown() { - embedder_context_.reset(); - ThreadTest::TearDown(); -} - } // namespace testing } // namespace flutter diff --git a/shell/platform/embedder/tests/embedder_test.h b/shell/platform/embedder/tests/embedder_test.h index f170a106553dc..15cc101ae24a6 100644 --- a/shell/platform/embedder/tests/embedder_test.h +++ b/shell/platform/embedder/tests/embedder_test.h @@ -19,8 +19,6 @@ class EmbedderTest : public ThreadTest { public: EmbedderTest(); - ~EmbedderTest() override; - std::string GetFixturesDirectory() const; EmbedderTestContext& GetEmbedderContext(); @@ -28,12 +26,6 @@ class EmbedderTest : public ThreadTest { private: std::unique_ptr embedder_context_; - // |testing::Test| - void SetUp() override; - - // |testing::Test| - void TearDown() override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTest); }; diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index bdfec3cd2e4d0..244761694a8dc 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -16,6 +16,7 @@ "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx index 912b534df93a2..1119b279ddd6c 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -16,6 +16,7 @@ "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/testing/BUILD.gn b/testing/BUILD.gn index a1028b0f18952..8bf9d7bc3922f 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -54,7 +54,11 @@ source_set("skia") { testonly = true sources = [ + "$flutter_root/testing/assertions_skia.cc", "$flutter_root/testing/assertions_skia.h", + "$flutter_root/testing/canvas_test.h", + "$flutter_root/testing/mock_canvas.cc", + "$flutter_root/testing/mock_canvas.h", ] public_deps = [ @@ -118,7 +122,8 @@ if (current_toolchain == host_toolchain) { testonly = true sources = [ - "$flutter_root/testing/test_metal_surface_unittests.cc", + "mock_canvas_unittests.cc", + "test_metal_surface_unittests.cc", ] deps = [ diff --git a/testing/assertions_skia.cc b/testing/assertions_skia.cc new file mode 100644 index 0000000000000..e8b7ce992b8a8 --- /dev/null +++ b/testing/assertions_skia.cc @@ -0,0 +1,118 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/assertions_skia.h" + +namespace flutter { +namespace testing { + +std::ostream& operator<<(std::ostream& os, const SkClipOp& o) { + switch (o) { + case SkClipOp::kDifference: + os << "ClipOpDifference"; + break; + case SkClipOp::kIntersect: + os << "ClipOpIntersect"; + break; +#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS + case SkClipOp::kUnion_deprecated: + os << "ClipOpUnion_deprecated"; + break; + case SkClipOp::kXOR_deprecated: + os << "ClipOpXOR_deprecated"; + break; + case SkClipOp::kReverseDifference_deprecated: + os << "ClipOpReverseDifference_deprecated"; + break; + case SkClipOp::kReplace_deprecated: + os << "ClipOpReplace_deprectaed"; + break; +#else + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway2: + os << "ClipOpReserved2"; + break; + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway3: + os << "ClipOpReserved3"; + break; + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway4: + os << "ClipOpReserved4"; + break; + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway5: + os << "ClipOpReserved5"; + break; +#endif + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { + os << std::endl; + os << "Scale X: " << m[SkMatrix::kMScaleX] << ", "; + os << "Skew X: " << m[SkMatrix::kMSkewX] << ", "; + os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl; + os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", "; + os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", "; + os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl; + os << "Persp X: " << m[SkMatrix::kMPersp0] << ", "; + os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", "; + os << "Persp Z: " << m[SkMatrix::kMPersp2]; + os << std::endl; + return os; +} + +std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { + os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " + << m.get(0, 3) << std::endl; + os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " + << m.get(1, 3) << std::endl; + os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " + << m.get(2, 3) << std::endl; + os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " + << m.get(3, 3); + return os; +} + +std::ostream& operator<<(std::ostream& os, const SkVector3& v) { + return os << v.x() << ", " << v.y() << ", " << v.z(); +} + +std::ostream& operator<<(std::ostream& os, const SkVector4& v) { + return os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", " + << v.fData[3]; +} + +std::ostream& operator<<(std::ostream& os, const SkRect& r) { + return os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", " + << r.fBottom; +} + +std::ostream& operator<<(std::ostream& os, const SkRRect& r) { + return os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", " + << r.rect().fRight << ", " << r.rect().fBottom; +} + +std::ostream& operator<<(std::ostream& os, const SkPath& r) { + return os << "Valid: " << r.isValid() << ", FillType: " << r.getFillType() + << ", Bounds: " << r.getBounds(); +} + +std::ostream& operator<<(std::ostream& os, const SkPoint& r) { + return os << "XY: " << r.fX << ", " << r.fY; +} + +std::ostream& operator<<(std::ostream& os, const SkISize& size) { + return os << size.width() << ", " << size.height(); +} + +std::ostream& operator<<(std::ostream& os, const SkColor4f& r) { + return os << r.fR << ", " << r.fG << ", " << r.fB << ", " << r.fA; +} + +std::ostream& operator<<(std::ostream& os, const SkPaint& r) { + return os << "Color: " << r.getColor4f() << ", Style: " << r.getStyle() + << ", AA: " << r.isAntiAlias() << ", Shader: " << r.getShader(); +} + +} // namespace testing +} // namespace flutter diff --git a/testing/assertions_skia.h b/testing/assertions_skia.h index 2b501189a23ae..f1eec1897c426 100644 --- a/testing/assertions_skia.h +++ b/testing/assertions_skia.h @@ -7,73 +7,31 @@ #include +#include "third_party/skia/include/core/SkClipOp.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkMatrix44.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint3.h" #include "third_party/skia/include/core/SkRRect.h" -//------------------------------------------------------------------------------ -// Printing -//------------------------------------------------------------------------------ - -inline std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { - os << std::endl; - os << "Scale X: " << m[SkMatrix::kMScaleX] << ", "; - os << "Skew X: " << m[SkMatrix::kMSkewX] << ", "; - os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl; - os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", "; - os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", "; - os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl; - os << "Persp X: " << m[SkMatrix::kMPersp0] << ", "; - os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", "; - os << "Persp Z: " << m[SkMatrix::kMPersp2]; - os << std::endl; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { - os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " - << m.get(0, 3) << std::endl; - os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " - << m.get(1, 3) << std::endl; - os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " - << m.get(2, 3) << std::endl; - os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " - << m.get(3, 3); - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkVector3& v) { - os << v.x() << ", " << v.y() << ", " << v.z(); - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkVector4& v) { - os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", " - << v.fData[3]; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkRect& r) { - os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", " - << r.fBottom; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkRRect& r) { - os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", " - << r.rect().fRight << ", " << r.rect().fBottom; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkPoint& r) { - os << "XY: " << r.fX << ", " << r.fY; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkISize& size) { - os << size.width() << ", " << size.height(); - return os; -} +namespace flutter { +namespace testing { + +extern std::ostream& operator<<(std::ostream& os, const SkClipOp& o); +extern std::ostream& operator<<(std::ostream& os, const SkMatrix& m); +extern std::ostream& operator<<(std::ostream& os, const SkMatrix44& m); +extern std::ostream& operator<<(std::ostream& os, const SkVector3& v); +extern std::ostream& operator<<(std::ostream& os, const SkVector4& v); +extern std::ostream& operator<<(std::ostream& os, const SkRect& r); +extern std::ostream& operator<<(std::ostream& os, const SkRRect& r); +extern std::ostream& operator<<(std::ostream& os, const SkPath& r); +extern std::ostream& operator<<(std::ostream& os, const SkPoint& r); +extern std::ostream& operator<<(std::ostream& os, const SkISize& size); +extern std::ostream& operator<<(std::ostream& os, const SkColor4f& r); +extern std::ostream& operator<<(std::ostream& os, const SkPaint& r); + +} // namespace testing +} // namespace flutter #endif // FLUTTER_TESTING_ASSERTIONS_SKIA_H_ diff --git a/testing/canvas_test.h b/testing/canvas_test.h new file mode 100644 index 0000000000000..80c157a305dc4 --- /dev/null +++ b/testing/canvas_test.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TESTING_CANVAS_TEST_H_ +#define TESTING_CANVAS_TEST_H_ + +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +// This fixture allows creating tests that make use of a mock |SkCanvas|. +template +class CanvasTestBase : public BaseT { + public: + CanvasTestBase() = default; + + MockCanvas& mock_canvas() { return canvas_; } + + private: + MockCanvas canvas_; + + FML_DISALLOW_COPY_AND_ASSIGN(CanvasTestBase); +}; +using CanvasTest = CanvasTestBase<::testing::Test>; + +} // namespace testing +} // namespace flutter + +#endif // TESTING_CANVAS_TEST_H_ diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc new file mode 100644 index 0000000000000..6782ffdeda733 --- /dev/null +++ b/testing/mock_canvas.cc @@ -0,0 +1,457 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/mock_canvas.h" + +#include "flutter/fml/logging.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkSerialProcs.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +namespace flutter { +namespace testing { + +constexpr SkISize kSize = SkISize::Make(64, 64); + +MockCanvas::MockCanvas() + : SkCanvasVirtualEnforcer(kSize.fWidth, kSize.fHeight), + internal_canvas_(imageInfo().width(), imageInfo().height()), + current_layer_(0) { + internal_canvas_.addCanvas(this); +} + +MockCanvas::~MockCanvas() { + EXPECT_EQ(current_layer_, 0); +} + +void MockCanvas::willSave() { + draw_calls_.emplace_back( + DrawCall{current_layer_, SaveData{current_layer_ + 1}}); + current_layer_++; // Must go here; func params order of eval is undefined +} + +SkCanvas::SaveLayerStrategy MockCanvas::getSaveLayerStrategy( + const SaveLayerRec& rec) { + // saveLayer calls this prior to running, so we use it to track saveLayer + // calls + draw_calls_.emplace_back(DrawCall{ + current_layer_, + SaveLayerData{rec.fBounds ? *rec.fBounds : SkRect(), + rec.fPaint ? *rec.fPaint : SkPaint(), + rec.fBackdrop ? sk_ref_sp(rec.fBackdrop) + : sk_sp(), + current_layer_ + 1}}); + current_layer_++; // Must go here; func params order of eval is undefined + return kNoLayer_SaveLayerStrategy; +} + +void MockCanvas::willRestore() { + FML_DCHECK(current_layer_ > 0); + + draw_calls_.emplace_back( + DrawCall{current_layer_, RestoreData{current_layer_ - 1}}); + current_layer_--; // Must go here; func params order of eval is undefined +} + +void MockCanvas::didConcat(const SkMatrix& matrix) { + draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{matrix}}); +} + +void MockCanvas::didSetMatrix(const SkMatrix& matrix) { + draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{matrix}}); +} + +void MockCanvas::onDrawTextBlob(const SkTextBlob* text, + SkScalar x, + SkScalar y, + const SkPaint& paint) { + // This duplicates existing logic in SkCanvas::onDrawPicture + // that should probably be split out so it doesn't need to be here as well. + SkRect storage; + const SkRect* bounds = nullptr; + if (paint.canComputeFastBounds()) { + storage = text->bounds().makeOffset(x, y); + SkRect tmp; + if (this->quickReject(paint.computeFastBounds(storage, &tmp))) { + return; + } + bounds = &storage; + } + + draw_calls_.emplace_back(DrawCall{ + current_layer_, DrawTextData{text ? text->serialize(SkSerialProcs{}) + : SkData::MakeUninitialized(0), + paint, SkPoint::Make(x, y)}}); +} + +void MockCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { + draw_calls_.emplace_back(DrawCall{current_layer_, DrawRectData{rect, paint}}); +} + +void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { + draw_calls_.emplace_back(DrawCall{current_layer_, DrawPathData{path, paint}}); +} + +void MockCanvas::onDrawShadowRec(const SkPath& path, + const SkDrawShadowRec& rec) { + (void)rec; // Can't use b/c Skia keeps this type anonymous. + draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}}); +} + +void MockCanvas::onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) { + // This duplicates existing logic in SkCanvas::onDrawPicture + // that should probably be split out so it doesn't need to be here as well. + if (!paint || paint->canComputeFastBounds()) { + SkRect bounds = picture->cullRect(); + if (paint) { + paint->computeFastBounds(bounds, &bounds); + } + if (matrix) { + matrix->mapRect(&bounds); + } + if (this->quickReject(bounds)) { + return; + } + } + + draw_calls_.emplace_back(DrawCall{ + current_layer_, + DrawPictureData{ + picture ? picture->serialize() : SkData::MakeUninitialized(0), + paint ? *paint : SkPaint(), matrix ? *matrix : SkMatrix()}}); +} + +void MockCanvas::onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle style) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipRectData{rect, op, style}}); +} + +void MockCanvas::onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle style) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipRRectData{rrect, op, style}}); +} + +void MockCanvas::onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle style) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipPathData{path, op, style}}); +} + +bool MockCanvas::onDoSaveBehind(const SkRect*) { + FML_DCHECK(false); + return false; +} + +void MockCanvas::onDrawAnnotation(const SkRect&, const char[], SkData*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawDrawable(SkDrawable*, const SkMatrix*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawPatch(const SkPoint[12], + const SkColor[4], + const SkPoint[4], + SkBlendMode, + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawPaint(const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBehind(const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawPoints(PointMode, + size_t, + const SkPoint[], + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawRegion(const SkRegion&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawOval(const SkRect&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawArc(const SkRect&, + SkScalar, + SkScalar, + bool, + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmap(const SkBitmap&, + SkScalar, + SkScalar, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImage(const SkImage*, + SkScalar, + SkScalar, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmapRect(const SkBitmap&, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImageRect(const SkImage*, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImageNine(const SkImage*, + const SkIRect&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmapNine(const SkBitmap&, + const SkIRect&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImageLattice(const SkImage*, + const Lattice&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmapLattice(const SkBitmap&, + const Lattice&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawVerticesObject(const SkVertices*, + const SkVertices::Bone[], + int, + SkBlendMode, + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawAtlas(const SkImage*, + const SkRSXform[], + const SkRect[], + const SkColor[], + int, + SkBlendMode, + const SkRect*, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawEdgeAAQuad(const SkRect&, + const SkPoint[4], + QuadAAFlags, + const SkColor4f&, + SkBlendMode) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawEdgeAAImageSet(const ImageSetEntry[], + int, + const SkPoint[], + const SkMatrix[], + const SkPaint*, + SrcRectConstraint) { + FML_DCHECK(false); +} + +void MockCanvas::onClipRegion(const SkRegion&, SkClipOp) { + FML_DCHECK(false); +} + +bool operator==(const MockCanvas::SaveData& a, const MockCanvas::SaveData& b) { + return a.save_to_layer == b.save_to_layer; +} + +std::ostream& operator<<(std::ostream& os, const MockCanvas::SaveData& data) { + return os << data.save_to_layer; +} + +bool operator==(const MockCanvas::SaveLayerData& a, + const MockCanvas::SaveLayerData& b) { + return a.save_bounds == b.save_bounds && a.restore_paint == b.restore_paint && + a.backdrop_filter == b.backdrop_filter && + a.save_to_layer == b.save_to_layer; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::SaveLayerData& data) { + return os << data.save_bounds << " " << data.restore_paint << " " + << data.backdrop_filter << " " << data.save_to_layer; +} + +bool operator==(const MockCanvas::RestoreData& a, + const MockCanvas::RestoreData& b) { + return a.restore_to_layer == b.restore_to_layer; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::RestoreData& data) { + return os << data.restore_to_layer; +} + +bool operator==(const MockCanvas::ConcatMatrixData& a, + const MockCanvas::ConcatMatrixData& b) { + return a.matrix == b.matrix; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ConcatMatrixData& data) { + return os << data.matrix; +} + +bool operator==(const MockCanvas::SetMatrixData& a, + const MockCanvas::SetMatrixData& b) { + return a.matrix == b.matrix; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::SetMatrixData& data) { + return os << data.matrix; +} + +bool operator==(const MockCanvas::DrawRectData& a, + const MockCanvas::DrawRectData& b) { + return a.rect == b.rect && a.paint == b.paint; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawRectData& data) { + return os << data.rect << " " << data.paint; +} + +bool operator==(const MockCanvas::DrawPathData& a, + const MockCanvas::DrawPathData& b) { + return a.path == b.path && a.paint == b.paint; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPathData& data) { + return os << data.path << " " << data.paint; +} + +bool operator==(const MockCanvas::DrawTextData& a, + const MockCanvas::DrawTextData& b) { + return a.serialized_text->equals(b.serialized_text.get()) && + a.paint == b.paint && a.offset == b.offset; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawTextData& data) { + return os << data.serialized_text << " " << data.paint << " " << data.offset; +} + +bool operator==(const MockCanvas::DrawPictureData& a, + const MockCanvas::DrawPictureData& b) { + return a.serialized_picture->equals(b.serialized_picture.get()) && + a.paint == b.paint && a.matrix == b.matrix; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPictureData& data) { + return os << data.serialized_picture << " " << data.paint << " " + << data.matrix; +} + +bool operator==(const MockCanvas::DrawShadowData& a, + const MockCanvas::DrawShadowData& b) { + return a.path == b.path; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawShadowData& data) { + return os << data.path; +} + +bool operator==(const MockCanvas::ClipRectData& a, + const MockCanvas::ClipRectData& b) { + return a.rect == b.rect && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRectData& data) { + return os << data.rect << " " << data.clip_op << " " << data.style; +} + +bool operator==(const MockCanvas::ClipRRectData& a, + const MockCanvas::ClipRRectData& b) { + return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRRectData& data) { + return os << data.rrect << " " << data.clip_op << " " << data.style; +} + +bool operator==(const MockCanvas::ClipPathData& a, + const MockCanvas::ClipPathData& b) { + return a.path == b.path && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipPathData& data) { + return os << data.path << " " << data.clip_op << " " << data.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawCallData& data) { + std::visit([&os](auto& d) { os << d; }, data); + return os; +} + +bool operator==(const MockCanvas::DrawCall& a, const MockCanvas::DrawCall& b) { + return a.layer == b.layer && a.data == b.data; +} + +std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawCall& draw) { + return os << "[Layer: " << draw.layer << ", Data: " << draw.data << "]"; +} + +} // namespace testing +} // namespace flutter diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h new file mode 100644 index 0000000000000..cc4c2b11a9c2e --- /dev/null +++ b/testing/mock_canvas.h @@ -0,0 +1,318 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TESTING_MOCK_CANVAS_H_ +#define TESTING_MOCK_CANVAS_H_ + +#include +#include +#include + +#include "flutter/testing/assertions_skia.h" +#include "gtest/gtest.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" +#include "third_party/skia/include/core/SkClipOp.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" + +namespace flutter { +namespace testing { + +static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); + +// Mock |SkCanvas|, useful for writing tests that use Skia but do not interact +// with the GPU. +// +// The |MockCanvas| stores a list of |DrawCall| that the test can later verify +// against the expected list of primitives to be drawn. +class MockCanvas : public SkCanvasVirtualEnforcer { + public: + using SkCanvas::kHard_ClipEdgeStyle; + using SkCanvas::kSoft_ClipEdgeStyle; + + struct SaveData { + int save_to_layer; + }; + + struct SaveLayerData { + SkRect save_bounds; + SkPaint restore_paint; + sk_sp backdrop_filter; + int save_to_layer; + }; + + struct RestoreData { + int restore_to_layer; + }; + + struct ConcatMatrixData { + SkMatrix matrix; + }; + + struct SetMatrixData { + SkMatrix matrix; + }; + + struct DrawRectData { + SkRect rect; + SkPaint paint; + }; + + struct DrawPathData { + SkPath path; + SkPaint paint; + }; + + struct DrawTextData { + sk_sp serialized_text; + SkPaint paint; + SkPoint offset; + }; + + struct DrawPictureData { + sk_sp serialized_picture; + SkPaint paint; + SkMatrix matrix; + }; + + struct DrawShadowData { + SkPath path; + }; + + struct ClipRectData { + SkRect rect; + SkClipOp clip_op; + ClipEdgeStyle style; + }; + + struct ClipRRectData { + SkRRect rrect; + SkClipOp clip_op; + ClipEdgeStyle style; + }; + + struct ClipPathData { + SkPath path; + SkClipOp clip_op; + ClipEdgeStyle style; + }; + + // Discriminated union of all the different |DrawCall| types. It is roughly + // equivalent to the different methods in |SkCanvas|' public API. + using DrawCallData = std::variant; + + // A single call made against this canvas. + struct DrawCall { + int layer; + DrawCallData data; + }; + + MockCanvas(); + ~MockCanvas() override; + + SkNWayCanvas* internal_canvas() { return &internal_canvas_; } + + const std::vector& draw_calls() const { return draw_calls_; } + + protected: + // Save/restore/set operations that we track. + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; + void willRestore() override; + void didRestore() override {} + void didConcat(const SkMatrix& matrix) override; + void didSetMatrix(const SkMatrix& matrix) override; + + // Draw and clip operations that we track. + void onDrawRect(const SkRect& rect, const SkPaint& paint) override; + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + void onDrawTextBlob(const SkTextBlob* text, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override; + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override; + void onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle style) override; + void onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle style) override; + void onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle style) override; + + // Operations that we don't track. + bool onDoSaveBehind(const SkRect*) override; + void onDrawAnnotation(const SkRect&, const char[], SkData*) override; + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawDrawable(SkDrawable*, const SkMatrix*) override; + void onDrawPatch(const SkPoint[12], + const SkColor[4], + const SkPoint[4], + SkBlendMode, + const SkPaint&) override; + void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint&) override; + void onDrawPoints(PointMode, + size_t, + const SkPoint[], + const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, + SkScalar, + SkScalar, + bool, + const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawBitmapRect(const SkBitmap&, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImage(const SkImage* image, + SkScalar x, + SkScalar y, + const SkPaint* paint) override; + void onDrawImageRect(const SkImage*, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImageNine(const SkImage*, + const SkIRect&, + const SkRect&, + const SkPaint*) override; + void onDrawBitmap(const SkBitmap& bitmap, + SkScalar x, + SkScalar y, + const SkPaint* paint) override; + void onDrawBitmapNine(const SkBitmap&, + const SkIRect&, + const SkRect&, + const SkPaint*) override; + void onDrawImageLattice(const SkImage*, + const Lattice&, + const SkRect&, + const SkPaint*) override; + void onDrawBitmapLattice(const SkBitmap&, + const Lattice&, + const SkRect&, + const SkPaint*) override; + void onDrawVerticesObject(const SkVertices*, + const SkVertices::Bone[], + int, + SkBlendMode, + const SkPaint&) override; + void onDrawAtlas(const SkImage*, + const SkRSXform[], + const SkRect[], + const SkColor[], + int, + SkBlendMode, + const SkRect*, + const SkPaint*) override; + void onDrawEdgeAAQuad(const SkRect&, + const SkPoint[4], + QuadAAFlags, + const SkColor4f&, + SkBlendMode) override; + void onDrawEdgeAAImageSet(const ImageSetEntry[], + int, + const SkPoint[], + const SkMatrix[], + const SkPaint*, + SrcRectConstraint) override; + void onClipRegion(const SkRegion&, SkClipOp) override; + + private: + SkNWayCanvas internal_canvas_; + + std::vector draw_calls_; + int current_layer_; +}; + +extern bool operator==(const MockCanvas::SaveData& a, + const MockCanvas::SaveData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::SaveData& data); +extern bool operator==(const MockCanvas::SaveLayerData& a, + const MockCanvas::SaveLayerData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::SaveLayerData& data); +extern bool operator==(const MockCanvas::RestoreData& a, + const MockCanvas::RestoreData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::RestoreData& data); +extern bool operator==(const MockCanvas::ConcatMatrixData& a, + const MockCanvas::ConcatMatrixData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ConcatMatrixData& data); +extern bool operator==(const MockCanvas::SetMatrixData& a, + const MockCanvas::SetMatrixData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::SetMatrixData& data); +extern bool operator==(const MockCanvas::DrawRectData& a, + const MockCanvas::DrawRectData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawRectData& data); +extern bool operator==(const MockCanvas::DrawPathData& a, + const MockCanvas::DrawPathData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPathData& data); +extern bool operator==(const MockCanvas::DrawTextData& a, + const MockCanvas::DrawTextData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawTextData& data); +extern bool operator==(const MockCanvas::DrawPictureData& a, + const MockCanvas::DrawPictureData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPictureData& data); +extern bool operator==(const MockCanvas::DrawShadowData& a, + const MockCanvas::DrawShadowData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawShadowData& data); +extern bool operator==(const MockCanvas::ClipRectData& a, + const MockCanvas::ClipRectData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRectData& data); +extern bool operator==(const MockCanvas::ClipRRectData& a, + const MockCanvas::ClipRRectData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRRectData& data); +extern bool operator==(const MockCanvas::ClipPathData& a, + const MockCanvas::ClipPathData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipPathData& data); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawCallData& data); +extern bool operator==(const MockCanvas::DrawCall& a, + const MockCanvas::DrawCall& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawCall& draw); + +} // namespace testing +} // namespace flutter + +#endif // TESTING_MOCK_CANVAS_H_ diff --git a/testing/mock_canvas_unittests.cc b/testing/mock_canvas_unittests.cc new file mode 100644 index 0000000000000..bab2fa43edd99 --- /dev/null +++ b/testing/mock_canvas_unittests.cc @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/mock_canvas.h" + +#include "flutter/testing/canvas_test.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +using MockCanvasTest = CanvasTest; + +TEST_F(MockCanvasTest, DrawCalls) { + const SkRect rect = SkRect::MakeWH(5.0f, 5.0f); + const SkPaint paint = SkPaint(SkColors::kGreen); + const auto expected_draw_calls = std::vector{ + MockCanvas::DrawCall{0, MockCanvas::DrawRectData{rect, paint}}}; + + mock_canvas().drawRect(rect, paint); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(MockCanvasTest, InvalidDrawCalls) { + EXPECT_DEATH_IF_SUPPORTED(mock_canvas().drawRRect(SkRRect(), SkPaint()), ""); +} + +} // namespace testing +} // namespace flutter diff --git a/testing/thread_test.cc b/testing/thread_test.cc index 88415169a3c70..2f67b6ee18368 100644 --- a/testing/thread_test.cc +++ b/testing/thread_test.cc @@ -8,18 +8,16 @@ namespace flutter { namespace testing { +namespace { -// |testing::Test| -void ThreadTest::SetUp() { +fml::RefPtr GetDefaultTaskRunner() { fml::MessageLoop::EnsureInitializedForCurrentThread(); - current_task_runner_ = fml::MessageLoop::GetCurrent().GetTaskRunner(); + return fml::MessageLoop::GetCurrent().GetTaskRunner(); } -// |testing::Test| -void ThreadTest::TearDown() { - current_task_runner_ = nullptr; - extra_threads_.clear(); -} +} // namespace + +ThreadTest::ThreadTest() : current_task_runner_(GetDefaultTaskRunner()) {} fml::RefPtr ThreadTest::GetCurrentTaskRunner() { return current_task_runner_; diff --git a/testing/thread_test.h b/testing/thread_test.h index 8c55dbf80ce68..4a7d60fb0312c 100644 --- a/testing/thread_test.h +++ b/testing/thread_test.h @@ -21,14 +21,16 @@ namespace testing { /// @brief A fixture that creates threads with running message loops that /// are terminated when the test is done (the threads are joined /// then as well). While this fixture may be used on it's own, it is -/// often sub-classed but other fixtures whose functioning requires +/// often sub-classed by other fixtures whose functioning requires /// threads to be created as necessary. /// class ThreadTest : public ::testing::Test { public: + ThreadTest(); + //---------------------------------------------------------------------------- /// @brief Get the task runner for the thread that the current unit-test - /// is running on. The creates a message loop is necessary. + /// is running on. This creates a message loop as necessary. /// /// @attention Unlike all other threads and task runners, this task runner is /// shared by all tests running in the process. Tests must ensure @@ -56,16 +58,11 @@ class ThreadTest : public ::testing::Test { /// fml::RefPtr CreateNewThread(std::string name = ""); - protected: - // |testing::Test| - void SetUp() override; - - // |testing::Test| - void TearDown() override; - private: fml::RefPtr current_task_runner_; std::vector> extra_threads_; + + FML_DISALLOW_COPY_AND_ASSIGN(ThreadTest); }; } // namespace testing From 36dc3b5a5132567fd57b51f5dd1d97d30582df9b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 3 Dec 2019 12:49:59 -0500 Subject: [PATCH 305/591] Roll src/third_party/skia 2792515dab7c..49e564e5e02c (28 commits) (#14084) https://skia.googlesource.com/skia.git/+log/2792515dab7c..49e564e5e02c git log 2792515dab7c..49e564e5e02c --date=short --first-parent --format='%ad %ae %s' 2019-12-03 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-03 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-02 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-02 rosasco@google.com skqp CIPD upload infra for Fuchsia 2019-12-02 mtklein@google.com remove T*/void* union in SkTArray 2019-12-02 mtklein@google.com tweak for GCC, memcpy() -> for-loop 2019-12-02 mtklein@google.com remove pointless tests 2019-12-02 egdaniel@google.com Update GrSwizzle to only store key and not string. 2019-12-02 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-02 bsalomon@google.com This is a reland of GrDomainEffect with significant changes: 2019-12-02 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-02 michaelludwig@google.com Compare textureType separately from backend format 2019-12-02 stani@google.com Expose SkRuntimeShaderFactory class to clients 2019-12-02 jlavrova@google.com A new API call on Paragraph: a number of unresolved glyphs 2019-12-02 mtklein@google.com replace SkColorSpaceXformSteps::Required() 2019-12-02 reed@google.com Reland "fix sense of ifdef for deprecated method" 2019-12-02 reed@google.com Revert "fix sense of ifdef for deprecated method" 2019-12-02 reed@google.com fix sense of ifdef for deprecated method 2019-12-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src c8e3617e402d..8a4500482e8d (272 commits) 2019-12-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 bc82fb14aab4..ea0dcd4bda18 (7 commits) 2019-12-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader fb7ca1d5aca5..b64fbfec4dcd (3 commits) 2019-12-01 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update SKP version 2019-11-29 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-29 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-29 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 43fc40aebfd8..c8e3617e402d (252 commits) 2019-11-29 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 fc3ec57ddf27..bc82fb14aab4 (9 commits) 2019-11-29 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader d6e903bdc9ef..fb7ca1d5aca5 (13 commits) 2019-11-29 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@49e564e5e02c If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 576270b959494..0a64bb69e328a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '2792515dab7c5623e6b02dc0645ca87bc035c545', + 'skia_revision': '49e564e5e02cfa80202452f47aa28f422db08c0d', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index dc1ea2555d9f2..0c35c18737dca 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 81e6c3d8cb6f315b7415188b8e0ca05a +Signature: 6994b850237578149dcd1d33789b2a59 UNUSED LICENSES: @@ -962,6 +962,7 @@ FILE: ../../../third_party/skia/animations/text#1.xml FILE: ../../../third_party/skia/bench/microbench.json FILE: ../../../third_party/skia/bench/skpbench.json FILE: ../../../third_party/skia/build/fuchsia/skqp/skqp.cmx +FILE: ../../../third_party/skia/build/fuchsia/skqp/test_manifest.json FILE: ../../../third_party/skia/docker/binary-size/Dockerfile FILE: ../../../third_party/skia/docker/cmake-release/Dockerfile FILE: ../../../third_party/skia/docker/skia-build-tools/Dockerfile From 84bf72917caa519db0406070a5d3c5575ebf68fb Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 3 Dec 2019 12:02:37 -0800 Subject: [PATCH 306/591] Revert PRs to unblock David and Jim's work (#14088) * Revert "Add flow test fixtures and tests (#13986)" This reverts commit 620f5281b819f304e8e9e945222e26b17b087cc3. * Revert "Dynamically determine whether to use offscreen surface based on need (#13976)" This reverts commit a86ef946563b020108320bbfb974bf7343284fd3. --- BUILD.gn | 5 +- ci/licenses_golden/licenses_flutter | 14 - flow/BUILD.gn | 75 +-- flow/embedded_views.h | 17 - flow/flow_run_all_unittests.cc | 7 - flow/layers/backdrop_filter_layer.cc | 6 +- flow/layers/backdrop_filter_layer.h | 1 + .../layers/backdrop_filter_layer_unittests.cc | 184 ------- flow/layers/clip_path_layer.cc | 3 +- flow/layers/clip_path_layer.h | 1 + flow/layers/clip_path_layer_unittests.cc | 194 -------- flow/layers/clip_rect_layer.cc | 3 +- flow/layers/clip_rect_layer.h | 1 + flow/layers/clip_rect_layer_unittests.cc | 192 -------- flow/layers/clip_rrect_layer.cc | 3 +- flow/layers/clip_rrect_layer.h | 1 + flow/layers/clip_rrect_layer_unittests.cc | 197 -------- flow/layers/color_filter_layer.cc | 6 +- flow/layers/color_filter_layer.h | 1 + flow/layers/color_filter_layer_unittests.cc | 197 -------- flow/layers/container_layer.cc | 45 +- flow/layers/container_layer.h | 26 +- flow/layers/container_layer_unittests.cc | 200 -------- flow/layers/layer.cc | 27 +- flow/layers/layer.h | 33 -- flow/layers/layer_tree.cc | 2 + flow/layers/layer_tree.h | 6 +- flow/layers/layer_tree_unittests.cc | 203 -------- flow/layers/layer_unittests.cc | 188 ------- flow/layers/opacity_layer.cc | 10 +- flow/layers/opacity_layer.h | 1 + flow/layers/opacity_layer_unittests.cc | 310 ------------ flow/layers/performance_overlay_layer.cc | 55 +-- flow/layers/performance_overlay_layer.h | 7 - .../performance_overlay_layer_unittests.cc | 85 +--- flow/layers/physical_shape_layer.cc | 96 ++-- flow/layers/physical_shape_layer.h | 12 +- flow/layers/physical_shape_layer_unittests.cc | 272 ++--------- flow/layers/picture_layer.cc | 2 + flow/layers/picture_layer.h | 1 + flow/layers/picture_layer_unittests.cc | 102 ---- flow/layers/platform_view_layer.cc | 2 + flow/layers/platform_view_layer.h | 1 + flow/layers/platform_view_layer_unittests.cc | 38 -- flow/layers/shader_mask_layer.cc | 6 +- flow/layers/shader_mask_layer.h | 1 + flow/layers/shader_mask_layer_unittests.cc | 255 ---------- flow/layers/texture_layer.cc | 2 + flow/layers/texture_layer.h | 1 + flow/layers/texture_layer_unittests.cc | 57 --- flow/layers/transform_layer.cc | 2 + flow/layers/transform_layer.h | 1 + flow/layers/transform_layer_unittests.cc | 226 --------- flow/matrix_decomposition_unittests.cc | 9 +- flow/mutators_stack_unittests.cc | 1 - flow/raster_cache_unittests.cc | 10 - flow/skia_gpu_object.h | 3 + flow/skia_gpu_object_unittests.cc | 107 +--- flow/testing/layer_test.h | 74 --- flow/testing/mock_layer.cc | 38 -- flow/testing/mock_layer.h | 50 -- flow/testing/mock_layer_unittests.cc | 85 ---- flow/testing/mock_texture.cc | 31 -- flow/testing/mock_texture.h | 57 --- flow/testing/mock_texture_unittests.cc | 43 -- flow/testing/skia_gpu_object_layer_test.cc | 18 - flow/testing/skia_gpu_object_layer_test.h | 30 -- flow/texture.cc | 10 +- flow/texture.h | 8 +- flow/texture_unittests.cc | 99 +--- fml/BUILD.gn | 5 +- runtime/runtime_test.cc | 20 +- runtime/runtime_test.h | 15 +- shell/common/rasterizer.cc | 3 +- shell/common/shell_test.cc | 36 +- shell/common/shell_test.h | 17 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 22 +- shell/gpu/gpu_surface_gl.h | 9 +- shell/gpu/gpu_surface_gl_delegate.cc | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 6 +- shell/gpu/gpu_surface_metal.h | 3 +- shell/gpu/gpu_surface_metal.mm | 3 +- shell/gpu/gpu_surface_software.cc | 3 +- shell/gpu/gpu_surface_software.h | 4 +- shell/gpu/gpu_surface_vulkan.cc | 3 +- shell/gpu/gpu_surface_vulkan.h | 4 +- .../framework/Source/FlutterPlatformViews.mm | 2 +- shell/platform/darwin/ios/ios_surface_gl.h | 2 +- shell/platform/darwin/ios/ios_surface_gl.mm | 7 +- .../platform/embedder/tests/embedder_test.cc | 15 +- shell/platform/embedder/tests/embedder_test.h | 8 + .../flutter/meta/flutter_aot_runner.cmx | 1 - .../flutter/meta/flutter_jit_runner.cmx | 1 - shell/platform/fuchsia/flutter/surface.cc | 3 +- shell/platform/fuchsia/flutter/surface.h | 3 +- testing/BUILD.gn | 7 +- testing/assertions_skia.cc | 118 ----- testing/assertions_skia.h | 84 +++- testing/canvas_test.h | 33 -- testing/mock_canvas.cc | 457 ------------------ testing/mock_canvas.h | 318 ------------ testing/mock_canvas_unittests.cc | 30 -- testing/thread_test.cc | 14 +- testing/thread_test.h | 15 +- 105 files changed, 424 insertions(+), 4910 deletions(-) delete mode 100644 flow/layers/backdrop_filter_layer_unittests.cc delete mode 100644 flow/layers/clip_path_layer_unittests.cc delete mode 100644 flow/layers/clip_rect_layer_unittests.cc delete mode 100644 flow/layers/clip_rrect_layer_unittests.cc delete mode 100644 flow/layers/color_filter_layer_unittests.cc delete mode 100644 flow/layers/container_layer_unittests.cc delete mode 100644 flow/layers/layer_tree_unittests.cc delete mode 100644 flow/layers/layer_unittests.cc delete mode 100644 flow/layers/opacity_layer_unittests.cc delete mode 100644 flow/layers/picture_layer_unittests.cc delete mode 100644 flow/layers/platform_view_layer_unittests.cc delete mode 100644 flow/layers/shader_mask_layer_unittests.cc delete mode 100644 flow/layers/texture_layer_unittests.cc delete mode 100644 flow/layers/transform_layer_unittests.cc delete mode 100644 flow/testing/layer_test.h delete mode 100644 flow/testing/mock_layer.cc delete mode 100644 flow/testing/mock_layer.h delete mode 100644 flow/testing/mock_layer_unittests.cc delete mode 100644 flow/testing/mock_texture.cc delete mode 100644 flow/testing/mock_texture.h delete mode 100644 flow/testing/mock_texture_unittests.cc delete mode 100644 flow/testing/skia_gpu_object_layer_test.cc delete mode 100644 flow/testing/skia_gpu_object_layer_test.h delete mode 100644 testing/assertions_skia.cc delete mode 100644 testing/canvas_test.h delete mode 100644 testing/mock_canvas.cc delete mode 100644 testing/mock_canvas.h delete mode 100644 testing/mock_canvas_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index 419ae849544a1..361a5c6f0e2f6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -95,10 +95,7 @@ group("flutter") { # Fuchsia currently only supports a subset of our unit tests if (is_fuchsia) { - public_deps += [ - "$flutter_root/flow:flow_tests", - "$flutter_root/fml:fml_tests", - ] + public_deps += [ "$flutter_root/fml:fml_tests" ] } } diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f57b00086d432..c296ebe416fbd 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -30,33 +30,24 @@ FILE: ../../../flutter/flow/instrumentation.cc FILE: ../../../flutter/flow/instrumentation.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h -FILE: ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/child_scene_layer.cc FILE: ../../../flutter/flow/layers/child_scene_layer.h FILE: ../../../flutter/flow/layers/clip_path_layer.cc FILE: ../../../flutter/flow/layers/clip_path_layer.h -FILE: ../../../flutter/flow/layers/clip_path_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rect_layer.cc FILE: ../../../flutter/flow/layers/clip_rect_layer.h -FILE: ../../../flutter/flow/layers/clip_rect_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.h -FILE: ../../../flutter/flow/layers/clip_rrect_layer_unittests.cc FILE: ../../../flutter/flow/layers/color_filter_layer.cc FILE: ../../../flutter/flow/layers/color_filter_layer.h -FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h -FILE: ../../../flutter/flow/layers/container_layer_unittests.cc FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h FILE: ../../../flutter/flow/layers/layer_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h -FILE: ../../../flutter/flow/layers/layer_tree_unittests.cc -FILE: ../../../flutter/flow/layers/layer_unittests.cc FILE: ../../../flutter/flow/layers/opacity_layer.cc FILE: ../../../flutter/flow/layers/opacity_layer.h -FILE: ../../../flutter/flow/layers/opacity_layer_unittests.cc FILE: ../../../flutter/flow/layers/performance_overlay_layer.cc FILE: ../../../flutter/flow/layers/performance_overlay_layer.h FILE: ../../../flutter/flow/layers/performance_overlay_layer_unittests.cc @@ -65,19 +56,14 @@ FILE: ../../../flutter/flow/layers/physical_shape_layer.h FILE: ../../../flutter/flow/layers/physical_shape_layer_unittests.cc FILE: ../../../flutter/flow/layers/picture_layer.cc FILE: ../../../flutter/flow/layers/picture_layer.h -FILE: ../../../flutter/flow/layers/picture_layer_unittests.cc FILE: ../../../flutter/flow/layers/platform_view_layer.cc FILE: ../../../flutter/flow/layers/platform_view_layer.h -FILE: ../../../flutter/flow/layers/platform_view_layer_unittests.cc FILE: ../../../flutter/flow/layers/shader_mask_layer.cc FILE: ../../../flutter/flow/layers/shader_mask_layer.h -FILE: ../../../flutter/flow/layers/shader_mask_layer_unittests.cc FILE: ../../../flutter/flow/layers/texture_layer.cc FILE: ../../../flutter/flow/layers/texture_layer.h -FILE: ../../../flutter/flow/layers/texture_layer_unittests.cc FILE: ../../../flutter/flow/layers/transform_layer.cc FILE: ../../../flutter/flow/layers/transform_layer.h -FILE: ../../../flutter/flow/layers/transform_layer_unittests.cc FILE: ../../../flutter/flow/matrix_decomposition.cc FILE: ../../../flutter/flow/matrix_decomposition.h FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 50981bb316e16..c2f0c98415a3e 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -4,8 +4,8 @@ if (is_fuchsia) { import("//build/fuchsia/sdk.gni") - import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") } + import("$flutter_root/testing/testing.gni") source_set("flow") { @@ -102,26 +102,6 @@ test_fixtures("flow_fixtures") { fixtures = [] } -source_set("flow_testing") { - testonly = true - - sources = [ - "testing/layer_test.h", - "testing/mock_layer.cc", - "testing/mock_layer.h", - "testing/mock_texture.cc", - "testing/mock_texture.h", - "testing/skia_gpu_object_layer_test.cc", - "testing/skia_gpu_object_layer_test.h", - ] - - public_deps = [ - ":flow", - "$flutter_root/testing:skia", - "//third_party/googletest:gtest", - ] -} - executable("flow_unittests") { testonly = true @@ -129,75 +109,22 @@ executable("flow_unittests") { "flow_run_all_unittests.cc", "flow_test_utils.cc", "flow_test_utils.h", - "layers/backdrop_filter_layer_unittests.cc", - "layers/clip_path_layer_unittests.cc", - "layers/clip_rect_layer_unittests.cc", - "layers/clip_rrect_layer_unittests.cc", - "layers/color_filter_layer_unittests.cc", - "layers/container_layer_unittests.cc", - "layers/layer_tree_unittests.cc", - "layers/layer_unittests.cc", - "layers/opacity_layer_unittests.cc", "layers/performance_overlay_layer_unittests.cc", "layers/physical_shape_layer_unittests.cc", - "layers/picture_layer_unittests.cc", - "layers/platform_view_layer_unittests.cc", - "layers/shader_mask_layer_unittests.cc", - "layers/texture_layer_unittests.cc", - "layers/transform_layer_unittests.cc", "matrix_decomposition_unittests.cc", "mutators_stack_unittests.cc", "raster_cache_unittests.cc", "skia_gpu_object_unittests.cc", - "testing/mock_layer_unittests.cc", - "testing/mock_texture_unittests.cc", "texture_unittests.cc", ] deps = [ ":flow", ":flow_fixtures", - ":flow_testing", "$flutter_root/fml", - "$flutter_root/testing:skia", "$flutter_root/testing:testing_lib", "//third_party/dart/runtime:libdart_jit", # for tracing "//third_party/googletest:gtest", "//third_party/skia", ] } - -if (is_fuchsia) { - fuchsia_archive("flow_tests") { - testonly = true - - deps = [ - ":flow_unittests", - ] - - binary = "flow_unittests" - - libraries = common_libs - - meta_dir = "$flutter_root/testing/fuchsia/meta" - cmx_file = "$meta_dir/fuchsia_test.cmx" - - resources = [ - { - path = rebase_path( - "$flutter_root/testing/resources/performance_overlay_gold_60fps.png") - dest = "flutter/testing/resources/performance_overlay_gold_60fps.png" - }, - { - path = rebase_path( - "$flutter_root/testing/resources/performance_overlay_gold_90fps.png") - dest = "flutter/testing/resources/performance_overlay_gold_90fps.png" - }, - { - path = rebase_path( - "$flutter_root/testing/resources/performance_overlay_gold_120fps.png") - dest = "flutter/testing/resources/performance_overlay_gold_120fps.png" - }, - ] - } -} diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 030eb88c8a06d..919ee83a8bb86 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -143,7 +143,6 @@ class MutatorsStack { // Returns an iterator pointing to the bottom of the stack. const std::vector>::const_reverse_iterator Bottom() const; - bool is_empty() const { return vector_.empty(); } bool operator==(const MutatorsStack& other) const { if (vector_.size() != other.vector_.size()) { @@ -157,26 +156,10 @@ class MutatorsStack { return true; } - bool operator==(const std::vector& other) const { - if (vector_.size() != other.size()) { - return false; - } - for (size_t i = 0; i < vector_.size(); i++) { - if (*vector_[i] != other[i]) { - return false; - } - } - return true; - } - bool operator!=(const MutatorsStack& other) const { return !operator==(other); } - bool operator!=(const std::vector& other) const { - return !operator==(other); - } - private: std::vector> vector_; }; // MutatorsStack diff --git a/flow/flow_run_all_unittests.cc b/flow/flow_run_all_unittests.cc index 39963730172ee..4cf0ba3d7fcdf 100644 --- a/flow/flow_run_all_unittests.cc +++ b/flow/flow_run_all_unittests.cc @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "flutter/fml/build_config.h" #include "flutter/fml/command_line.h" #include "flutter/fml/logging.h" #include "gtest/gtest.h" @@ -24,14 +23,8 @@ int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); fml::CommandLine cmd = fml::CommandLineFromArgcArgv(argc, argv); - -#if defined(OS_FUCHSIA) - flutter::SetGoldenDir(cmd.GetOptionValueWithDefault( - "golden-dir", "/pkg/data/flutter/testing/resources")); -#else flutter::SetGoldenDir( cmd.GetOptionValueWithDefault("golden-dir", "flutter/testing/resources")); -#endif flutter::SetFontFile(cmd.GetOptionValueWithDefault( "font-file", "flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf")); diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index c69b7fcb3d8d1..573db97f191a2 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -7,9 +7,9 @@ namespace flutter { BackdropFilterLayer::BackdropFilterLayer(sk_sp filter) - : filter_(std::move(filter)) { - set_layer_reads_surface(filter_.get() != nullptr); -} + : filter_(std::move(filter)) {} + +BackdropFilterLayer::~BackdropFilterLayer() = default; void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index 732a1ac27e89a..ede9ceeef41f8 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -14,6 +14,7 @@ namespace flutter { class BackdropFilterLayer : public ContainerLayer { public: BackdropFilterLayer(sk_sp filter); + ~BackdropFilterLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc deleted file mode 100644 index b0582300da91f..0000000000000 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/backdrop_filter_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" -#include "third_party/skia/include/core/SkImageFilter.h" -#include "third_party/skia/include/effects/SkImageFilters.h" - -namespace flutter { -namespace testing { - -using BackdropFilterLayerTest = LayerTest; - -TEST_F(BackdropFilterLayerTest, EmptyLayer) { - auto layer = std::make_shared(sk_sp()); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(BackdropFilterLayerTest, PaintBeforePreroll) { - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(sk_sp()); - layer->Add(mock_layer); - - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(BackdropFilterLayerTest, EmptyFilter) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(nullptr); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(BackdropFilterLayerTest, SimpleFilter) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_filter); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - layer_filter, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(BackdropFilterLayerTest, MultipleChildren) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); - const SkPath child_path1 = SkPath().addRect(child_bounds); - const SkPath child_path2 = - SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); - const SkPaint child_paint1 = SkPaint(SkColors::kYellow); - const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(layer_filter); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), children_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), - layer_filter, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(BackdropFilterLayerTest, Nested) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); - const SkPath child_path1 = SkPath().addRect(child_bounds); - const SkPath child_path2 = - SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); - const SkPaint child_paint1 = SkPaint(SkColors::kYellow); - const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); - auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray)); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer1 = std::make_shared(layer_filter1); - auto layer2 = std::make_shared(layer_filter2); - layer2->Add(mock_layer2); - layer1->Add(mock_layer1); - layer1->Add(layer2); - - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer1->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer1->paint_bounds(), children_bounds); - EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - - layer1->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), - layer_filter1, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), - SkPaint(), layer_filter2, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index acc6f17dd5433..d08c19b34eeb9 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -15,9 +15,10 @@ namespace flutter { ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) : clip_path_(clip_path), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); - set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } +ClipPathLayer::~ClipPathLayer() = default; + void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_path_bounds = clip_path_.getBounds(); diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index c21e53c34e76e..fd4d56f0db7f0 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -12,6 +12,7 @@ namespace flutter { class ClipPathLayer : public ContainerLayer { public: ClipPathLayer(const SkPath& clip_path, Clip clip_behavior = Clip::antiAlias); + ~ClipPathLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc deleted file mode 100644 index 68b5fe9b48bad..0000000000000 --- a/flow/layers/clip_path_layer_unittests.cc +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/clip_path_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using ClipPathLayerTest = LayerTest; - -TEST_F(ClipPathLayerTest, ClipNone) { - EXPECT_DEATH_IF_SUPPORTED( - auto clip = std::make_shared(SkPath(), Clip::none), - "clip_behavior != Clip::none"); -} - -TEST_F(ClipPathLayerTest, EmptyLayer) { - auto layer = std::make_shared(SkPath(), Clip::hardEdge); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipPathLayerTest, PaintBeforePreroll) { - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath layer_path = SkPath().addRect(layer_bounds); - auto layer = std::make_shared(layer_path, Clip::hardEdge); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipPathLayerTest, CulledLayer) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath layer_path = SkPath().addRect(layer_bounds); - auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(layer_path, Clip::hardEdge); - layer->Add(mock_layer); - - preroll_context()->cull_rect = kEmptyRect; // Cull everything - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipPathLayerTest, ChildOutsideBounds) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); - const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath layer_path = SkPath().addRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_path, Clip::hardEdge); - layer->Add(mock_layer); - - SkRect intersect_bounds = layer_bounds; - SkRect child_intersect_bounds = layer_bounds; - intersect_bounds.intersect(cull_bounds); - child_intersect_bounds.intersect(child_bounds); - preroll_context()->cull_rect = cull_bounds; // Cull child - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ClipPathLayerTest, FullyContainedChild) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath layer_path = SkPath().addRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_path, Clip::hardEdge); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ClipPathLayerTest, PartiallyContainedChild) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); - const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath layer_path = SkPath().addRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_path, Clip::hardEdge); - layer->Add(mock_layer); - - SkRect intersect_bounds = layer_bounds; - SkRect child_intersect_bounds = layer_bounds; - intersect_bounds.intersect(cull_bounds); - child_intersect_bounds.intersect(child_bounds); - preroll_context()->cull_rect = cull_bounds; // Cull child - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index 141ad8a009694..de7590624e408 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -9,9 +9,10 @@ namespace flutter { ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) : clip_rect_(clip_rect), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); - set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } +ClipRectLayer::~ClipRectLayer() = default; + void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; if (context->cull_rect.intersect(clip_rect_)) { diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index 50eef22a46e2f..76c5a3f01c873 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -12,6 +12,7 @@ namespace flutter { class ClipRectLayer : public ContainerLayer { public: ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior); + ~ClipRectLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc deleted file mode 100644 index c5ee39f2e02e8..0000000000000 --- a/flow/layers/clip_rect_layer_unittests.cc +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/clip_rect_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using ClipRectLayerTest = LayerTest; - -TEST_F(ClipRectLayerTest, ClipNone) { - EXPECT_DEATH_IF_SUPPORTED( - auto clip = std::make_shared(kEmptyRect, Clip::none), - "clip_behavior != Clip::none"); -} - -TEST_F(ClipRectLayerTest, EmptyLayer) { - auto layer = std::make_shared(kEmptyRect, Clip::hardEdge); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipRectLayerTest, PaintBeforePreroll) { - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - auto layer = std::make_shared(layer_bounds, Clip::hardEdge); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipRectLayerTest, CulledLayer) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(layer_bounds, Clip::hardEdge); - layer->Add(mock_layer); - - preroll_context()->cull_rect = kEmptyRect; // Cull everything - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipRectLayerTest, ChildOutsideBounds) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); - const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_bounds, Clip::hardEdge); - layer->Add(mock_layer); - - SkRect intersect_bounds = layer_bounds; - SkRect child_intersect_bounds = layer_bounds; - intersect_bounds.intersect(cull_bounds); - child_intersect_bounds.intersect(child_bounds); - preroll_context()->cull_rect = cull_bounds; // Cull child - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_bounds)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ClipRectLayerTest, FullyContainedChild) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_bounds, Clip::hardEdge); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_bounds)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ClipRectLayerTest, PartiallyContainedChild) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); - const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_bounds, Clip::hardEdge); - layer->Add(mock_layer); - - SkRect intersect_bounds = layer_bounds; - SkRect child_intersect_bounds = layer_bounds; - intersect_bounds.intersect(cull_bounds); - child_intersect_bounds.intersect(child_bounds); - preroll_context()->cull_rect = cull_bounds; // Cull child - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_bounds)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index dd4fdd09f03d4..9899a1658732d 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -9,9 +9,10 @@ namespace flutter { ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) : clip_rrect_(clip_rrect), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); - set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } +ClipRRectLayer::~ClipRRectLayer() = default; + void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_rrect_bounds = clip_rrect_.getBounds(); diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index ce1cca2b568de..53f74f30a0776 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -12,6 +12,7 @@ namespace flutter { class ClipRRectLayer : public ContainerLayer { public: ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior); + ~ClipRRectLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc deleted file mode 100644 index 4eb506eec5ecc..0000000000000 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/clip_rrect_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using ClipRRectLayerTest = LayerTest; - -TEST_F(ClipRRectLayerTest, ClipNone) { - const SkRRect layer_rrect = SkRRect::MakeEmpty(); - EXPECT_DEATH_IF_SUPPORTED( - auto clip = std::make_shared(layer_rrect, Clip::none), - "clip_behavior != Clip::none"); -} - -TEST_F(ClipRRectLayerTest, EmptyLayer) { - const SkRRect layer_rrect = SkRRect::MakeEmpty(); - auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipRRectLayerTest, PaintBeforePreroll) { - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); - auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipRRectLayerTest, CulledLayer) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - layer->Add(mock_layer); - - preroll_context()->cull_rect = kEmptyRect; // Cull everything - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ClipRRectLayerTest, ChildOutsideBounds) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); - const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - layer->Add(mock_layer); - - SkRect intersect_bounds = layer_bounds; - SkRect child_intersect_bounds = layer_bounds; - intersect_bounds.intersect(cull_bounds); - child_intersect_bounds.intersect(child_bounds); - preroll_context()->cull_rect = cull_bounds; // Cull child - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ClipRRectLayerTest, FullyContainedChild) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); - const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - layer->Add(mock_layer); - - SkRect intersect_bounds = layer_bounds; - SkRect child_intersect_bounds = layer_bounds; - intersect_bounds.intersect(cull_bounds); - child_intersect_bounds.intersect(child_bounds); - preroll_context()->cull_rect = cull_bounds; // Cull child - - layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched - EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index d37c938784dc4..f838b0612b2e5 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -7,9 +7,9 @@ namespace flutter { ColorFilterLayer::ColorFilterLayer(sk_sp filter) - : filter_(std::move(filter)) { - set_renders_to_save_layer(true); -} + : filter_(std::move(filter)) {} + +ColorFilterLayer::~ColorFilterLayer() = default; void ColorFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ColorFilterLayer::Paint"); diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index 628c5b17f6e1c..cf1de4cb610fc 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -14,6 +14,7 @@ namespace flutter { class ColorFilterLayer : public ContainerLayer { public: ColorFilterLayer(sk_sp filter); + ~ColorFilterLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc deleted file mode 100644 index d85dadb642a7b..0000000000000 --- a/flow/layers/color_filter_layer_unittests.cc +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/color_filter_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" -#include "third_party/skia/include/core/SkColorFilter.h" -#include "third_party/skia/include/effects/SkColorMatrixFilter.h" - -namespace flutter { -namespace testing { - -using ColorFilterLayerTest = LayerTest; - -TEST_F(ColorFilterLayerTest, EmptyLayer) { - auto layer = std::make_shared(sk_sp()); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ColorFilterLayerTest, PaintBeforePreroll) { - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(sk_sp()); - layer->Add(mock_layer); - - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ColorFilterLayerTest, EmptyFilter) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(nullptr); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - - SkPaint filter_paint; - filter_paint.setColorFilter(nullptr); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ColorFilterLayerTest, SimpleFilter) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto layer_filter = - SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_filter); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - - SkPaint filter_paint; - filter_paint.setColorFilter(layer_filter); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ColorFilterLayerTest, MultipleChildren) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); - const SkPath child_path1 = SkPath().addRect(child_bounds); - const SkPath child_path2 = - SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); - const SkPaint child_paint1 = SkPaint(SkColors::kYellow); - const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter = - SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(layer_filter); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), children_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - - SkPaint filter_paint; - filter_paint.setColorFilter(layer_filter); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, - filter_paint, nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ColorFilterLayerTest, Nested) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); - const SkPath child_path1 = SkPath().addRect(child_bounds); - const SkPath child_path2 = - SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); - const SkPaint child_paint1 = SkPaint(SkColors::kYellow); - const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter1 = - SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); - auto layer_filter2 = - SkColorMatrixFilter::MakeLightingFilter(SK_ColorMAGENTA, SK_ColorBLUE); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer1 = std::make_shared(layer_filter1); - auto layer2 = std::make_shared(layer_filter2); - layer2->Add(mock_layer2); - layer1->Add(mock_layer1); - layer1->Add(layer2); - - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer1->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer1->paint_bounds(), children_bounds); - EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - - SkPaint filter_paint1, filter_paint2; - filter_paint1.setColorFilter(layer_filter1); - filter_paint2.setColorFilter(layer_filter2); - layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, - filter_paint1, nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), - filter_paint2, nullptr, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 1c81262b549f7..d5c6a2a03a34a 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -6,44 +6,13 @@ namespace flutter { -ContainerLayer::ContainerLayer() - : child_needs_screen_readback_(false), renders_to_save_layer_(false) {} +ContainerLayer::ContainerLayer() {} + +ContainerLayer::~ContainerLayer() = default; void ContainerLayer::Add(std::shared_ptr layer) { - Layer* the_layer = layer.get(); - the_layer->set_parent(this); + layer->set_parent(this); layers_.push_back(std::move(layer)); - if (the_layer->tree_reads_surface()) { - NotifyChildReadback(the_layer); - } -} - -void ContainerLayer::ClearChildren() { - layers_.clear(); - if (child_needs_screen_readback_) { - child_needs_screen_readback_ = false; - UpdateTreeReadsSurface(); - } -} - -void ContainerLayer::set_renders_to_save_layer(bool value) { - if (renders_to_save_layer_ != value) { - renders_to_save_layer_ = value; - UpdateTreeReadsSurface(); - } -} - -void ContainerLayer::NotifyChildReadback(const Layer* layer) { - if (child_needs_screen_readback_ || !layer->tree_reads_surface()) { - return; - } - child_needs_screen_readback_ = true; - UpdateTreeReadsSurface(); -} - -bool ContainerLayer::ComputeTreeReadsSurface() const { - return layer_reads_surface() || - (!renders_to_save_layer_ && child_needs_screen_readback_); } void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { @@ -54,12 +23,6 @@ void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(child_paint_bounds); } -void ContainerLayer::Paint(PaintContext& context) const { - FML_DCHECK(needs_painting()); - - PaintChildren(context); -} - void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index 431bd12b8dda5..ef1c03328d1df 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -13,54 +13,34 @@ namespace flutter { class ContainerLayer : public Layer { public: ContainerLayer(); + ~ContainerLayer() override; void Add(std::shared_ptr layer); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; - void Paint(PaintContext& context) const override; + #if defined(OS_FUCHSIA) void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) const std::vector>& layers() const { return layers_; } - // Called when the layer, which must be a child of this container, - // changes its tree_reads_surface() result from false to true. - void NotifyChildReadback(const Layer* layer); - protected: void PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds); void PaintChildren(PaintContext& context) const; - virtual bool ComputeTreeReadsSurface() const override; - #if defined(OS_FUCHSIA) void UpdateSceneChildren(SceneUpdateContext& context); #endif // defined(OS_FUCHSIA) - // Specify whether or not the container has its children render - // to a SaveLayer which will prevent many rendering anomalies - // from propagating to the parent - such as if the children - // read back from the surface on which they render, or if the - // children perform non-associative rendering. Those children - // will now be performing those operations on the SaveLayer - // rather than the layer that this container renders onto. - void set_renders_to_save_layer(bool value); - // For OpacityLayer to restructure to have a single child. - void ClearChildren(); + void ClearChildren() { layers_.clear(); } private: std::vector> layers_; - // child_needs_screen_readback_ is maintained even if the - // renders_to_save_layer_ property is set in case both - // parameters are dynamically and independently determined. - bool child_needs_screen_readback_; - bool renders_to_save_layer_; - FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc deleted file mode 100644 index f4724b7c0949e..0000000000000 --- a/flow/layers/container_layer_unittests.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/container_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using ContainerLayerTest = LayerTest; - -TEST_F(ContainerLayerTest, LayerWithParentHasPlatformView) { - auto layer = std::make_shared(); - - preroll_context()->has_platform_view = true; - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), - "!context->has_platform_view"); -} - -TEST_F(ContainerLayerTest, EmptyLayer) { - auto layer = std::make_shared(); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ContainerLayerTest, PaintBeforePreroll) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(); - layer->Add(mock_layer); - - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ContainerLayerTest, Simple) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPaint child_paint(SkColors::kGreen); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_FALSE(preroll_context()->has_platform_view); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path, child_paint}}})); -} - -TEST_F(ContainerLayerTest, Multiple) { - SkPath child_path1; - child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPath child_path2; - child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); - SkPaint child_paint1(SkColors::kGray); - SkPaint child_paint2(SkColors::kGreen); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - - auto mock_layer1 = std::make_shared( - child_path1, child_paint1, true /* fake_has_platform_view */); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect expected_total_bounds = child_path1.getBounds(); - expected_total_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); - EXPECT_TRUE(preroll_context()->has_platform_view); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), - kGiantRect); // Siblings are independent - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ - child_path2, child_paint2}}})); -} - -TEST_F(ContainerLayerTest, MultipleWithEmpty) { - SkPath child_path1; - child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPaint child_paint1(SkColors::kGray); - SkPaint child_paint2(SkColors::kGreen); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(SkPath(), child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_FALSE(preroll_context()->has_platform_view); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); - EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_FALSE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); -} - -TEST_F(ContainerLayerTest, NeedsSystemComposite) { - SkPath child_path1; - child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPath child_path2; - child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); - SkPaint child_paint1(SkColors::kGray); - SkPaint child_paint2(SkColors::kGreen); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - - auto mock_layer1 = std::make_shared( - child_path1, child_paint1, false /* fake_has_platform_view */, - true /* fake_needs_system_composite */); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect expected_total_bounds = child_path1.getBounds(); - expected_total_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); - EXPECT_FALSE(preroll_context()->has_platform_view); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_TRUE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_TRUE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ - child_path2, child_paint2}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index 052b70fac9827..b729f582a0a9a 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "flutter/flow/layers/layer.h" -#include "flutter/flow/layers/container_layer.h" #include "flutter/flow/paint_utils.h" #include "third_party/skia/include/core/SkColorFilter.h" @@ -14,9 +13,7 @@ Layer::Layer() : parent_(nullptr), needs_system_composite_(false), paint_bounds_(SkRect::MakeEmpty()), - unique_id_(NextUniqueID()), - tree_reads_surface_(false), - layer_reads_surface_(false) {} + unique_id_(NextUniqueID()) {} Layer::~Layer() = default; @@ -29,28 +26,6 @@ uint64_t Layer::NextUniqueID() { return id; } -void Layer::set_layer_reads_surface(bool value) { - if (layer_reads_surface_ != value) { - layer_reads_surface_ = value; - UpdateTreeReadsSurface(); - } -} - -bool Layer::ComputeTreeReadsSurface() const { - return layer_reads_surface_; -} - -void Layer::UpdateTreeReadsSurface() { - bool new_tree_reads_surface = ComputeTreeReadsSurface(); - - if (tree_reads_surface_ != new_tree_reads_surface) { - tree_reads_surface_ = new_tree_reads_surface; - if (parent_ != nullptr) { - parent_->NotifyChildReadback(this); - } - } -} - void Layer::Preroll(PrerollContext* context, const SkMatrix& matrix) {} #if defined(OS_FUCHSIA) diff --git a/flow/layers/layer.h b/flow/layers/layer.h index ef114b4c381c3..66944376e8ce8 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -145,47 +145,14 @@ class Layer { bool needs_painting() const { return !paint_bounds_.isEmpty(); } - // True iff the layer, or some descendant of the layer, performs an - // operation which depends on (i.e. must read back from) the surface - // on which it is rendered in any way beyond the functionality of - // BlendMode. This value has no setter as it is computed from other - // flags and properties on the layer. - // For an example see |BackdropFilterLayer|. - // - // See |UpdateTreeReadsSurface| - // See |set_layer_reads_surface| - // See |ContainerLayer::set_renders_to_save_layer| - // See |ContainerLayer::NotifyChildReadback| - bool tree_reads_surface() const { return tree_reads_surface_; } - uint64_t unique_id() const { return unique_id_; } - protected: - // Compute a new value for tree_reads_surface_ from all of the various - // properties of this layer. - // Used by |UpdateTreeReadsSurface| - virtual bool ComputeTreeReadsSurface() const; - - // Update the tree_reads_surface_ value and propagate changes to - // ancestors if needed. - // Uses |ComputeTreeReadsSurface| - void UpdateTreeReadsSurface(); - - // True iff the layer itself (not a child or other descendant) performs - // an operation which reads from the surface on which it is rendered. - bool layer_reads_surface() const { return layer_reads_surface_; } - - void set_layer_reads_surface(bool value); - private: ContainerLayer* parent_; bool needs_system_composite_; SkRect paint_bounds_; uint64_t unique_id_; - bool tree_reads_surface_; - bool layer_reads_surface_; - static uint64_t NextUniqueID(); FML_DISALLOW_COPY_AND_ASSIGN(Layer); diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index caa5531a79c06..f0e37c9bed565 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -17,6 +17,8 @@ LayerTree::LayerTree() checkerboard_raster_cache_images_(false), checkerboard_offscreen_layers_(false) {} +LayerTree::~LayerTree() = default; + void LayerTree::RecordBuildTime(fml::TimePoint start) { build_start_ = start; build_finish_ = fml::TimePoint::Now(); diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index d1a22be02c43e..124b8a85dea45 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -22,6 +22,8 @@ class LayerTree { public: LayerTree(); + ~LayerTree(); + void Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false); @@ -41,10 +43,6 @@ class LayerTree { root_layer_ = std::move(root_layer); } - bool root_needs_screen_readback() const { - return root_layer_ && root_layer_->tree_reads_surface(); - } - const SkISize& frame_size() const { return frame_size_; } void set_frame_size(const SkISize& frame_size) { frame_size_ = frame_size; } diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc deleted file mode 100644 index e640e92c84b89..0000000000000 --- a/flow/layers/layer_tree_unittests.cc +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/layer_tree.h" - -#include "flutter/flow/compositor_context.h" -#include "flutter/flow/layers/container_layer.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/canvas_test.h" -#include "flutter/testing/mock_canvas.h" -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -class LayerTreeTest : public CanvasTest { - public: - void SetUp() override { - root_transform_ = SkMatrix::MakeTrans(1.0f, 1.0f); - scoped_frame_ = compositor_context_.AcquireFrame( - nullptr, &mock_canvas(), nullptr, root_transform_, false, nullptr); - } - - void TearDown() override { scoped_frame_ = nullptr; } - - LayerTree& layer_tree() { return layer_tree_; } - CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); } - const SkMatrix& root_transform() { return root_transform_; } - - private: - LayerTree layer_tree_; - CompositorContext compositor_context_; - SkMatrix root_transform_; - std::unique_ptr scoped_frame_; -}; - -TEST_F(LayerTreeTest, EmptyLayer) { - auto layer = std::make_shared(); - - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - - layer_tree().Paint(frame()); -} - -TEST_F(LayerTreeTest, PaintBeforePreroll) { - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - SkPath child_path; - child_path.addRect(child_bounds); - auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(); - layer->Add(mock_layer); - - layer_tree().set_root_layer(layer); - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); - - layer_tree().Paint(frame()); - EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); -} - -TEST_F(LayerTreeTest, Simple) { - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kCyan); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(); - layer->Add(mock_layer); - - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), root_transform()); - - layer_tree().Paint(frame()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path, child_paint}}})); -} - -TEST_F(LayerTreeTest, Multiple) { - const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path2 = SkPath().addRect(8.0f, 2.0f, 16.5f, 14.5f); - const SkPaint child_paint1(SkColors::kGray); - const SkPaint child_paint2(SkColors::kGreen); - auto mock_layer1 = std::make_shared( - child_path1, child_paint1, true /* fake_has_platform_view */); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect expected_total_bounds = child_path1.getBounds(); - expected_total_bounds.join(child_path2.getBounds()); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); - EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), - kGiantRect); // Siblings are independent - - layer_tree().Paint(frame()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ - child_path2, child_paint2}}})); -} - -TEST_F(LayerTreeTest, MultipleWithEmpty) { - const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); - const SkPaint child_paint1(SkColors::kGray); - const SkPaint child_paint2(SkColors::kGreen); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(SkPath(), child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); - EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_FALSE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); - EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); - - layer_tree().Paint(frame()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); -} - -TEST_F(LayerTreeTest, NeedsSystemComposite) { - const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path2 = SkPath().addRect(8.0f, 2.0f, 16.5f, 14.5f); - const SkPaint child_paint1(SkColors::kGray); - const SkPaint child_paint2(SkColors::kGreen); - auto mock_layer1 = std::make_shared( - child_path1, child_paint1, false /* fake_has_platform_view */, - true /* fake_needs_system_composite */); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect expected_total_bounds = child_path1.getBounds(); - expected_total_bounds.join(child_path2.getBounds()); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_TRUE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_TRUE(layer->needs_system_composite()); - EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); - EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); - - layer_tree().Paint(frame()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ - child_path2, child_paint2}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/layer_unittests.cc b/flow/layers/layer_unittests.cc deleted file mode 100644 index 360dba5acd5dc..0000000000000 --- a/flow/layers/layer_unittests.cc +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/backdrop_filter_layer.h" -#include "flutter/flow/layers/clip_path_layer.h" -#include "flutter/flow/layers/clip_rect_layer.h" -#include "flutter/flow/layers/clip_rrect_layer.h" -#include "flutter/flow/layers/color_filter_layer.h" -#include "flutter/flow/layers/container_layer.h" -#include "flutter/flow/layers/opacity_layer.h" -#include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/flow/layers/shader_mask_layer.h" - -#include "third_party/skia/include/effects/SkBlurImageFilter.h" - -#include "gtest/gtest.h" - -namespace flutter { - -class ReadbackLayer : public ContainerLayer { - public: - ReadbackLayer(const bool reads, const bool saves) { - set_layer_reads_surface(reads); - set_renders_to_save_layer(saves); - } - ~ReadbackLayer() override = default; - - static std::shared_ptr Make(const bool reads, - const bool saves) { - return std::make_shared(reads, saves); - } - - void set_read(const bool reads) { set_layer_reads_surface(reads); } - - void Paint(PaintContext& context) const override {} -}; - -void TestLayerFlag(bool reads, bool saves) { - EXPECT_EQ(ReadbackLayer(reads, saves).tree_reads_surface(), reads); -} - -void TestChildFlag(bool child_reads, bool uses_save_layer, bool ret) { - ReadbackLayer parent = ReadbackLayer(false, uses_save_layer); - parent.Add(ReadbackLayer::Make(child_reads, false)); - EXPECT_EQ(parent.tree_reads_surface(), ret); -} - -TEST(Layer, ReadbackFalse) { - TestLayerFlag(false, false); - TestLayerFlag(false, true); -} - -TEST(Layer, ReadbackTrue) { - TestLayerFlag(true, false); - TestLayerFlag(true, true); -} - -TEST(Layer, NoReadbackNoSaveLayer) { - TestChildFlag(false, false, false); -} - -TEST(Layer, NoReadbackButSaveLayer) { - TestChildFlag(false, true, false); -} - -TEST(Layer, ReadbackNoSaveLayer) { - TestChildFlag(true, false, true); -} - -TEST(Layer, ReadbackButSaveLayer) { - TestChildFlag(true, true, false); -} - -TEST(Layer, AddedChildReadback) { - ReadbackLayer parent = ReadbackLayer(false, false); - EXPECT_FALSE(parent.tree_reads_surface()); - parent.Add(ReadbackLayer::Make(false, false)); - EXPECT_FALSE(parent.tree_reads_surface()); - parent.Add(ReadbackLayer::Make(true, false)); - EXPECT_TRUE(parent.tree_reads_surface()); -} - -TEST(Layer, ChildChangesToReadback) { - ReadbackLayer parent = ReadbackLayer(false, false); - EXPECT_FALSE(parent.tree_reads_surface()); - parent.Add(ReadbackLayer::Make(false, false)); - EXPECT_FALSE(parent.tree_reads_surface()); - std::shared_ptr child = ReadbackLayer::Make(false, false); - parent.Add(child); - EXPECT_FALSE(parent.tree_reads_surface()); - child->set_read(true); - EXPECT_TRUE(parent.tree_reads_surface()); -} - -TEST(Layer, BackdropFilterLayer) { - sk_sp filter = SkBlurImageFilter::Make( - 5.0f, 5.0f, nullptr, nullptr, SkBlurImageFilter::kClamp_TileMode); - EXPECT_TRUE(BackdropFilterLayer(filter).tree_reads_surface()); - filter.reset(); - EXPECT_FALSE(BackdropFilterLayer(filter).tree_reads_surface()); -} - -void TestClipRect(Clip clip_behavior, bool ret) { - ClipRectLayer layer = ClipRectLayer(SkRect::MakeWH(5, 5), clip_behavior); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_EQ(layer.tree_reads_surface(), ret); -} - -TEST(Layer, ClipRectSaveLayer) { - // TestClipRect(Clip::none, true); // ClipRectLayer asserts !Clip::none - TestClipRect(Clip::hardEdge, true); - TestClipRect(Clip::antiAlias, true); - TestClipRect(Clip::antiAliasWithSaveLayer, false); -} - -void TestClipRRect(Clip clip_behavior, bool ret) { - SkRRect r_rect = SkRRect::MakeRect(SkRect::MakeWH(5, 5)); - ClipRRectLayer layer = ClipRRectLayer(r_rect, clip_behavior); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_EQ(layer.tree_reads_surface(), ret); -} - -TEST(Layer, ClipRRectSaveLayer) { - // TestClipRRect(Clip::none, true); // ClipRRectLayer asserts !Clip::none - TestClipRRect(Clip::hardEdge, true); - TestClipRRect(Clip::antiAlias, true); - TestClipRRect(Clip::antiAliasWithSaveLayer, false); -} - -void TestClipPath(Clip clip_behavior, bool ret) { - SkPath path = SkPath(); - path.moveTo(0, 0); - path.lineTo(5, 0); - path.lineTo(0, 5); - path.close(); - ClipPathLayer layer = ClipPathLayer(path, clip_behavior); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_EQ(layer.tree_reads_surface(), ret); -} - -TEST(Layer, ClipPathSaveLayer) { - // TestClipPath(Clip::none, true); // ClipRRectLayer asserts !Clip::none - TestClipPath(Clip::hardEdge, true); - TestClipPath(Clip::antiAlias, true); - TestClipPath(Clip::antiAliasWithSaveLayer, false); -} - -TEST(Layer, ColorFilterSaveLayer) { - sk_sp filter = SkColorFilters::LinearToSRGBGamma(); - ColorFilterLayer layer = ColorFilterLayer(filter); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_FALSE(layer.tree_reads_surface()); -} - -TEST(Layer, OpacitySaveLayer) { - OpacityLayer layer = OpacityLayer(10, SkPoint::Make(0, 0)); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_FALSE(layer.tree_reads_surface()); -} - -void TestPhysicalShapeLayer(Clip clip_behavior, bool ret) { - SkPath path = SkPath(); - path.moveTo(0, 0); - path.lineTo(5, 0); - path.lineTo(0, 5); - path.close(); - PhysicalShapeLayer layer = PhysicalShapeLayer( - SK_ColorRED, SK_ColorBLUE, 1.0f, 100.0f, 10.0f, path, clip_behavior); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_EQ(layer.tree_reads_surface(), ret); -} - -TEST(Layer, PhysicalShapeSaveLayer) { - TestPhysicalShapeLayer(Clip::none, true); - TestPhysicalShapeLayer(Clip::hardEdge, true); - TestPhysicalShapeLayer(Clip::antiAlias, true); - TestPhysicalShapeLayer(Clip::antiAliasWithSaveLayer, false); -} - -TEST(Layer, ShaderMaskSaveLayer) { - ShaderMaskLayer layer = ShaderMaskLayer( - SkShaders::Empty(), SkRect::MakeWH(5, 5), SkBlendMode::kSrcOver); - layer.Add(ReadbackLayer::Make(true, false)); - EXPECT_FALSE(layer.tree_reads_surface()); -} - -} // namespace flutter diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 6fd5f1692d809..6257700ffbddf 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -9,13 +9,9 @@ namespace flutter { OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset) - : alpha_(alpha), offset_(offset) { - // This type of layer either renders via a SaveLayer or it renders - // from a raster cache image. In either case, it does not pass - // any rendering from a child through to the surface so it is - // effectively "renders to save layer" in all cases. - set_renders_to_save_layer(true); -} + : alpha_(alpha), offset_(offset) {} + +OpacityLayer::~OpacityLayer() = default; void OpacityLayer::EnsureSingleChild() { FML_DCHECK(layers().size() > 0); // OpacityLayer should never be a leaf diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index f1c18c51918e7..795d8841ba6ed 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -26,6 +26,7 @@ class OpacityLayer : public ContainerLayer { // to many leaf layers. Therefore we try to capture that offset here to stop // the propagation as repainting the OpacityLayer is expensive. OpacityLayer(int alpha, const SkPoint& offset); + ~OpacityLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc deleted file mode 100644 index 84b2221a2e3d0..0000000000000 --- a/flow/layers/opacity_layer_unittests.cc +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/opacity_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using OpacityLayerTest = LayerTest; - -TEST_F(OpacityLayerTest, LeafLayer) { - auto layer = - std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), - "layers\\(\\)\\.size\\(\\) > 0"); -} - -TEST_F(OpacityLayerTest, EmptyLayer) { - auto mock_layer = std::make_shared(SkPath()); - auto layer = - std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds()); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(OpacityLayerTest, PaintBeforePreroll) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - auto mock_layer = std::make_shared(child_path); - auto layer = - std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - layer->Add(mock_layer); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(OpacityLayerTest, FullyOpaque) { - const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); - const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); - const SkMatrix layer_transform = - SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer_transform)); -#endif - const SkPaint child_paint = SkPaint(SkColors::kGreen); - const SkRect expected_layer_bounds = - layer_transform.mapRect(child_path.getBounds()); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(SK_AlphaOPAQUE, layer_offset); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), - SkMatrix::Concat(initial_transform, layer_transform)); - EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_transform), Mutator(SK_AlphaOPAQUE)})); - - const SkPaint opacity_paint = SkPaint(SkColors::kBlack); // A = 1.0f - SkRect opacity_bounds; - expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) - .roundOut(&opacity_bounds); - auto expected_draw_calls = std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, -#endif - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, - 2}}, - MockCanvas::DrawCall{2, - MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); -} - -TEST_F(OpacityLayerTest, FullyTransparent) { - const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); - const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); - const SkMatrix layer_transform = - SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer_transform)); -#endif - const SkPaint child_paint = SkPaint(SkColors::kGreen); - const SkRect expected_layer_bounds = - layer_transform.mapRect(child_path.getBounds()); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = - std::make_shared(SK_AlphaTRANSPARENT, layer_offset); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), - SkMatrix::Concat(initial_transform, layer_transform)); - EXPECT_EQ( - mock_layer->parent_mutators(), - std::vector({Mutator(layer_transform), Mutator(SK_AlphaTRANSPARENT)})); - - auto expected_draw_calls = std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, -#endif - MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, - MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{2, - MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); -} - -TEST_F(OpacityLayerTest, HalfTransparent) { - const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); - const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); - const SkMatrix layer_transform = - SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer_transform)); -#endif - const SkPaint child_paint = SkPaint(SkColors::kGreen); - const SkRect expected_layer_bounds = - layer_transform.mapRect(child_path.getBounds()); - const SkAlpha alpha_half = 255 / 2; - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(alpha_half, layer_offset); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), - SkMatrix::Concat(initial_transform, layer_transform)); - EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_transform), Mutator(alpha_half)})); - - const SkPaint opacity_paint = - SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha_half))); - SkRect opacity_bounds; - expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) - .roundOut(&opacity_bounds); - auto expected_draw_calls = std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, -#endif - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, - 2}}, - MockCanvas::DrawCall{2, - MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); -} - -TEST_F(OpacityLayerTest, Nested) { - const SkPath child1_path = SkPath().addRect(SkRect::MakeWH(5.0f, 6.0f)); - const SkPath child2_path = SkPath().addRect(SkRect::MakeWH(2.0f, 7.0f)); - const SkPath child3_path = SkPath().addRect(SkRect::MakeWH(6.0f, 6.0f)); - const SkPoint layer1_offset = SkPoint::Make(0.5f, 1.5f); - const SkPoint layer2_offset = SkPoint::Make(2.5f, 0.5f); - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); - const SkMatrix layer1_transform = - SkMatrix::MakeTrans(layer1_offset.fX, layer1_offset.fY); - const SkMatrix layer2_transform = - SkMatrix::MakeTrans(layer2_offset.fX, layer2_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer1_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer1_transform)); - const SkMatrix integral_layer2_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), - layer2_transform)); -#endif - const SkPaint child1_paint = SkPaint(SkColors::kRed); - const SkPaint child2_paint = SkPaint(SkColors::kBlue); - const SkPaint child3_paint = SkPaint(SkColors::kGreen); - const SkAlpha alpha1 = 155; - const SkAlpha alpha2 = 224; - auto mock_layer1 = std::make_shared(child1_path, child1_paint); - auto mock_layer2 = std::make_shared(child2_path, child2_paint); - auto mock_layer3 = std::make_shared(child3_path, child3_paint); - auto layer1 = std::make_shared(alpha1, layer1_offset); - auto layer2 = std::make_shared(alpha2, layer2_offset); - layer2->Add(mock_layer2); - layer1->Add(mock_layer1); - layer1->Add(layer2); - layer1->Add(mock_layer3); // Ensure something is processed after recursion - - const SkRect expected_layer2_bounds = - layer2_transform.mapRect(child2_path.getBounds()); - SkRect expected_layer1_bounds = expected_layer2_bounds; - expected_layer1_bounds.join(child1_path.getBounds()); - expected_layer1_bounds.join(child3_path.getBounds()); - expected_layer1_bounds = layer1_transform.mapRect(expected_layer1_bounds); - layer1->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds()); - EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds()); - EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); - EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(mock_layer3->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), - SkMatrix::Concat(initial_transform, layer1_transform)); - // EXPECT_EQ(mock_layer1->parent_mutators(), - // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); - EXPECT_EQ( - mock_layer2->parent_matrix(), - SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), - layer2_transform)); - // EXPECT_EQ(mock_layer2->parent_mutators(), - // std::vector({Mutator(layer1_transform), Mutator(alpha1), - // Mutator(layer2_transform), Mutator(alpha2)})); - EXPECT_EQ(mock_layer3->parent_matrix(), - SkMatrix::Concat(initial_transform, layer1_transform)); - // EXPECT_EQ(mock_layer3->parent_mutators(), - // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); - - const SkPaint opacity1_paint = - SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha1))); - const SkPaint opacity2_paint = - SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha2))); - SkRect opacity1_bounds, opacity2_bounds; - expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY) - .roundOut(&opacity1_bounds); - expected_layer2_bounds.makeOffset(-layer2_offset.fX, -layer2_offset.fY) - .roundOut(&opacity2_bounds); - auto expected_draw_calls = std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer1_transform}}, -#endif - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint, - nullptr, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child1_path, child1_paint}}, - MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, - MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 3, MockCanvas::SetMatrixData{integral_layer2_transform}}, -#endif - MockCanvas::DrawCall{ - 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint, - nullptr, 4}}, - MockCanvas::DrawCall{ - 4, MockCanvas::DrawPathData{child2_path, child2_paint}}, - MockCanvas::DrawCall{4, MockCanvas::RestoreData{3}}, - MockCanvas::DrawCall{3, MockCanvas::RestoreData{2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child3_path, child3_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); - layer1->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index ef7b6f2c6194c..ebb279c966866 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -8,11 +8,26 @@ #include "flutter/flow/layers/performance_overlay_layer.h" #include "third_party/skia/include/core/SkFont.h" -#include "third_party/skia/include/core/SkTextBlob.h" namespace flutter { namespace { +void DrawStatisticsText(SkCanvas& canvas, + const std::string& string, + int x, + int y, + const std::string& font_path) { + SkFont font; + if (font_path != "") { + font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); + } + font.setSize(15); + SkPaint paint; + paint.setColor(SK_ColorGRAY); + canvas.drawSimpleText(string.c_str(), string.size(), SkTextEncoding::kUTF8, x, + y, font, paint); +} + void VisualizeStopWatch(SkCanvas& canvas, const Stopwatch& stopwatch, SkScalar x, @@ -32,39 +47,21 @@ void VisualizeStopWatch(SkCanvas& canvas, } if (show_labels) { - auto text = PerformanceOverlayLayer::MakeStatisticsText( - stopwatch, label_prefix, font_path); - SkPaint paint; - paint.setColor(SK_ColorGRAY); - canvas.drawTextBlob(text, x + label_x, y + height + label_y, paint); + double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); + double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); + std::stringstream stream; + stream.setf(std::ios::fixed | std::ios::showpoint); + stream << std::setprecision(1); + stream << label_prefix << " " + << "max " << max_ms_per_frame << " ms/frame, " + << "avg " << average_ms_per_frame << " ms/frame"; + DrawStatisticsText(canvas, stream.str(), x + label_x, y + height + label_y, + font_path); } } } // namespace -sk_sp PerformanceOverlayLayer::MakeStatisticsText( - const Stopwatch& stopwatch, - const std::string& label_prefix, - const std::string& font_path) { - SkFont font; - if (font_path != "") { - font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); - } - font.setSize(15); - - double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); - double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); - std::stringstream stream; - stream.setf(std::ios::fixed | std::ios::showpoint); - stream << std::setprecision(1); - stream << label_prefix << " " - << "max " << max_ms_per_frame << " ms/frame, " - << "avg " << average_ms_per_frame << " ms/frame"; - auto text = stream.str(); - return SkTextBlob::MakeFromText(text.c_str(), text.size(), font, - SkTextEncoding::kUTF8); -} - PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options, const char* font_path) : options_(options) { diff --git a/flow/layers/performance_overlay_layer.h b/flow/layers/performance_overlay_layer.h index b1434a221e688..b5c3370d2055a 100644 --- a/flow/layers/performance_overlay_layer.h +++ b/flow/layers/performance_overlay_layer.h @@ -7,12 +7,9 @@ #include -#include "flutter/flow/instrumentation.h" #include "flutter/flow/layers/layer.h" #include "flutter/fml/macros.h" -class SkTextBlob; - namespace flutter { const int kDisplayRasterizerStatistics = 1 << 0; @@ -22,10 +19,6 @@ const int kVisualizeEngineStatistics = 1 << 3; class PerformanceOverlayLayer : public Layer { public: - static sk_sp MakeStatisticsText(const Stopwatch& stopwatch, - const std::string& label_prefix, - const std::string& font_path); - explicit PerformanceOverlayLayer(uint64_t options, const char* font_path = nullptr); diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 11d7218f2c5aa..605717c870ee3 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -2,28 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/layers/performance_overlay_layer.h" - #include "flutter/flow/flow_test_utils.h" +#include "flutter/flow/layers/performance_overlay_layer.h" #include "flutter/flow/raster_cache.h" -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/build_config.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" -#include "gtest/gtest.h" -#include "third_party/skia/include/core/SkData.h" -#include "third_party/skia/include/core/SkSerialProcs.h" + #include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/utils/SkBase64.h" -#include -#include +#include "gtest/gtest.h" -namespace flutter { -namespace testing { -namespace { +#include // To get the size of kMockedTimes in compile time. template @@ -88,7 +77,7 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; -#if !defined(OS_LINUX) +#if !OS_LINUX GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX const bool golden_data_matches = golden_data->equals(snapshot_data.get()); @@ -108,71 +97,12 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Golden file mismatch. Please check " << "the difference between " << golden_file_path << " and " << new_golden_file_path << ", and replace the former " - << "with the latter if the difference looks good.\nS\n" + << "with the latter if the difference looks good.\n\n" << "See also the base64 encoded " << new_golden_file_path << ":\n" << b64_char; } } -} // namespace - -using PerformanceOverlayLayerTest = LayerTest; - -TEST_F(PerformanceOverlayLayerTest, EmptyLayerGraph) { - const uint64_t overlay_opts = kVisualizeRasterizerStatistics; - auto layer = std::make_shared(overlay_opts); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), ""); -} - -TEST_F(PerformanceOverlayLayerTest, InvalidOptions) { - const SkRect layer_bounds = SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f); - const uint64_t overlay_opts = 0; - auto layer = std::make_shared(overlay_opts); - - // TODO(): Note calling code has to call set_paint_bounds right now. Make - // this a constructor parameter and move the set_paint_bounds into Preroll - layer->set_paint_bounds(layer_bounds); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), layer_bounds); - EXPECT_TRUE(layer->needs_painting()); - - // Nothing is drawn if options are invalid (0). - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); -} - -TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { - const SkRect layer_bounds = SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f); - const uint64_t overlay_opts = kDisplayRasterizerStatistics; - auto layer = std::make_shared(overlay_opts); - - // TODO(): Note calling code has to call set_paint_bounds right now. Make - // this a constructor parameter and move the set_paint_bounds into Preroll - layer->set_paint_bounds(layer_bounds); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), layer_bounds); - EXPECT_TRUE(layer->needs_painting()); - - layer->Paint(paint_context()); - auto overlay_text = PerformanceOverlayLayer::MakeStatisticsText( - paint_context().raster_time, "GPU", ""); - auto overlay_text_data = overlay_text->serialize(SkSerialProcs{}); - SkPaint text_paint; - text_paint.setColor(SK_ColorGRAY); - SkPoint text_position = SkPoint::Make(16.0f, 22.0f); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawTextData{overlay_text_data, text_paint, - text_position}}})); -} - TEST(PerformanceOverlayLayerDefault, Gold) { TestPerformanceOverlayLayerGold(60); } @@ -184,6 +114,3 @@ TEST(PerformanceOverlayLayer90fps, Gold) { TEST(PerformanceOverlayLayer120fps, Gold) { TestPerformanceOverlayLayerGold(120); } - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index d138bff80f651..0a607a88c23b0 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -22,9 +22,7 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, : color_(color), shadow_color_(shadow_color), device_pixel_ratio_(device_pixel_ratio), -#if defined(OS_FUCHSIA) viewport_depth_(viewport_depth), -#endif elevation_(elevation), path_(path), isRect_(false), @@ -48,9 +46,10 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, // an SkPath. frameRRect_ = SkRRect::MakeRect(path.getBounds()); } - set_renders_to_save_layer(clip_behavior == Clip::antiAliasWithSaveLayer); } +PhysicalShapeLayer::~PhysicalShapeLayer() = default; + void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); @@ -67,11 +66,48 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, // Let the system compositor draw all shadows for us. set_needs_system_composite(true); #else - // We will draw the shadow in Paint(), so add some margin to the paint - // bounds to leave space for the shadow. We fill this whole region and clip - // children to it so we don't need to join the child paint bounds. - set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_, - device_pixel_ratio_)); + // Add some margin to the paint bounds to leave space for the shadow. + // We fill this whole region and clip children to it so we don't need to + // join the child paint bounds. + // The offset is calculated as follows: + + // .--- (kLightRadius) + // -------/ (light) + // | / + // | / + // |/ + // |O + // /| (kLightHeight) + // / | + // / | + // / | + // / | + // ------------- (layer) + // /| | + // / | | (elevation) + // A / | |B + // ------------------------------------------------ (canvas) + // --- (extent of shadow) + // + // E = lt } t = (r + w/2)/h + // } => + // r + w/2 = ht } E = (l/h)(r + w/2) + // + // Where: E = extent of shadow + // l = elevation of layer + // r = radius of the light source + // w = width of the layer + // h = light height + // t = tangent of AOB, i.e., multiplier for elevation to extent + SkRect bounds(path_.getBounds()); + // tangent for x + double tx = (kLightRadius * device_pixel_ratio_ + bounds.width() * 0.5) / + kLightHeight; + // tangent for y + double ty = (kLightRadius * device_pixel_ratio_ + bounds.height() * 0.5) / + kLightHeight; + bounds.outset(elevation_ * tx, elevation_ * ty); + set_paint_bounds(bounds); #endif // defined(OS_FUCHSIA) } } @@ -156,50 +192,6 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->restoreToCount(saveCount); } -SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkRect& bounds, - float elevation, - float pixel_ratio) { - // The shadow offset is calculated as follows: - // .--- (kLightRadius) - // -------/ (light) - // | / - // | / - // |/ - // |O - // /| (kLightHeight) - // / | - // / | - // / | - // / | - // ------------- (layer) - // /| | - // / | | (elevation) - // A / | |B - // ------------------------------------------------ (canvas) - // --- (extent of shadow) - // - // E = lt } t = (r + w/2)/h - // } => - // r + w/2 = ht } E = (l/h)(r + w/2) - // - // Where: E = extent of shadow - // l = elevation of layer - // r = radius of the light source - // w = width of the layer - // h = light height - // t = tangent of AOB, i.e., multiplier for elevation to extent - // tangent for x - double tx = - (kLightRadius * pixel_ratio + bounds.width() * 0.5) / kLightHeight; - // tangent for y - double ty = - (kLightRadius * pixel_ratio + bounds.height() * 0.5) / kLightHeight; - SkRect shadow_bounds(bounds); - shadow_bounds.outset(elevation * tx, elevation * ty); - - return shadow_bounds; -} - void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index 1b5564c3662b3..f884fe02fc5bd 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -18,10 +18,8 @@ class PhysicalShapeLayer : public ContainerLayer { float elevation, const SkPath& path, Clip clip_behavior); + ~PhysicalShapeLayer() override; - static SkRect ComputeShadowBounds(const SkRect& bounds, - float elevation, - float pixel_ratio); static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, @@ -37,21 +35,19 @@ class PhysicalShapeLayer : public ContainerLayer { void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) - float total_elevation() const { return total_elevation_; } - private: SkColor color_; SkColor shadow_color_; SkScalar device_pixel_ratio_; -#if defined(OS_FUCHSIA) - float viewport_depth_ = 0.0f; -#endif + float viewport_depth_; float elevation_ = 0.0f; float total_elevation_ = 0.0f; SkPath path_; bool isRect_; SkRRect frameRRect_; Clip clip_behavior_; + + friend class PhysicalShapeLayer_TotalElevation_Test; }; } // namespace flutter diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index f0e22593122c9..972424a2fec6d 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -4,243 +4,65 @@ #include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" namespace flutter { -namespace testing { -using PhysicalShapeLayerTest = LayerTest; - -TEST_F(PhysicalShapeLayerTest, EmptyLayer) { - auto layer = - std::make_shared(SK_ColorBLACK, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - 0.0f, // elevation - SkPath(), Clip::none); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(PhysicalShapeLayerTest, PaintBeforePreroll) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - auto mock_layer = std::make_shared(child_path, SkPaint()); - auto layer = - std::make_shared(SK_ColorBLACK, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - 0.0f, // elevation - SkPath(), Clip::none); - layer->Add(mock_layer); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { - SkPath layer_path; - layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - auto layer = - std::make_shared(SK_ColorGREEN, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - 0.0f, // elevation - layer_path, Clip::none); - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - SkPaint layer_paint; - layer_paint.setColor(SK_ColorGREEN); - layer_paint.setAntiAlias(true); - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -} - -TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) { - SkPath layer_path; - layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPath child1_path; - child1_path.addRect(4, 0, 12, 12).close(); - SkPath child2_path; - child2_path.addRect(3, 2, 5, 15).close(); - auto child1 = std::make_shared(SK_ColorRED, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - 0.0f, // elevation - child1_path, Clip::none); - auto child2 = - std::make_shared(SK_ColorBLUE, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - 0.0f, // elevation - child2_path, Clip::none); - auto layer = - std::make_shared(SK_ColorGREEN, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - 0.0f, // elevation - layer_path, Clip::none); - layer->Add(child1); - layer->Add(child2); - - SkRect child_paint_bounds; - layer->Preroll(preroll_context(), SkMatrix()); - child_paint_bounds.join(child1->paint_bounds()); - child_paint_bounds.join(child2->paint_bounds()); - EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); - EXPECT_NE(layer->paint_bounds(), child_paint_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - SkPaint layer_paint; - layer_paint.setColor(SK_ColorGREEN); - layer_paint.setAntiAlias(true); - SkPaint child1_paint; - child1_paint.setColor(SK_ColorRED); - child1_paint.setAntiAlias(true); - SkPaint child2_paint; - child2_paint.setColor(SK_ColorBLUE); - child2_paint.setAntiAlias(true); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child1_path, child1_paint}}, - MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ - child2_path, child2_paint}}})); -} - -TEST_F(PhysicalShapeLayerTest, ElevationSimple) { - constexpr float initial_elevation = 20.0f; - SkPath layer_path; - layer_path.addRect(0, 0, 8, 8).close(); - auto layer = std::make_shared( - SK_ColorGREEN, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - initial_elevation, layer_path, Clip::none); +TEST(PhysicalShapeLayer, TotalElevation) { + std::shared_ptr layers[4]; - layer->Preroll(preroll_context(), SkMatrix()); - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not do any painting there. -#if defined(OS_FUCHSIA) - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_TRUE(layer->needs_system_composite()); -#else - EXPECT_EQ(layer->paint_bounds(), - PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), - initial_elevation, 1.0f)); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); -#endif - EXPECT_EQ(layer->total_elevation(), initial_elevation); + SkColor dummy_color = 0; + SkPath dummy_path; + for (int i = 0; i < 4; i += 1) { + layers[i] = + std::make_shared(dummy_color, dummy_color, + 1.0f, // pixel ratio, + 1.0f, // depth + (float)(i + 1), // elevation + dummy_path, Clip::none); + } - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(OS_FUCHSIA) - SkPaint layer_paint; - layer_paint.setColor(SK_ColorGREEN); - layer_paint.setAntiAlias(true); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -#endif -} + layers[0]->Add(layers[1]); + layers[0]->Add(layers[2]); + layers[2]->Add(layers[3]); -TEST_F(PhysicalShapeLayerTest, ElevationComplex) { - // The layer tree should look like this: - // layers[0] +1.0f = 1.0f + const Stopwatch unused_stopwatch; + TextureRegistry unused_texture_registry; + MutatorsStack unused_stack; + PrerollContext preroll_context{ + nullptr, // raster_cache (don't consult the cache) + nullptr, // gr_context (used for the raster cache) + nullptr, // external view embedder + unused_stack, // mutator stack + nullptr, // SkColorSpace* dst_color_space + kGiantRect, // SkRect cull_rect + unused_stopwatch, // frame time (dont care) + unused_stopwatch, // engine time (dont care) + unused_texture_registry, // texture registry (not supported) + false, // checkerboard_offscreen_layers + 0.0f, // total elevation + }; + + SkMatrix identity; + identity.setIdentity(); + + layers[0]->Preroll(&preroll_context, identity); + + // It should look like this: + // layers[0] +1.0f // | \ // | \ // | \ - // | layers[2] +3.0f = 4.0f + // | layers[2] +3.0f // | | - // | layers[3] +4.0f = 8.0f + // | layers[3] +4.0f // | // | - // layers[1] + 2.0f = 3.0f - constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f}; - constexpr float total_elevations[4] = {1.0f, 3.0f, 4.0f, 8.0f}; - SkPath layer_path; - layer_path.addRect(0, 0, 80, 80).close(); - - std::shared_ptr layers[4]; - for (int i = 0; i < 4; i += 1) { - layers[i] = std::make_shared( - SK_ColorBLACK, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - initial_elevations[i], layer_path, Clip::none); - } - layers[0]->Add(layers[1]); - layers[0]->Add(layers[2]); - layers[2]->Add(layers[3]); - - layers[0]->Preroll(preroll_context(), SkMatrix()); - for (int i = 0; i < 4; i += 1) { - // On Fuchsia, the system compositor handles all elevated - // PhysicalShapeLayers and their shadows , so we do not do any painting - // there. -#if defined(OS_FUCHSIA) - EXPECT_EQ(layers[i]->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layers[i]->needs_painting()); - EXPECT_TRUE(layers[i]->needs_system_composite()); -#else - EXPECT_EQ(layers[i]->paint_bounds(), - (PhysicalShapeLayer::ComputeShadowBounds( - layer_path.getBounds(), initial_elevations[i], - 1.0f /* pixel_ratio */))); - EXPECT_TRUE(layers[i]->needs_painting()); - EXPECT_FALSE(layers[i]->needs_system_composite()); -#endif - EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]); - } - - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(OS_FUCHSIA) - SkPaint layer_paint; - layer_paint.setColor(SK_ColorBLACK); - layer_paint.setAntiAlias(true); - layers[0]->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, - MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, - MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, - MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -#endif + // layers[1] + 2.0f + EXPECT_EQ(layers[0]->total_elevation_, 1.0f); + EXPECT_EQ(layers[1]->total_elevation_, 3.0f); + EXPECT_EQ(layers[2]->total_elevation_, 4.0f); + EXPECT_EQ(layers[3]->total_elevation_, 8.0f); } -} // namespace testing } // namespace flutter diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 230b648f50e80..c4275e76c13cf 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -17,6 +17,8 @@ PictureLayer::PictureLayer(const SkPoint& offset, is_complex_(is_complex), will_change_(will_change) {} +PictureLayer::~PictureLayer() = default; + void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkPicture* sk_picture = picture(); diff --git a/flow/layers/picture_layer.h b/flow/layers/picture_layer.h index e733e7455ca6c..9c40cbef37cbd 100644 --- a/flow/layers/picture_layer.h +++ b/flow/layers/picture_layer.h @@ -19,6 +19,7 @@ class PictureLayer : public Layer { SkiaGPUObject picture, bool is_complex, bool will_change); + ~PictureLayer() override; SkPicture* picture() const { return picture_.get().get(); } diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc deleted file mode 100644 index 7c88845c61be7..0000000000000 --- a/flow/layers/picture_layer_unittests.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#define FML_USED_ON_EMBEDDER - -#include "flutter/flow/layers/picture_layer.h" - -#include "flutter/flow/testing/skia_gpu_object_layer_test.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" -#include "third_party/skia/include/core/SkPicture.h" - -#ifndef SUPPORT_FRACTIONAL_TRANSLATION -#include "flutter/flow/raster_cache.h" -#endif - -namespace flutter { -namespace testing { - -using PictureLayerTest = SkiaGPUObjectLayerTest; - -TEST_F(PictureLayerTest, InvalidPicture) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - auto layer = std::make_shared( - layer_offset, SkiaGPUObject(), false, false); - - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); -} - -TEST_F(PictureLayerTest, PaintBeforePrerollInvalidPicture) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - auto layer = std::make_shared( - layer_offset, SkiaGPUObject(), false, false); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "picture_\\.get\\(\\)"); -} - -TEST_F(PictureLayerTest, PaintBeforePreroll) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); - auto layer = std::make_shared( - layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); - - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(PictureLayerTest, EmptyLayer) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - const SkRect picture_bounds = SkRect::MakeEmpty(); - auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); - auto layer = std::make_shared( - layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(PictureLayerTest, SimplePicture) { - const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); - const SkMatrix layer_offset_matrix = - SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); - const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); - auto layer = std::make_shared( - layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), - picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); - EXPECT_EQ(layer->picture(), mock_picture.get()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - layer->Paint(paint_context()); - auto expected_draw_calls = std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, - MockCanvas::ConcatMatrixData{layer_offset_matrix}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( - layer_offset_matrix)}}, -#endif - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), - SkMatrix()}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); - EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 81541b7a0cde8..3f72993f97d66 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -11,6 +11,8 @@ PlatformViewLayer::PlatformViewLayer(const SkPoint& offset, int64_t view_id) : offset_(offset), size_(size), view_id_(view_id) {} +PlatformViewLayer::~PlatformViewLayer() = default; + void PlatformViewLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), diff --git a/flow/layers/platform_view_layer.h b/flow/layers/platform_view_layer.h index 242b3734dd3b1..7ce7ccb58a856 100644 --- a/flow/layers/platform_view_layer.h +++ b/flow/layers/platform_view_layer.h @@ -14,6 +14,7 @@ namespace flutter { class PlatformViewLayer : public Layer { public: PlatformViewLayer(const SkPoint& offset, const SkSize& size, int64_t view_id); + ~PlatformViewLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/platform_view_layer_unittests.cc b/flow/layers/platform_view_layer_unittests.cc deleted file mode 100644 index 123f9ab9925f6..0000000000000 --- a/flow/layers/platform_view_layer_unittests.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/platform_view_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using PlatformViewLayerTest = LayerTest; - -TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - const SkSize layer_size = SkSize::Make(8.0f, 8.0f); - const int64_t view_id = 0; - auto layer = - std::make_shared(layer_offset, layer_size, view_id); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_FALSE(preroll_context()->has_platform_view); - EXPECT_EQ(layer->paint_bounds(), - SkRect::MakeSize(layer_size) - .makeOffset(layer_offset.fX, layer_offset.fY)); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - - layer->Paint(paint_context()); - EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); - EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index a1590ec8c9e9a..36e7b7332aeae 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -9,9 +9,9 @@ namespace flutter { ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode) - : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) { - set_renders_to_save_layer(true); -} + : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {} + +ShaderMaskLayer::~ShaderMaskLayer() = default; void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index 7f633c0372d45..01836f4f2fb54 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -16,6 +16,7 @@ class ShaderMaskLayer : public ContainerLayer { ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode); + ~ShaderMaskLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc deleted file mode 100644 index 4194747863ff4..0000000000000 --- a/flow/layers/shader_mask_layer_unittests.cc +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/shader_mask_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" -#include "third_party/skia/include/core/SkShader.h" -#include "third_party/skia/include/effects/SkPerlinNoiseShader.h" - -namespace flutter { -namespace testing { - -using ShaderMaskLayerTest = LayerTest; - -TEST_F(ShaderMaskLayerTest, EmptyLayer) { - auto layer = - std::make_shared(nullptr, kEmptyRect, SkBlendMode::kSrc); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ShaderMaskLayerTest, PaintBeforePreroll) { - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - auto mock_layer = std::make_shared(child_path); - auto layer = - std::make_shared(nullptr, kEmptyRect, SkBlendMode::kSrc); - layer->Add(mock_layer); - - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(ShaderMaskLayerTest, EmptyFilter) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(nullptr, layer_bounds, - SkBlendMode::kSrc); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - - SkPaint filter_paint; - filter_paint.setBlendMode(SkBlendMode::kSrc); - filter_paint.setShader(nullptr); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawRectData{SkRect::MakeWH( - layer_bounds.width(), - layer_bounds.height()), - filter_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ShaderMaskLayerTest, SimpleFilter) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); - const SkPath child_path = SkPath().addRect(child_bounds); - const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto layer_filter = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_filter, layer_bounds, - SkBlendMode::kSrc); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - - SkPaint filter_paint; - filter_paint.setBlendMode(SkBlendMode::kSrc); - filter_paint.setShader(layer_filter); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawRectData{SkRect::MakeWH( - layer_bounds.width(), - layer_bounds.height()), - filter_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ShaderMaskLayerTest, MultipleChildren) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); - const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); - const SkPath child_path1 = SkPath().addRect(child_bounds); - const SkPath child_path2 = - SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); - const SkPaint child_paint1 = SkPaint(SkColors::kYellow); - const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(layer_filter, layer_bounds, - SkBlendMode::kSrc); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), children_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - - SkPaint filter_paint; - filter_paint.setBlendMode(SkBlendMode::kSrc); - filter_paint.setShader(layer_filter); - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawRectData{SkRect::MakeWH( - layer_bounds.width(), - layer_bounds.height()), - filter_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(ShaderMaskLayerTest, Nested) { - const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 7.5f, 8.5f); - const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); - const SkPath child_path1 = SkPath().addRect(child_bounds); - const SkPath child_path2 = - SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); - const SkPaint child_paint1 = SkPaint(SkColors::kYellow); - const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter1 = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); - auto layer_filter2 = - SkPerlinNoiseShader::MakeImprovedNoise(2.0f, 2.0f, 2, 2.0f); - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer1 = std::make_shared(layer_filter1, layer_bounds, - SkBlendMode::kSrc); - auto layer2 = std::make_shared(layer_filter2, layer_bounds, - SkBlendMode::kSrc); - layer2->Add(mock_layer2); - layer1->Add(mock_layer1); - layer1->Add(layer2); - - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer1->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer1->paint_bounds(), children_bounds); - EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - - SkPaint filter_paint1, filter_paint2; - filter_paint1.setBlendMode(SkBlendMode::kSrc); - filter_paint2.setBlendMode(SkBlendMode::kSrc); - filter_paint1.setShader(layer_filter1); - filter_paint2.setShader(layer_filter2); - layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, - 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(), - nullptr, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 2, - MockCanvas::DrawRectData{ - SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), - filter_paint2}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, - MockCanvas::DrawRectData{ - SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), - filter_paint1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index 848f69c8a115a..c7716dd59bc29 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -14,6 +14,8 @@ TextureLayer::TextureLayer(const SkPoint& offset, bool freeze) : offset_(offset), size_(size), texture_id_(texture_id), freeze_(freeze) {} +TextureLayer::~TextureLayer() = default; + void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); diff --git a/flow/layers/texture_layer.h b/flow/layers/texture_layer.h index 20f6c709d6107..7c04471afa0c1 100644 --- a/flow/layers/texture_layer.h +++ b/flow/layers/texture_layer.h @@ -17,6 +17,7 @@ class TextureLayer : public Layer { const SkSize& size, int64_t texture_id, bool freeze); + ~TextureLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/texture_layer_unittests.cc b/flow/layers/texture_layer_unittests.cc deleted file mode 100644 index d8a1a2bee8aa5..0000000000000 --- a/flow/layers/texture_layer_unittests.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/texture_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/flow/testing/mock_texture.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using TextureLayerTest = LayerTest; - -TEST_F(TextureLayerTest, InvalidTexture) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - const SkSize layer_size = SkSize::Make(8.0f, 8.0f); - auto layer = - std::make_shared(layer_offset, layer_size, 0, false); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), - (SkRect::MakeSize(layer_size) - .makeOffset(layer_offset.fX, layer_offset.fY))); - EXPECT_TRUE(layer->needs_painting()); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); -} - -TEST_F(TextureLayerTest, EmptyLayer) { - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - const SkSize layer_size = SkSize::Make(0.0f, 0.0f); - const int64_t texture_id = 0; - auto mock_texture = std::make_shared(texture_id); - auto layer = std::make_shared(layer_offset, layer_size, - texture_id, false); - - // Ensure the texture is located by the Layer. - preroll_context()->texture_registry.RegisterTexture(mock_texture); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_texture->paint_calls(), - std::vector({MockTexture::PaintCall{ - mock_canvas(), layer->paint_bounds(), false, nullptr}})); - EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 9513e8bc0fec6..5a7af132c68f2 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -24,6 +24,8 @@ TransformLayer::TransformLayer(const SkMatrix& transform) } } +TransformLayer::~TransformLayer() = default; + void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix child_matrix; child_matrix.setConcat(matrix, transform_); diff --git a/flow/layers/transform_layer.h b/flow/layers/transform_layer.h index a21e7d4f10c5b..f19a963ced9fe 100644 --- a/flow/layers/transform_layer.h +++ b/flow/layers/transform_layer.h @@ -14,6 +14,7 @@ namespace flutter { class TransformLayer : public ContainerLayer { public: TransformLayer(const SkMatrix& transform); + ~TransformLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/transform_layer_unittests.cc b/flow/layers/transform_layer_unittests.cc deleted file mode 100644 index 8d196db279c4b..0000000000000 --- a/flow/layers/transform_layer_unittests.cc +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/transform_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using TransformLayerTest = LayerTest; - -TEST_F(TransformLayerTest, EmptyLayer) { - auto layer = std::make_shared(SkMatrix()); // identity - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(TransformLayerTest, PaintBeforePreroll) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - auto mock_layer = std::make_shared(child_path, SkPaint()); - auto layer = std::make_shared(SkMatrix()); // identity - layer->Add(mock_layer); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(TransformLayerTest, Identity) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - auto mock_layer = std::make_shared(child_path, SkPaint()); - auto layer = std::make_shared(SkMatrix()); // identity - layer->Add(mock_layer); - - preroll_context()->cull_rect = cull_rect; - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity - EXPECT_EQ(mock_layer->parent_cull_rect(), cull_rect); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(SkMatrix())})); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path, SkPaint()}}})); -} - -TEST_F(TransformLayerTest, Simple) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - SkMatrix layer_transform = SkMatrix::MakeTrans(2.5f, 2.5f); - SkMatrix inverse_layer_transform; - EXPECT_TRUE(layer_transform.invert(&inverse_layer_transform)); - - auto mock_layer = std::make_shared(child_path, SkPaint()); - auto layer = std::make_shared(layer_transform); - layer->Add(mock_layer); - - preroll_context()->cull_rect = cull_rect; - layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), - layer_transform.mapRect(mock_layer->paint_bounds())); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_EQ(mock_layer->parent_matrix(), - SkMatrix::Concat(initial_transform, layer_transform)); - EXPECT_EQ(mock_layer->parent_cull_rect(), - inverse_layer_transform.mapRect(cull_rect)); - EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_transform)})); - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer_transform}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, SkPaint()}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(TransformLayerTest, Nested) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - SkMatrix layer1_transform = SkMatrix::MakeTrans(2.5f, 2.5f); - SkMatrix layer2_transform = SkMatrix::MakeTrans(2.5f, 2.5f); - SkMatrix inverse_layer1_transform, inverse_layer2_transform; - EXPECT_TRUE(layer1_transform.invert(&inverse_layer1_transform)); - EXPECT_TRUE(layer2_transform.invert(&inverse_layer2_transform)); - - auto mock_layer = std::make_shared(child_path, SkPaint()); - auto layer1 = std::make_shared(layer1_transform); - auto layer2 = std::make_shared(layer2_transform); - layer1->Add(layer2); - layer2->Add(mock_layer); - - preroll_context()->cull_rect = cull_rect; - layer1->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer2->paint_bounds(), - layer2_transform.mapRect(mock_layer->paint_bounds())); - EXPECT_EQ(layer1->paint_bounds(), - layer1_transform.mapRect(layer2->paint_bounds())); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_EQ( - mock_layer->parent_matrix(), - SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), - layer2_transform)); - EXPECT_EQ(mock_layer->parent_cull_rect(), - inverse_layer2_transform.mapRect( - inverse_layer1_transform.mapRect(cull_rect))); - EXPECT_EQ( - mock_layer->parent_mutators(), - std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); - - layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer1_transform}}, - MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{layer2_transform}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, SkPaint()}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -TEST_F(TransformLayerTest, NestedSeparated) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); - SkMatrix layer1_transform = SkMatrix::MakeTrans(2.5f, 2.5f); - SkMatrix layer2_transform = SkMatrix::MakeTrans(2.5f, 2.5f); - SkMatrix inverse_layer1_transform, inverse_layer2_transform; - EXPECT_TRUE(layer1_transform.invert(&inverse_layer1_transform)); - EXPECT_TRUE(layer2_transform.invert(&inverse_layer2_transform)); - - auto mock_layer1 = - std::make_shared(child_path, SkPaint(SkColors::kBlue)); - auto mock_layer2 = - std::make_shared(child_path, SkPaint(SkColors::kGreen)); - auto layer1 = std::make_shared(layer1_transform); - auto layer2 = std::make_shared(layer2_transform); - layer1->Add(mock_layer1); - layer1->Add(layer2); - layer2->Add(mock_layer2); - - preroll_context()->cull_rect = cull_rect; - layer1->Preroll(preroll_context(), initial_transform); - SkRect expected_layer1_bounds = layer2->paint_bounds(); - expected_layer1_bounds.join(mock_layer1->paint_bounds()); - layer1_transform.mapRect(&expected_layer1_bounds); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer2->paint_bounds(), - layer2_transform.mapRect(mock_layer2->paint_bounds())); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_EQ(mock_layer1->parent_matrix(), - SkMatrix::Concat(initial_transform, layer1_transform)); - EXPECT_EQ( - mock_layer2->parent_matrix(), - SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), - layer2_transform)); - EXPECT_EQ(mock_layer1->parent_cull_rect(), - inverse_layer1_transform.mapRect(cull_rect)); - EXPECT_EQ(mock_layer2->parent_cull_rect(), - inverse_layer2_transform.mapRect( - inverse_layer1_transform.mapRect(cull_rect))); - EXPECT_EQ(mock_layer1->parent_mutators(), - std::vector({Mutator(layer1_transform)})); - EXPECT_EQ( - mock_layer2->parent_mutators(), - std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); - - layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer1_transform}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, - SkPaint(SkColors::kBlue)}}, - MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{layer2_transform}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, - SkPaint(SkColors::kGreen)}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/matrix_decomposition_unittests.cc b/flow/matrix_decomposition_unittests.cc index 8aa511e4a0a97..cf96025276737 100644 --- a/flow/matrix_decomposition_unittests.cc +++ b/flow/matrix_decomposition_unittests.cc @@ -12,9 +12,6 @@ #include "flutter/flow/matrix_decomposition.h" #include "gtest/gtest.h" -namespace flutter { -namespace testing { - TEST(MatrixDecomposition, Rotation) { SkMatrix44 matrix = SkMatrix44::I(); @@ -96,8 +93,7 @@ TEST(MatrixDecomposition, Combination) { } TEST(MatrixDecomposition, ScaleFloatError) { - constexpr float scale_increment = 0.00001f; - for (float scale = 0.0001f; scale < 2.0f; scale += scale_increment) { + for (float scale = 0.0001f; scale < 2.0f; scale += 0.000001f) { SkMatrix44 matrix = SkMatrix44::I(); matrix.setScale(scale, scale, 1.0f); @@ -156,6 +152,3 @@ TEST(MatrixDecomposition, ScaleFloatError) { ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[1]); ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[2]); } - -} // namespace testing -} // namespace flutter diff --git a/flow/mutators_stack_unittests.cc b/flow/mutators_stack_unittests.cc index 1d31a81623307..97cfe9a54a7c7 100644 --- a/flow/mutators_stack_unittests.cc +++ b/flow/mutators_stack_unittests.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "flutter/flow/embedded_views.h" - #include "gtest/gtest.h" namespace flutter { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index bd83d807875f2..64f4405ebe5a0 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -3,15 +3,10 @@ // found in the LICENSE file. #include "flutter/flow/raster_cache.h" - #include "gtest/gtest.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkPictureRecorder.h" -namespace flutter { -namespace testing { -namespace { - sk_sp GetSamplePicture() { SkPictureRecorder recorder; recorder.beginRecording(SkRect::MakeWH(150, 100)); @@ -22,8 +17,6 @@ sk_sp GetSamplePicture() { return recorder.finishRecordingAsPicture(); } -} // namespace - TEST(RasterCache, SimpleInitialization) { flutter::RasterCache cache; ASSERT_TRUE(true); @@ -100,6 +93,3 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) { ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true, false)); // 5 } - -} // namespace testing -} // namespace flutter diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 2a09f982a4386..4823ec14208c5 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -54,11 +54,14 @@ class SkiaGPUObject { using SkiaObjectType = T; SkiaGPUObject() = default; + SkiaGPUObject(sk_sp object, fml::RefPtr queue) : object_(std::move(object)), queue_(std::move(queue)) { FML_DCHECK(object_); } + SkiaGPUObject(SkiaGPUObject&&) = default; + ~SkiaGPUObject() { reset(); } SkiaGPUObject& operator=(SkiaGPUObject&&) = default; diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index 9df82ad11c312..aa259a6909eec 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "flutter/flow/skia_gpu_object.h" - #include "flutter/fml/message_loop.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" @@ -14,6 +13,8 @@ namespace flutter { namespace testing { +using SkiaGpuObjectTest = flutter::testing::ThreadTest; + class TestSkObject : public SkRefCnt { public: TestSkObject(std::shared_ptr latch, @@ -21,9 +22,7 @@ class TestSkObject : public SkRefCnt { : latch_(latch), dtor_task_queue_id_(dtor_task_queue_id) {} ~TestSkObject() { - if (dtor_task_queue_id_) { - *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); - } + *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); latch_->Signal(); } @@ -32,107 +31,19 @@ class TestSkObject : public SkRefCnt { fml::TaskQueueId* dtor_task_queue_id_; }; -class SkiaGpuObjectTest : public ThreadTest { - public: - SkiaGpuObjectTest() - : unref_task_runner_(CreateNewThread()), - unref_queue_(fml::MakeRefCounted( - unref_task_runner(), - fml::TimeDelta::FromSeconds(0))), - delayed_unref_queue_(fml::MakeRefCounted( - unref_task_runner(), - fml::TimeDelta::FromSeconds(3))) { - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - } - - fml::RefPtr unref_task_runner() { - return unref_task_runner_; - } - fml::RefPtr unref_queue() { return unref_queue_; } - fml::RefPtr delayed_unref_queue() { - return delayed_unref_queue_; - } - - private: - fml::RefPtr unref_task_runner_; - fml::RefPtr unref_queue_; - fml::RefPtr delayed_unref_queue_; -}; +TEST_F(SkiaGpuObjectTest, UnrefQueue) { + fml::RefPtr task_runner = CreateNewThread(); + fml::RefPtr queue = fml::MakeRefCounted( + task_runner, fml::TimeDelta::FromSeconds(0)); -TEST_F(SkiaGpuObjectTest, QueueSimple) { std::shared_ptr latch = std::make_shared(); fml::TaskQueueId dtor_task_queue_id(0); SkRefCnt* ref_object = new TestSkObject(latch, &dtor_task_queue_id); - unref_queue()->Unref(ref_object); - latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); -} - -TEST_F(SkiaGpuObjectTest, ObjectDestructor) { - std::shared_ptr latch = - std::make_shared(); - fml::TaskQueueId dtor_task_queue_id(0); - - { - auto object = sk_make_sp(latch, &dtor_task_queue_id); - SkiaGPUObject sk_object(object, unref_queue()); - ASSERT_EQ(sk_object.get(), object); - ASSERT_EQ(dtor_task_queue_id, 0); - } - - latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); -} - -TEST_F(SkiaGpuObjectTest, ObjectReset) { - std::shared_ptr latch = - std::make_shared(); - fml::TaskQueueId dtor_task_queue_id(0); - SkiaGPUObject sk_object( - sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); - - sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); - - latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); -} - -TEST_F(SkiaGpuObjectTest, ObjectResetBeforeDestructor) { - std::shared_ptr latch = - std::make_shared(); - fml::TaskQueueId dtor_task_queue_id(0); - - { - auto object = sk_make_sp(latch, &dtor_task_queue_id); - SkiaGPUObject sk_object(object, unref_queue()); - ASSERT_EQ(sk_object.get(), object); - ASSERT_EQ(dtor_task_queue_id, 0); - - sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); - } - - latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); -} - -TEST_F(SkiaGpuObjectTest, ObjectResetTwice) { - std::shared_ptr latch = - std::make_shared(); - fml::TaskQueueId dtor_task_queue_id(0); - SkiaGPUObject sk_object( - sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); - - sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); - sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); - + queue->Unref(ref_object); latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); + ASSERT_EQ(dtor_task_queue_id, task_runner->GetTaskQueueId()); } } // namespace testing diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h deleted file mode 100644 index e38d690a2eeb7..0000000000000 --- a/flow/testing/layer_test.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLOW_TESTING_LAYER_TEST_H_ -#define FLOW_TESTING_LAYER_TEST_H_ - -#include "flutter/flow/layers/layer.h" - -#include -#include - -#include "flutter/fml/macros.h" -#include "flutter/testing/canvas_test.h" -#include "flutter/testing/mock_canvas.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/utils/SkNWayCanvas.h" - -namespace flutter { -namespace testing { - -// This fixture allows generating tests which can |Paint()| and |Preroll()| -// |Layer|'s. -// |LayerTest| is a default implementation based on |::testing::Test|. -// -// |BaseT| should be the base test type, such as |::testing::Test| below. -template -class LayerTestBase : public CanvasTestBase { - using TestT = CanvasTestBase; - - public: - LayerTestBase() - : preroll_context_({ - nullptr, /* raster_cache */ - nullptr, /* gr_context */ - nullptr, /* external_view_embedder */ - mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), - kGiantRect, /* cull_rect */ - raster_time_, ui_time_, texture_registry_, - false, /* checkerboard_offscreen_layers */ - 0.0f /* total_elevation */ - }), - paint_context_({ - TestT::mock_canvas().internal_canvas(), /* internal_nodes_canvas */ - &TestT::mock_canvas(), /* leaf_nodes_canvas */ - nullptr, /* gr_context */ - nullptr, /* external_view_embedder */ - raster_time_, ui_time_, texture_registry_, - nullptr, /* raster_cache */ - false, /* checkerboard_offscreen_layers */ - }) {} - - TextureRegistry& texture_regitry() { return texture_registry_; } - PrerollContext* preroll_context() { return &preroll_context_; } - Layer::PaintContext& paint_context() { return paint_context_; } - - private: - Stopwatch raster_time_; - Stopwatch ui_time_; - MutatorsStack mutators_stack_; - TextureRegistry texture_registry_; - - PrerollContext preroll_context_; - Layer::PaintContext paint_context_; - - FML_DISALLOW_COPY_AND_ASSIGN(LayerTestBase); -}; -using LayerTest = LayerTestBase<::testing::Test>; - -} // namespace testing -} // namespace flutter - -#endif // FLOW_TESTING_LAYER_TEST_H_ diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc deleted file mode 100644 index 1065f43054674..0000000000000 --- a/flow/testing/mock_layer.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/testing/mock_layer.h" - -namespace flutter { -namespace testing { - -MockLayer::MockLayer(SkPath path, - SkPaint paint, - bool fake_has_platform_view, - bool fake_needs_system_composite) - : fake_paint_path_(path), - fake_paint_(paint), - fake_has_platform_view_(fake_has_platform_view), - fake_needs_system_composite_(fake_needs_system_composite) {} - -void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - parent_mutators_ = context->mutators_stack; - parent_matrix_ = matrix; - parent_cull_rect_ = context->cull_rect; - parent_elevation_ = context->total_elevation; - parent_has_platform_view_ = context->has_platform_view; - - context->has_platform_view = fake_has_platform_view_; - set_paint_bounds(fake_paint_path_.getBounds()); - set_needs_system_composite(fake_needs_system_composite_); -} - -void MockLayer::Paint(PaintContext& context) const { - FML_DCHECK(needs_painting()); - - context.leaf_nodes_canvas->drawPath(fake_paint_path_, fake_paint_); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h deleted file mode 100644 index b55452a37812c..0000000000000 --- a/flow/testing/mock_layer.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLOW_TESTING_MOCK_LAYER_H_ -#define FLOW_TESTING_MOCK_LAYER_H_ - -#include "flutter/flow/layers/layer.h" - -namespace flutter { -namespace testing { - -// Mock implementation of the |Layer| interface that does nothing but paint -// the specified |path| into the canvas. It records the |PrerollContext| and -// |PaintContext| data passed in by its parent |Layer|, so the test can later -// verify the data against expected values. -class MockLayer : public Layer { - public: - MockLayer(SkPath path, - SkPaint paint = SkPaint(), - bool fake_has_platform_view = false, - bool fake_needs_system_composite = false); - - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; - void Paint(PaintContext& context) const override; - - const MutatorsStack& parent_mutators() { return parent_mutators_; } - const SkMatrix& parent_matrix() { return parent_matrix_; } - const SkRect& parent_cull_rect() { return parent_cull_rect_; } - float parent_elevation() { return parent_elevation_; } - bool parent_has_platform_view() { return parent_has_platform_view_; } - - private: - MutatorsStack parent_mutators_; - SkMatrix parent_matrix_; - SkRect parent_cull_rect_ = SkRect::MakeEmpty(); - SkPath fake_paint_path_; - SkPaint fake_paint_; - float parent_elevation_ = 0; - bool parent_has_platform_view_ = false; - bool fake_has_platform_view_ = false; - bool fake_needs_system_composite_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(MockLayer); -}; - -} // namespace testing -} // namespace flutter - -#endif // FLOW_TESTING_MOCK_LAYER_H_ diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc deleted file mode 100644 index ab2518edec108..0000000000000 --- a/flow/testing/mock_layer_unittests.cc +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/testing/mock_layer.h" - -#include "flutter/flow/testing/layer_test.h" -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -using MockLayerTest = LayerTest; - -TEST_F(MockLayerTest, PaintBeforePreroll) { - SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); - auto layer = std::make_shared(path, SkPaint()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(MockLayerTest, EmptyLayer) { - auto layer = std::make_shared(SkPath(), SkPaint()); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->paint_bounds(), SkPath().getBounds()); - - EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); -} - -TEST_F(MockLayerTest, SimpleParams) { - const SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); - const SkPaint paint = SkPaint(SkColors::kBlue); - const SkMatrix start_matrix = SkMatrix::MakeTrans(1.0f, 2.0f); - const SkMatrix scale_matrix = SkMatrix::MakeScale(0.5f, 0.5f); - const SkRect cull_rect = SkRect::MakeWH(5.0f, 5.0f); - const float parent_elevation = 5.0f; - const bool parent_has_platform_view = true; - auto layer = std::make_shared(path, paint); - - preroll_context()->mutators_stack.PushTransform(scale_matrix); - preroll_context()->cull_rect = cull_rect; - preroll_context()->total_elevation = parent_elevation; - preroll_context()->has_platform_view = parent_has_platform_view; - layer->Preroll(preroll_context(), start_matrix); - EXPECT_EQ(preroll_context()->has_platform_view, false); - EXPECT_EQ(layer->paint_bounds(), path.getBounds()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(layer->parent_mutators(), std::vector{Mutator(scale_matrix)}); - EXPECT_EQ(layer->parent_matrix(), start_matrix); - EXPECT_EQ(layer->parent_cull_rect(), cull_rect); - EXPECT_EQ(layer->parent_elevation(), parent_elevation); - EXPECT_EQ(layer->parent_has_platform_view(), parent_has_platform_view); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{path, paint}}})); -} - -TEST_F(MockLayerTest, FakePlatformView) { - auto layer = std::make_shared(SkPath(), SkPaint(), - true /* fake_has_platform_view */); - EXPECT_EQ(preroll_context()->has_platform_view, false); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(preroll_context()->has_platform_view, true); -} - -TEST_F(MockLayerTest, FakeSystemComposite) { - auto layer = std::make_shared( - SkPath(), SkPaint(), false /* fake_has_platform_view */, - true /* fake_needs_system_composite */); - EXPECT_EQ(layer->needs_system_composite(), false); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->needs_system_composite(), true); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/testing/mock_texture.cc b/flow/testing/mock_texture.cc deleted file mode 100644 index 26e49b764cdaf..0000000000000 --- a/flow/testing/mock_texture.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/testing/mock_texture.h" - -namespace flutter { -namespace testing { - -MockTexture::MockTexture(int64_t textureId) : Texture(textureId) {} - -void MockTexture::Paint(SkCanvas& canvas, - const SkRect& bounds, - bool freeze, - GrContext* context) { - paint_calls_.emplace_back(PaintCall{canvas, bounds, freeze, context}); -} - -bool operator==(const MockTexture::PaintCall& a, - const MockTexture::PaintCall& b) { - return &a.canvas == &b.canvas && a.bounds == b.bounds && - a.context == b.context && a.freeze == b.freeze; -} - -std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) { - return os << &data.canvas << " " << data.bounds << " " << data.context << " " - << data.freeze; -} - -} // namespace testing -} // namespace flutter diff --git a/flow/testing/mock_texture.h b/flow/testing/mock_texture.h deleted file mode 100644 index c5339ebb77a66..0000000000000 --- a/flow/testing/mock_texture.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/texture.h" -#include "flutter/testing/assertions_skia.h" - -#include -#include - -namespace flutter { -namespace testing { - -// Mock implementation of the |Texture| interface that does not interact with -// the GPU. It simply records the list of various calls made so the test can -// later verify them against expected data. -class MockTexture : public Texture { - public: - struct PaintCall { - SkCanvas& canvas; - SkRect bounds; - bool freeze; - GrContext* context; - }; - - explicit MockTexture(int64_t textureId); - - // Called from GPU thread. - void Paint(SkCanvas& canvas, - const SkRect& bounds, - bool freeze, - GrContext* context) override; - - void OnGrContextCreated() override { gr_context_created_ = true; } - void OnGrContextDestroyed() override { gr_context_destroyed_ = true; } - void MarkNewFrameAvailable() override {} - void OnTextureUnregistered() override { unregistered_ = true; } - - const std::vector& paint_calls() { return paint_calls_; } - bool gr_context_created() { return gr_context_created_; } - bool gr_context_destroyed() { return gr_context_destroyed_; } - bool unregistered() { return unregistered_; } - - private: - std::vector paint_calls_; - bool gr_context_created_ = false; - bool gr_context_destroyed_ = false; - bool unregistered_ = false; -}; - -extern bool operator==(const MockTexture::PaintCall& a, - const MockTexture::PaintCall& b); -extern std::ostream& operator<<(std::ostream& os, - const MockTexture::PaintCall& data); - -} // namespace testing -} // namespace flutter diff --git a/flow/testing/mock_texture_unittests.cc b/flow/testing/mock_texture_unittests.cc deleted file mode 100644 index 107eb76307928..0000000000000 --- a/flow/testing/mock_texture_unittests.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/testing/mock_texture.h" - -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -TEST(MockTextureTest, Callbacks) { - auto texture = std::make_shared(0); - - ASSERT_FALSE(texture->gr_context_created()); - texture->OnGrContextCreated(); - ASSERT_TRUE(texture->gr_context_created()); - - ASSERT_FALSE(texture->gr_context_destroyed()); - texture->OnGrContextDestroyed(); - ASSERT_TRUE(texture->gr_context_destroyed()); - - ASSERT_FALSE(texture->unregistered()); - texture->OnTextureUnregistered(); - ASSERT_TRUE(texture->unregistered()); -} - -TEST(MockTextureTest, PaintCalls) { - SkCanvas canvas; - const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); - const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); - const auto expected_paint_calls = - std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr}, - MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr}}; - auto texture = std::make_shared(0); - - texture->Paint(canvas, paint_bounds1, false, nullptr); - texture->Paint(canvas, paint_bounds2, true, nullptr); - EXPECT_EQ(texture->paint_calls(), expected_paint_calls); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/testing/skia_gpu_object_layer_test.cc b/flow/testing/skia_gpu_object_layer_test.cc deleted file mode 100644 index 1fca8ec8f3b81..0000000000000 --- a/flow/testing/skia_gpu_object_layer_test.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/testing/skia_gpu_object_layer_test.h" - -#include "flutter/fml/time/time_delta.h" - -namespace flutter { -namespace testing { - -SkiaGPUObjectLayerTest::SkiaGPUObjectLayerTest() - : unref_queue_(fml::MakeRefCounted( - GetCurrentTaskRunner(), - fml::TimeDelta::FromSeconds(0))) {} - -} // namespace testing -} // namespace flutter diff --git a/flow/testing/skia_gpu_object_layer_test.h b/flow/testing/skia_gpu_object_layer_test.h deleted file mode 100644 index d573ac0b41007..0000000000000 --- a/flow/testing/skia_gpu_object_layer_test.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ -#define FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ - -#include "flutter/flow/skia_gpu_object.h" -#include "flutter/flow/testing/layer_test.h" -#include "flutter/testing/thread_test.h" - -namespace flutter { -namespace testing { - -// This fixture allows generating tests that create |SkiaGPUObject|'s which -// are destroyed on a |SkiaUnrefQueue|. -class SkiaGPUObjectLayerTest : public LayerTestBase { - public: - SkiaGPUObjectLayerTest(); - - fml::RefPtr unref_queue() { return unref_queue_; } - - private: - fml::RefPtr unref_queue_; -}; - -} // namespace testing -} // namespace flutter - -#endif // FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ diff --git a/flow/texture.cc b/flow/texture.cc index 15c93d360366e..6f25c6df89593 100644 --- a/flow/texture.cc +++ b/flow/texture.cc @@ -6,12 +6,10 @@ namespace flutter { -Texture::Texture(int64_t id) : id_(id) {} - -Texture::~Texture() = default; - TextureRegistry::TextureRegistry() = default; +TextureRegistry::~TextureRegistry() = default; + void TextureRegistry::RegisterTexture(std::shared_ptr texture) { mapping_[texture->Id()] = texture; } @@ -38,4 +36,8 @@ std::shared_ptr TextureRegistry::GetTexture(int64_t id) { return it != mapping_.end() ? it->second : nullptr; } +Texture::Texture(int64_t id) : id_(id) {} + +Texture::~Texture() = default; + } // namespace flutter diff --git a/flow/texture.h b/flow/texture.h index 812588d382bb1..6e06445884b66 100644 --- a/flow/texture.h +++ b/flow/texture.h @@ -14,9 +14,12 @@ namespace flutter { class Texture { + protected: + Texture(int64_t id); + public: - Texture(int64_t id); // Called from UI or GPU thread. - virtual ~Texture(); // Called from GPU thread. + // Called from GPU thread. + virtual ~Texture(); // Called from GPU thread. virtual void Paint(SkCanvas& canvas, @@ -47,6 +50,7 @@ class Texture { class TextureRegistry { public: TextureRegistry(); + ~TextureRegistry(); // Called from GPU thread. void RegisterTexture(std::shared_ptr texture); diff --git a/flow/texture_unittests.cc b/flow/texture_unittests.cc index f3eb0fc0931ac..d292e3965af87 100644 --- a/flow/texture_unittests.cc +++ b/flow/texture_unittests.cc @@ -2,97 +2,44 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/testing/mock_texture.h" #include "flutter/flow/texture.h" - #include "gtest/gtest.h" namespace flutter { namespace testing { -TEST(TextureRegistryTest, UnregisterTextureCallbackTriggered) { - TextureRegistry registry; - auto mock_texture1 = std::make_shared(0); - auto mock_texture2 = std::make_shared(1); - - registry.RegisterTexture(mock_texture1); - registry.RegisterTexture(mock_texture2); - ASSERT_EQ(registry.GetTexture(0), mock_texture1); - ASSERT_EQ(registry.GetTexture(1), mock_texture2); - ASSERT_FALSE(mock_texture1->unregistered()); - ASSERT_FALSE(mock_texture2->unregistered()); - - registry.UnregisterTexture(0); - ASSERT_EQ(registry.GetTexture(0), nullptr); - ASSERT_TRUE(mock_texture1->unregistered()); - ASSERT_FALSE(mock_texture2->unregistered()); - - registry.UnregisterTexture(1); - ASSERT_EQ(registry.GetTexture(1), nullptr); - ASSERT_TRUE(mock_texture1->unregistered()); - ASSERT_TRUE(mock_texture2->unregistered()); -} - -TEST(TextureRegistryTest, GrContextCallbackTriggered) { - TextureRegistry registry; - auto mock_texture1 = std::make_shared(0); - auto mock_texture2 = std::make_shared(1); - - registry.RegisterTexture(mock_texture1); - registry.RegisterTexture(mock_texture2); - ASSERT_FALSE(mock_texture1->gr_context_created()); - ASSERT_FALSE(mock_texture2->gr_context_created()); - ASSERT_FALSE(mock_texture1->gr_context_destroyed()); - ASSERT_FALSE(mock_texture2->gr_context_destroyed()); +class MockTexture : public Texture { + public: + MockTexture(int64_t textureId) : Texture(textureId) {} - registry.OnGrContextCreated(); - ASSERT_TRUE(mock_texture1->gr_context_created()); - ASSERT_TRUE(mock_texture2->gr_context_created()); + ~MockTexture() override = default; - registry.UnregisterTexture(0); - registry.OnGrContextDestroyed(); - ASSERT_FALSE(mock_texture1->gr_context_destroyed()); - ASSERT_TRUE(mock_texture2->gr_context_created()); -} + // Called from GPU thread. + void Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) override {} -TEST(TextureRegistryTest, RegisterTextureTwice) { - TextureRegistry registry; - auto mock_texture1 = std::make_shared(0); - auto mock_texture2 = std::make_shared(0); + void OnGrContextCreated() override {} - registry.RegisterTexture(mock_texture1); - ASSERT_EQ(registry.GetTexture(0), mock_texture1); - registry.RegisterTexture(mock_texture2); - ASSERT_EQ(registry.GetTexture(0), mock_texture2); - ASSERT_FALSE(mock_texture1->unregistered()); - ASSERT_FALSE(mock_texture2->unregistered()); - - registry.UnregisterTexture(0); - ASSERT_EQ(registry.GetTexture(0), nullptr); - ASSERT_FALSE(mock_texture1->unregistered()); - ASSERT_TRUE(mock_texture2->unregistered()); -} + void OnGrContextDestroyed() override {} -TEST(TextureRegistryTest, ReuseSameTextureSlot) { - TextureRegistry registry; - auto mock_texture1 = std::make_shared(0); - auto mock_texture2 = std::make_shared(0); + void MarkNewFrameAvailable() override {} - registry.RegisterTexture(mock_texture1); - ASSERT_EQ(registry.GetTexture(0), mock_texture1); + void OnTextureUnregistered() override { unregistered_ = true; } - registry.UnregisterTexture(0); - ASSERT_EQ(registry.GetTexture(0), nullptr); - ASSERT_TRUE(mock_texture1->unregistered()); - ASSERT_FALSE(mock_texture2->unregistered()); + bool unregistered() { return unregistered_; } - registry.RegisterTexture(mock_texture2); - ASSERT_EQ(registry.GetTexture(0), mock_texture2); + private: + bool unregistered_ = false; +}; - registry.UnregisterTexture(0); - ASSERT_EQ(registry.GetTexture(0), nullptr); - ASSERT_TRUE(mock_texture1->unregistered()); - ASSERT_TRUE(mock_texture2->unregistered()); +TEST(TextureRegistry, UnregisterTextureCallbackTriggered) { + TextureRegistry textureRegistry; + std::shared_ptr mockTexture = std::make_shared(0); + textureRegistry.RegisterTexture(mockTexture); + textureRegistry.UnregisterTexture(0); + ASSERT_TRUE(mockTexture->unregistered()); } } // namespace testing diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 94c22c5d9a673..408942435ab83 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -2,11 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/fuchsia/sdk.gni") +import("$flutter_root/testing/testing.gni") + if (is_fuchsia) { - import("//build/fuchsia/sdk.gni") import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") } -import("$flutter_root/testing/testing.gni") source_set("fml") { sources = [ diff --git a/runtime/runtime_test.cc b/runtime/runtime_test.cc index 295d3daf2bc0e..455a52c95a67a 100644 --- a/runtime/runtime_test.cc +++ b/runtime/runtime_test.cc @@ -11,10 +11,9 @@ namespace flutter { namespace testing { RuntimeTest::RuntimeTest() - : native_resolver_(std::make_shared()), - assets_dir_(fml::OpenDirectory(GetFixturesPath(), - false, - fml::FilePermission::kRead)) {} + : native_resolver_(std::make_shared()) {} + +RuntimeTest::~RuntimeTest() = default; void RuntimeTest::SetSnapshotsAndAssets(Settings& settings) { if (!assets_dir_.is_valid()) { @@ -68,6 +67,19 @@ Settings RuntimeTest::CreateSettingsForFixture() { return settings; } +// |testing::ThreadTest| +void RuntimeTest::SetUp() { + assets_dir_ = + fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); + ThreadTest::SetUp(); +} + +// |testing::ThreadTest| +void RuntimeTest::TearDown() { + ThreadTest::TearDown(); + assets_dir_.reset(); +} + void RuntimeTest::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_resolver_->AddNativeCallback(std::move(name), callback); diff --git a/runtime/runtime_test.h b/runtime/runtime_test.h index abce2e94970f9..8c60a6e858a6c 100644 --- a/runtime/runtime_test.h +++ b/runtime/runtime_test.h @@ -19,17 +19,24 @@ class RuntimeTest : public ThreadTest { public: RuntimeTest(); + ~RuntimeTest(); + Settings CreateSettingsForFixture(); void AddNativeCallback(std::string name, Dart_NativeFunction callback); - private: - void SetSnapshotsAndAssets(Settings& settings); + protected: + // |testing::ThreadTest| + void SetUp() override; - std::shared_ptr native_resolver_; + // |testing::ThreadTest| + void TearDown() override; + + private: fml::UniqueFD assets_dir_; + std::shared_ptr native_resolver_; - FML_DISALLOW_COPY_AND_ASSIGN(RuntimeTest); + void SetSnapshotsAndAssets(Settings& settings); }; } // namespace testing diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index fa25b0d7933cf..11fdd3f6ee6ad 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -297,8 +297,7 @@ RasterStatus Rasterizer::DoDraw( RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { FML_DCHECK(surface_); - auto frame = surface_->AcquireFrame(layer_tree.frame_size(), - layer_tree.root_needs_screen_readback()); + auto frame = surface_->AcquireFrame(layer_tree.frame_size()); if (frame == nullptr) { return RasterStatus::kFailed; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 97a382dcacbbd..ed7f1febaf51c 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -19,13 +19,9 @@ namespace flutter { namespace testing { ShellTest::ShellTest() - : native_resolver_(std::make_shared()), - thread_host_("io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform | ThreadHost::Type::IO | - ThreadHost::Type::UI | ThreadHost::Type::GPU), - assets_dir_(fml::OpenDirectory(GetFixturesPath(), - false, - fml::FilePermission::kRead)) {} + : native_resolver_(std::make_shared()) {} + +ShellTest::~ShellTest() = default; void ShellTest::SendEnginePlatformMessage( Shell* shell, @@ -229,10 +225,10 @@ Settings ShellTest::CreateSettingsForFixture() { TaskRunners ShellTest::GetTaskRunnersForFixture() { return { "test", - thread_host_.platform_thread->GetTaskRunner(), // platform - thread_host_.gpu_thread->GetTaskRunner(), // gpu - thread_host_.ui_thread->GetTaskRunner(), // ui - thread_host_.io_thread->GetTaskRunner() // io + thread_host_->platform_thread->GetTaskRunner(), // platform + thread_host_->gpu_thread->GetTaskRunner(), // gpu + thread_host_->ui_thread->GetTaskRunner(), // ui + thread_host_->io_thread->GetTaskRunner() // io }; } @@ -271,6 +267,24 @@ void ShellTest::DestroyShell(std::unique_ptr shell, latch.Wait(); } +// |testing::ThreadTest| +void ShellTest::SetUp() { + ThreadTest::SetUp(); + assets_dir_ = + fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); + thread_host_ = std::make_unique( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI | + ThreadHost::Type::GPU); +} + +// |testing::ThreadTest| +void ShellTest::TearDown() { + ThreadTest::TearDown(); + assets_dir_.reset(); + thread_host_.reset(); +} + void ShellTest::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_resolver_->AddNativeCallback(std::move(name), callback); diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 0ae092a1c91bf..270ea812dbf8a 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -26,6 +26,8 @@ class ShellTest : public ThreadTest { public: ShellTest(); + ~ShellTest(); + Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); @@ -76,14 +78,19 @@ class ShellTest : public ThreadTest { // is unpredictive. static int UnreportedTimingsCount(Shell* shell); - private: - void SetSnapshotsAndAssets(Settings& settings); + protected: + // |testing::ThreadTest| + void SetUp() override; - std::shared_ptr native_resolver_; - ThreadHost thread_host_; + // |testing::ThreadTest| + void TearDown() override; + + private: fml::UniqueFD assets_dir_; + std::shared_ptr native_resolver_; + std::unique_ptr thread_host_; - FML_DISALLOW_COPY_AND_ASSIGN(ShellTest); + void SetSnapshotsAndAssets(Settings& settings); }; class ShellTestVsyncClock { diff --git a/shell/common/surface.h b/shell/common/surface.h index 661f21f04cb05..14f898fad3fe1 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -50,9 +50,7 @@ class Surface { virtual bool IsValid() = 0; - virtual std::unique_ptr AcquireFrame( - const SkISize& size, - const bool needs_readback) = 0; + virtual std::unique_ptr AcquireFrame(const SkISize& size) = 0; virtual SkMatrix GetRootTransformation() const = 0; diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index ac81687f84037..5f30a48375d33 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -193,17 +193,12 @@ static sk_sp CreateOffscreenSurface(GrContext* context, &surface_props); } -bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size, - const bool needs_readback) { - bool needs_offscreen = delegate_->UseOffscreenSurface(needs_readback); +bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { if (onscreen_surface_ != nullptr && size == SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height())) { // Surface size appears unchanged. So bail. - bool has_offscreen = offscreen_surface_ != nullptr; - if (needs_offscreen == has_offscreen) { - return true; - } + return true; } // We need to do some updates. @@ -233,7 +228,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size, return false; } - if (needs_offscreen) { + if (delegate_->UseOffscreenSurface()) { offscreen_surface = CreateOffscreenSurface(context_.get(), size); if (offscreen_surface == nullptr) { FML_LOG(ERROR) << "Could not create offscreen surface."; @@ -253,9 +248,7 @@ SkMatrix GPUSurfaceGL::GetRootTransformation() const { } // |Surface| -std::unique_ptr GPUSurfaceGL::AcquireFrame( - const SkISize& size, - const bool needs_readback) { +std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { if (delegate_ == nullptr) { return nullptr; } @@ -278,7 +271,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame( const auto root_surface_transformation = GetRootTransformation(); sk_sp surface = - AcquireRenderSurface(size, root_surface_transformation, needs_readback); + AcquireRenderSurface(size, root_surface_transformation); if (surface == nullptr) { return nullptr; @@ -342,15 +335,14 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { sk_sp GPUSurfaceGL::AcquireRenderSurface( const SkISize& untransformed_size, - const SkMatrix& root_surface_transformation, - const bool needs_readback) { + const SkMatrix& root_surface_transformation) { const auto transformed_rect = root_surface_transformation.mapRect( SkRect::MakeWH(untransformed_size.width(), untransformed_size.height())); const auto transformed_size = SkISize::Make(transformed_rect.width(), transformed_rect.height()); - if (!CreateOrUpdateSurfaces(transformed_size, needs_readback)) { + if (!CreateOrUpdateSurfaces(transformed_size)) { return nullptr; } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index b7a26e1a057b7..97325569bfd16 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -32,9 +32,7 @@ class GPUSurfaceGL : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame( - const SkISize& size, - const bool needs_readback) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; @@ -62,12 +60,11 @@ class GPUSurfaceGL : public Surface { bool valid_ = false; fml::WeakPtrFactory weak_factory_; - bool CreateOrUpdateSurfaces(const SkISize& size, const bool needs_readback); + bool CreateOrUpdateSurfaces(const SkISize& size); sk_sp AcquireRenderSurface( const SkISize& untransformed_size, - const SkMatrix& root_surface_transformation, - const bool needs_readback); + const SkMatrix& root_surface_transformation); bool PresentSurface(SkCanvas* canvas); diff --git a/shell/gpu/gpu_surface_gl_delegate.cc b/shell/gpu/gpu_surface_gl_delegate.cc index 89c660688b3cc..1ef969fe8acc5 100644 --- a/shell/gpu/gpu_surface_gl_delegate.cc +++ b/shell/gpu/gpu_surface_gl_delegate.cc @@ -12,8 +12,7 @@ bool GPUSurfaceGLDelegate::GLContextFBOResetAfterPresent() const { return false; } -bool GPUSurfaceGLDelegate::UseOffscreenSurface( - const bool needs_readback) const { +bool GPUSurfaceGLDelegate::UseOffscreenSurface() const { return false; } diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 7a222c3567909..dfe0ce7f468db 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -36,10 +36,8 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // subsequent frames. virtual bool GLContextFBOResetAfterPresent() const; - // Create an offscreen surface to render into before onscreen composition - // based on whether or not the frame will perform any operations that will - // require readback from the rendering target. - virtual bool UseOffscreenSurface(const bool needs_readback) const; + // Create an offscreen surface to render into before onscreen composition. + virtual bool UseOffscreenSurface() const; // A transformation applied to the onscreen surface before the canvas is // flushed. diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index 30f8fe2fa35e1..fc6b0964766ce 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -37,8 +37,7 @@ class GPUSurfaceMetal : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size, - const bool needs_readback) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index bc71337dcb94f..81abf740d48f6 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -83,8 +83,7 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& size, - const bool needs_readback) { +std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& size) { if (!IsValid()) { FML_LOG(ERROR) << "Metal surface was invalid."; return nullptr; diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index 7bec87eed1948..346857ac47e6a 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -24,8 +24,7 @@ bool GPUSurfaceSoftware::IsValid() { // |Surface| std::unique_ptr GPUSurfaceSoftware::AcquireFrame( - const SkISize& logical_size, - const bool needs_readback) { + const SkISize& logical_size) { // TODO(38466): Refactor GPU surface APIs take into account the fact that an // external view embedder may want to render to the root surface. if (!render_to_surface_) { diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index 66b8b90500463..af4d1cb3c107f 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -23,9 +23,7 @@ class GPUSurfaceSoftware : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame( - const SkISize& size, - const bool needs_readback) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index 7a494849bdfcf..fbc9696b15d52 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -22,8 +22,7 @@ bool GPUSurfaceVulkan::IsValid() { // |Surface| std::unique_ptr GPUSurfaceVulkan::AcquireFrame( - const SkISize& size, - const bool needs_readback) { + const SkISize& size) { auto surface = window_.AcquireSurface(); if (surface == nullptr) { diff --git a/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index 58c805936017e..7c410dd526cf7 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -26,9 +26,7 @@ class GPUSurfaceVulkan : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame( - const SkISize& size, - const bool needs_readback) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 4837ef22f4a42..33ca14d9fabea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -369,7 +369,7 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { EnsureOverlayInitialized(view_id, gl_context, gr_context); - auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_, true); + auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); canvas->flush(); diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 73148f34ce9ed..c1019bb442bb0 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -48,7 +48,7 @@ class IOSSurfaceGL final : public IOSSurface, intptr_t GLContextFBO() const override; - bool UseOffscreenSurface(const bool needs_readback) const override; + bool UseOffscreenSurface() const override; // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 1130375758831..48e70e00a4e7a 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -49,12 +49,11 @@ return IsValid() ? render_target_->framebuffer() : GL_NONE; } -bool IOSSurfaceGL::UseOffscreenSurface(const bool needs_readback) const { +bool IOSSurfaceGL::UseOffscreenSurface() const { // The onscreen surface wraps a GL renderbuffer, which is extremely slow to read. // Certain filter effects require making a copy of the current destination, so we - // render to an offscreen surface, which will be much quicker to read/copy, if - // they are present. - return needs_readback; + // always render to an offscreen surface, which will be much quicker to read/copy. + return true; } bool IOSSurfaceGL::GLContextMakeCurrent() { diff --git a/shell/platform/embedder/tests/embedder_test.cc b/shell/platform/embedder/tests/embedder_test.cc index bca2e0eb5f258..cc699c26e463a 100644 --- a/shell/platform/embedder/tests/embedder_test.cc +++ b/shell/platform/embedder/tests/embedder_test.cc @@ -9,12 +9,14 @@ namespace testing { EmbedderTest::EmbedderTest() = default; +EmbedderTest::~EmbedderTest() = default; + std::string EmbedderTest::GetFixturesDirectory() const { return GetFixturesPath(); } EmbedderTestContext& EmbedderTest::GetEmbedderContext() { - // Setup the embedder context lazily instead of in the constructor because we + // Setup the embedder context lazily instead of in the SetUp method because we // don't to do all the work if the test won't end up using context. if (!embedder_context_) { embedder_context_ = @@ -23,5 +25,16 @@ EmbedderTestContext& EmbedderTest::GetEmbedderContext() { return *embedder_context_; } +// |testing::Test| +void EmbedderTest::SetUp() { + ThreadTest::SetUp(); +} + +// |testing::Test| +void EmbedderTest::TearDown() { + embedder_context_.reset(); + ThreadTest::TearDown(); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/embedder/tests/embedder_test.h b/shell/platform/embedder/tests/embedder_test.h index 15cc101ae24a6..f170a106553dc 100644 --- a/shell/platform/embedder/tests/embedder_test.h +++ b/shell/platform/embedder/tests/embedder_test.h @@ -19,6 +19,8 @@ class EmbedderTest : public ThreadTest { public: EmbedderTest(); + ~EmbedderTest() override; + std::string GetFixturesDirectory() const; EmbedderTestContext& GetEmbedderContext(); @@ -26,6 +28,12 @@ class EmbedderTest : public ThreadTest { private: std::unique_ptr embedder_context_; + // |testing::Test| + void SetUp() override; + + // |testing::Test| + void TearDown() override; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTest); }; diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index 244761694a8dc..bdfec3cd2e4d0 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -16,7 +16,6 @@ "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", - "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx index 1119b279ddd6c..912b534df93a2 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -16,7 +16,6 @@ "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", - "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index 1fda6a50f54d5..29fbbc7294cc1 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -24,8 +24,7 @@ bool Surface::IsValid() { // |flutter::Surface| std::unique_ptr Surface::AcquireFrame( - const SkISize& size, - const bool needs_readback) { + const SkISize& size) { return std::make_unique( nullptr, [](const flutter::SurfaceFrame& surface_frame, SkCanvas* canvas) { return true; }); diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index 87d6d09320c08..66220f079b3e5 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -29,8 +29,7 @@ class Surface final : public flutter::Surface { // |flutter::Surface| std::unique_ptr AcquireFrame( - const SkISize& size, - const bool needs_readback) override; + const SkISize& size) override; // |flutter::Surface| GrContext* GetContext() override; diff --git a/testing/BUILD.gn b/testing/BUILD.gn index 8bf9d7bc3922f..a1028b0f18952 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -54,11 +54,7 @@ source_set("skia") { testonly = true sources = [ - "$flutter_root/testing/assertions_skia.cc", "$flutter_root/testing/assertions_skia.h", - "$flutter_root/testing/canvas_test.h", - "$flutter_root/testing/mock_canvas.cc", - "$flutter_root/testing/mock_canvas.h", ] public_deps = [ @@ -122,8 +118,7 @@ if (current_toolchain == host_toolchain) { testonly = true sources = [ - "mock_canvas_unittests.cc", - "test_metal_surface_unittests.cc", + "$flutter_root/testing/test_metal_surface_unittests.cc", ] deps = [ diff --git a/testing/assertions_skia.cc b/testing/assertions_skia.cc deleted file mode 100644 index e8b7ce992b8a8..0000000000000 --- a/testing/assertions_skia.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/testing/assertions_skia.h" - -namespace flutter { -namespace testing { - -std::ostream& operator<<(std::ostream& os, const SkClipOp& o) { - switch (o) { - case SkClipOp::kDifference: - os << "ClipOpDifference"; - break; - case SkClipOp::kIntersect: - os << "ClipOpIntersect"; - break; -#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS - case SkClipOp::kUnion_deprecated: - os << "ClipOpUnion_deprecated"; - break; - case SkClipOp::kXOR_deprecated: - os << "ClipOpXOR_deprecated"; - break; - case SkClipOp::kReverseDifference_deprecated: - os << "ClipOpReverseDifference_deprecated"; - break; - case SkClipOp::kReplace_deprecated: - os << "ClipOpReplace_deprectaed"; - break; -#else - case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway2: - os << "ClipOpReserved2"; - break; - case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway3: - os << "ClipOpReserved3"; - break; - case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway4: - os << "ClipOpReserved4"; - break; - case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway5: - os << "ClipOpReserved5"; - break; -#endif - } - return os; -} - -std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { - os << std::endl; - os << "Scale X: " << m[SkMatrix::kMScaleX] << ", "; - os << "Skew X: " << m[SkMatrix::kMSkewX] << ", "; - os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl; - os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", "; - os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", "; - os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl; - os << "Persp X: " << m[SkMatrix::kMPersp0] << ", "; - os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", "; - os << "Persp Z: " << m[SkMatrix::kMPersp2]; - os << std::endl; - return os; -} - -std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { - os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " - << m.get(0, 3) << std::endl; - os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " - << m.get(1, 3) << std::endl; - os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " - << m.get(2, 3) << std::endl; - os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " - << m.get(3, 3); - return os; -} - -std::ostream& operator<<(std::ostream& os, const SkVector3& v) { - return os << v.x() << ", " << v.y() << ", " << v.z(); -} - -std::ostream& operator<<(std::ostream& os, const SkVector4& v) { - return os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", " - << v.fData[3]; -} - -std::ostream& operator<<(std::ostream& os, const SkRect& r) { - return os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", " - << r.fBottom; -} - -std::ostream& operator<<(std::ostream& os, const SkRRect& r) { - return os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", " - << r.rect().fRight << ", " << r.rect().fBottom; -} - -std::ostream& operator<<(std::ostream& os, const SkPath& r) { - return os << "Valid: " << r.isValid() << ", FillType: " << r.getFillType() - << ", Bounds: " << r.getBounds(); -} - -std::ostream& operator<<(std::ostream& os, const SkPoint& r) { - return os << "XY: " << r.fX << ", " << r.fY; -} - -std::ostream& operator<<(std::ostream& os, const SkISize& size) { - return os << size.width() << ", " << size.height(); -} - -std::ostream& operator<<(std::ostream& os, const SkColor4f& r) { - return os << r.fR << ", " << r.fG << ", " << r.fB << ", " << r.fA; -} - -std::ostream& operator<<(std::ostream& os, const SkPaint& r) { - return os << "Color: " << r.getColor4f() << ", Style: " << r.getStyle() - << ", AA: " << r.isAntiAlias() << ", Shader: " << r.getShader(); -} - -} // namespace testing -} // namespace flutter diff --git a/testing/assertions_skia.h b/testing/assertions_skia.h index f1eec1897c426..2b501189a23ae 100644 --- a/testing/assertions_skia.h +++ b/testing/assertions_skia.h @@ -7,31 +7,73 @@ #include -#include "third_party/skia/include/core/SkClipOp.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkMatrix44.h" -#include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint3.h" #include "third_party/skia/include/core/SkRRect.h" -namespace flutter { -namespace testing { - -extern std::ostream& operator<<(std::ostream& os, const SkClipOp& o); -extern std::ostream& operator<<(std::ostream& os, const SkMatrix& m); -extern std::ostream& operator<<(std::ostream& os, const SkMatrix44& m); -extern std::ostream& operator<<(std::ostream& os, const SkVector3& v); -extern std::ostream& operator<<(std::ostream& os, const SkVector4& v); -extern std::ostream& operator<<(std::ostream& os, const SkRect& r); -extern std::ostream& operator<<(std::ostream& os, const SkRRect& r); -extern std::ostream& operator<<(std::ostream& os, const SkPath& r); -extern std::ostream& operator<<(std::ostream& os, const SkPoint& r); -extern std::ostream& operator<<(std::ostream& os, const SkISize& size); -extern std::ostream& operator<<(std::ostream& os, const SkColor4f& r); -extern std::ostream& operator<<(std::ostream& os, const SkPaint& r); - -} // namespace testing -} // namespace flutter +//------------------------------------------------------------------------------ +// Printing +//------------------------------------------------------------------------------ + +inline std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { + os << std::endl; + os << "Scale X: " << m[SkMatrix::kMScaleX] << ", "; + os << "Skew X: " << m[SkMatrix::kMSkewX] << ", "; + os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl; + os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", "; + os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", "; + os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl; + os << "Persp X: " << m[SkMatrix::kMPersp0] << ", "; + os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", "; + os << "Persp Z: " << m[SkMatrix::kMPersp2]; + os << std::endl; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { + os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " + << m.get(0, 3) << std::endl; + os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " + << m.get(1, 3) << std::endl; + os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " + << m.get(2, 3) << std::endl; + os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " + << m.get(3, 3); + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkVector3& v) { + os << v.x() << ", " << v.y() << ", " << v.z(); + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkVector4& v) { + os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", " + << v.fData[3]; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkRect& r) { + os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", " + << r.fBottom; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkRRect& r) { + os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", " + << r.rect().fRight << ", " << r.rect().fBottom; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkPoint& r) { + os << "XY: " << r.fX << ", " << r.fY; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const SkISize& size) { + os << size.width() << ", " << size.height(); + return os; +} #endif // FLUTTER_TESTING_ASSERTIONS_SKIA_H_ diff --git a/testing/canvas_test.h b/testing/canvas_test.h deleted file mode 100644 index 80c157a305dc4..0000000000000 --- a/testing/canvas_test.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef TESTING_CANVAS_TEST_H_ -#define TESTING_CANVAS_TEST_H_ - -#include "flutter/fml/macros.h" -#include "flutter/testing/mock_canvas.h" -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -// This fixture allows creating tests that make use of a mock |SkCanvas|. -template -class CanvasTestBase : public BaseT { - public: - CanvasTestBase() = default; - - MockCanvas& mock_canvas() { return canvas_; } - - private: - MockCanvas canvas_; - - FML_DISALLOW_COPY_AND_ASSIGN(CanvasTestBase); -}; -using CanvasTest = CanvasTestBase<::testing::Test>; - -} // namespace testing -} // namespace flutter - -#endif // TESTING_CANVAS_TEST_H_ diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc deleted file mode 100644 index 6782ffdeda733..0000000000000 --- a/testing/mock_canvas.cc +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/testing/mock_canvas.h" - -#include "flutter/fml/logging.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/core/SkPicture.h" -#include "third_party/skia/include/core/SkSerialProcs.h" -#include "third_party/skia/include/core/SkSize.h" -#include "third_party/skia/include/core/SkTextBlob.h" - -namespace flutter { -namespace testing { - -constexpr SkISize kSize = SkISize::Make(64, 64); - -MockCanvas::MockCanvas() - : SkCanvasVirtualEnforcer(kSize.fWidth, kSize.fHeight), - internal_canvas_(imageInfo().width(), imageInfo().height()), - current_layer_(0) { - internal_canvas_.addCanvas(this); -} - -MockCanvas::~MockCanvas() { - EXPECT_EQ(current_layer_, 0); -} - -void MockCanvas::willSave() { - draw_calls_.emplace_back( - DrawCall{current_layer_, SaveData{current_layer_ + 1}}); - current_layer_++; // Must go here; func params order of eval is undefined -} - -SkCanvas::SaveLayerStrategy MockCanvas::getSaveLayerStrategy( - const SaveLayerRec& rec) { - // saveLayer calls this prior to running, so we use it to track saveLayer - // calls - draw_calls_.emplace_back(DrawCall{ - current_layer_, - SaveLayerData{rec.fBounds ? *rec.fBounds : SkRect(), - rec.fPaint ? *rec.fPaint : SkPaint(), - rec.fBackdrop ? sk_ref_sp(rec.fBackdrop) - : sk_sp(), - current_layer_ + 1}}); - current_layer_++; // Must go here; func params order of eval is undefined - return kNoLayer_SaveLayerStrategy; -} - -void MockCanvas::willRestore() { - FML_DCHECK(current_layer_ > 0); - - draw_calls_.emplace_back( - DrawCall{current_layer_, RestoreData{current_layer_ - 1}}); - current_layer_--; // Must go here; func params order of eval is undefined -} - -void MockCanvas::didConcat(const SkMatrix& matrix) { - draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{matrix}}); -} - -void MockCanvas::didSetMatrix(const SkMatrix& matrix) { - draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{matrix}}); -} - -void MockCanvas::onDrawTextBlob(const SkTextBlob* text, - SkScalar x, - SkScalar y, - const SkPaint& paint) { - // This duplicates existing logic in SkCanvas::onDrawPicture - // that should probably be split out so it doesn't need to be here as well. - SkRect storage; - const SkRect* bounds = nullptr; - if (paint.canComputeFastBounds()) { - storage = text->bounds().makeOffset(x, y); - SkRect tmp; - if (this->quickReject(paint.computeFastBounds(storage, &tmp))) { - return; - } - bounds = &storage; - } - - draw_calls_.emplace_back(DrawCall{ - current_layer_, DrawTextData{text ? text->serialize(SkSerialProcs{}) - : SkData::MakeUninitialized(0), - paint, SkPoint::Make(x, y)}}); -} - -void MockCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { - draw_calls_.emplace_back(DrawCall{current_layer_, DrawRectData{rect, paint}}); -} - -void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { - draw_calls_.emplace_back(DrawCall{current_layer_, DrawPathData{path, paint}}); -} - -void MockCanvas::onDrawShadowRec(const SkPath& path, - const SkDrawShadowRec& rec) { - (void)rec; // Can't use b/c Skia keeps this type anonymous. - draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}}); -} - -void MockCanvas::onDrawPicture(const SkPicture* picture, - const SkMatrix* matrix, - const SkPaint* paint) { - // This duplicates existing logic in SkCanvas::onDrawPicture - // that should probably be split out so it doesn't need to be here as well. - if (!paint || paint->canComputeFastBounds()) { - SkRect bounds = picture->cullRect(); - if (paint) { - paint->computeFastBounds(bounds, &bounds); - } - if (matrix) { - matrix->mapRect(&bounds); - } - if (this->quickReject(bounds)) { - return; - } - } - - draw_calls_.emplace_back(DrawCall{ - current_layer_, - DrawPictureData{ - picture ? picture->serialize() : SkData::MakeUninitialized(0), - paint ? *paint : SkPaint(), matrix ? *matrix : SkMatrix()}}); -} - -void MockCanvas::onClipRect(const SkRect& rect, - SkClipOp op, - ClipEdgeStyle style) { - draw_calls_.emplace_back( - DrawCall{current_layer_, ClipRectData{rect, op, style}}); -} - -void MockCanvas::onClipRRect(const SkRRect& rrect, - SkClipOp op, - ClipEdgeStyle style) { - draw_calls_.emplace_back( - DrawCall{current_layer_, ClipRRectData{rrect, op, style}}); -} - -void MockCanvas::onClipPath(const SkPath& path, - SkClipOp op, - ClipEdgeStyle style) { - draw_calls_.emplace_back( - DrawCall{current_layer_, ClipPathData{path, op, style}}); -} - -bool MockCanvas::onDoSaveBehind(const SkRect*) { - FML_DCHECK(false); - return false; -} - -void MockCanvas::onDrawAnnotation(const SkRect&, const char[], SkData*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawDrawable(SkDrawable*, const SkMatrix*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawPatch(const SkPoint[12], - const SkColor[4], - const SkPoint[4], - SkBlendMode, - const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawPaint(const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawBehind(const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawPoints(PointMode, - size_t, - const SkPoint[], - const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawRegion(const SkRegion&, const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawOval(const SkRect&, const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawArc(const SkRect&, - SkScalar, - SkScalar, - bool, - const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawBitmap(const SkBitmap&, - SkScalar, - SkScalar, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawImage(const SkImage*, - SkScalar, - SkScalar, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawBitmapRect(const SkBitmap&, - const SkRect*, - const SkRect&, - const SkPaint*, - SrcRectConstraint) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawImageRect(const SkImage*, - const SkRect*, - const SkRect&, - const SkPaint*, - SrcRectConstraint) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawImageNine(const SkImage*, - const SkIRect&, - const SkRect&, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawBitmapNine(const SkBitmap&, - const SkIRect&, - const SkRect&, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawImageLattice(const SkImage*, - const Lattice&, - const SkRect&, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawBitmapLattice(const SkBitmap&, - const Lattice&, - const SkRect&, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawVerticesObject(const SkVertices*, - const SkVertices::Bone[], - int, - SkBlendMode, - const SkPaint&) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawAtlas(const SkImage*, - const SkRSXform[], - const SkRect[], - const SkColor[], - int, - SkBlendMode, - const SkRect*, - const SkPaint*) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawEdgeAAQuad(const SkRect&, - const SkPoint[4], - QuadAAFlags, - const SkColor4f&, - SkBlendMode) { - FML_DCHECK(false); -} - -void MockCanvas::onDrawEdgeAAImageSet(const ImageSetEntry[], - int, - const SkPoint[], - const SkMatrix[], - const SkPaint*, - SrcRectConstraint) { - FML_DCHECK(false); -} - -void MockCanvas::onClipRegion(const SkRegion&, SkClipOp) { - FML_DCHECK(false); -} - -bool operator==(const MockCanvas::SaveData& a, const MockCanvas::SaveData& b) { - return a.save_to_layer == b.save_to_layer; -} - -std::ostream& operator<<(std::ostream& os, const MockCanvas::SaveData& data) { - return os << data.save_to_layer; -} - -bool operator==(const MockCanvas::SaveLayerData& a, - const MockCanvas::SaveLayerData& b) { - return a.save_bounds == b.save_bounds && a.restore_paint == b.restore_paint && - a.backdrop_filter == b.backdrop_filter && - a.save_to_layer == b.save_to_layer; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::SaveLayerData& data) { - return os << data.save_bounds << " " << data.restore_paint << " " - << data.backdrop_filter << " " << data.save_to_layer; -} - -bool operator==(const MockCanvas::RestoreData& a, - const MockCanvas::RestoreData& b) { - return a.restore_to_layer == b.restore_to_layer; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::RestoreData& data) { - return os << data.restore_to_layer; -} - -bool operator==(const MockCanvas::ConcatMatrixData& a, - const MockCanvas::ConcatMatrixData& b) { - return a.matrix == b.matrix; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::ConcatMatrixData& data) { - return os << data.matrix; -} - -bool operator==(const MockCanvas::SetMatrixData& a, - const MockCanvas::SetMatrixData& b) { - return a.matrix == b.matrix; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::SetMatrixData& data) { - return os << data.matrix; -} - -bool operator==(const MockCanvas::DrawRectData& a, - const MockCanvas::DrawRectData& b) { - return a.rect == b.rect && a.paint == b.paint; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawRectData& data) { - return os << data.rect << " " << data.paint; -} - -bool operator==(const MockCanvas::DrawPathData& a, - const MockCanvas::DrawPathData& b) { - return a.path == b.path && a.paint == b.paint; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawPathData& data) { - return os << data.path << " " << data.paint; -} - -bool operator==(const MockCanvas::DrawTextData& a, - const MockCanvas::DrawTextData& b) { - return a.serialized_text->equals(b.serialized_text.get()) && - a.paint == b.paint && a.offset == b.offset; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawTextData& data) { - return os << data.serialized_text << " " << data.paint << " " << data.offset; -} - -bool operator==(const MockCanvas::DrawPictureData& a, - const MockCanvas::DrawPictureData& b) { - return a.serialized_picture->equals(b.serialized_picture.get()) && - a.paint == b.paint && a.matrix == b.matrix; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawPictureData& data) { - return os << data.serialized_picture << " " << data.paint << " " - << data.matrix; -} - -bool operator==(const MockCanvas::DrawShadowData& a, - const MockCanvas::DrawShadowData& b) { - return a.path == b.path; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawShadowData& data) { - return os << data.path; -} - -bool operator==(const MockCanvas::ClipRectData& a, - const MockCanvas::ClipRectData& b) { - return a.rect == b.rect && a.clip_op == b.clip_op && a.style == b.style; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::ClipRectData& data) { - return os << data.rect << " " << data.clip_op << " " << data.style; -} - -bool operator==(const MockCanvas::ClipRRectData& a, - const MockCanvas::ClipRRectData& b) { - return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::ClipRRectData& data) { - return os << data.rrect << " " << data.clip_op << " " << data.style; -} - -bool operator==(const MockCanvas::ClipPathData& a, - const MockCanvas::ClipPathData& b) { - return a.path == b.path && a.clip_op == b.clip_op && a.style == b.style; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::ClipPathData& data) { - return os << data.path << " " << data.clip_op << " " << data.style; -} - -std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawCallData& data) { - std::visit([&os](auto& d) { os << d; }, data); - return os; -} - -bool operator==(const MockCanvas::DrawCall& a, const MockCanvas::DrawCall& b) { - return a.layer == b.layer && a.data == b.data; -} - -std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawCall& draw) { - return os << "[Layer: " << draw.layer << ", Data: " << draw.data << "]"; -} - -} // namespace testing -} // namespace flutter diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h deleted file mode 100644 index cc4c2b11a9c2e..0000000000000 --- a/testing/mock_canvas.h +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef TESTING_MOCK_CANVAS_H_ -#define TESTING_MOCK_CANVAS_H_ - -#include -#include -#include - -#include "flutter/testing/assertions_skia.h" -#include "gtest/gtest.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" -#include "third_party/skia/include/core/SkClipOp.h" -#include "third_party/skia/include/core/SkData.h" -#include "third_party/skia/include/core/SkImageFilter.h" -#include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/core/SkRRect.h" -#include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/utils/SkNWayCanvas.h" - -namespace flutter { -namespace testing { - -static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); - -// Mock |SkCanvas|, useful for writing tests that use Skia but do not interact -// with the GPU. -// -// The |MockCanvas| stores a list of |DrawCall| that the test can later verify -// against the expected list of primitives to be drawn. -class MockCanvas : public SkCanvasVirtualEnforcer { - public: - using SkCanvas::kHard_ClipEdgeStyle; - using SkCanvas::kSoft_ClipEdgeStyle; - - struct SaveData { - int save_to_layer; - }; - - struct SaveLayerData { - SkRect save_bounds; - SkPaint restore_paint; - sk_sp backdrop_filter; - int save_to_layer; - }; - - struct RestoreData { - int restore_to_layer; - }; - - struct ConcatMatrixData { - SkMatrix matrix; - }; - - struct SetMatrixData { - SkMatrix matrix; - }; - - struct DrawRectData { - SkRect rect; - SkPaint paint; - }; - - struct DrawPathData { - SkPath path; - SkPaint paint; - }; - - struct DrawTextData { - sk_sp serialized_text; - SkPaint paint; - SkPoint offset; - }; - - struct DrawPictureData { - sk_sp serialized_picture; - SkPaint paint; - SkMatrix matrix; - }; - - struct DrawShadowData { - SkPath path; - }; - - struct ClipRectData { - SkRect rect; - SkClipOp clip_op; - ClipEdgeStyle style; - }; - - struct ClipRRectData { - SkRRect rrect; - SkClipOp clip_op; - ClipEdgeStyle style; - }; - - struct ClipPathData { - SkPath path; - SkClipOp clip_op; - ClipEdgeStyle style; - }; - - // Discriminated union of all the different |DrawCall| types. It is roughly - // equivalent to the different methods in |SkCanvas|' public API. - using DrawCallData = std::variant; - - // A single call made against this canvas. - struct DrawCall { - int layer; - DrawCallData data; - }; - - MockCanvas(); - ~MockCanvas() override; - - SkNWayCanvas* internal_canvas() { return &internal_canvas_; } - - const std::vector& draw_calls() const { return draw_calls_; } - - protected: - // Save/restore/set operations that we track. - void willSave() override; - SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; - void willRestore() override; - void didRestore() override {} - void didConcat(const SkMatrix& matrix) override; - void didSetMatrix(const SkMatrix& matrix) override; - - // Draw and clip operations that we track. - void onDrawRect(const SkRect& rect, const SkPaint& paint) override; - void onDrawPath(const SkPath& path, const SkPaint& paint) override; - void onDrawTextBlob(const SkTextBlob* text, - SkScalar x, - SkScalar y, - const SkPaint& paint) override; - void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override; - void onDrawPicture(const SkPicture* picture, - const SkMatrix* matrix, - const SkPaint* paint) override; - void onClipRect(const SkRect& rect, - SkClipOp op, - ClipEdgeStyle style) override; - void onClipRRect(const SkRRect& rrect, - SkClipOp op, - ClipEdgeStyle style) override; - void onClipPath(const SkPath& path, - SkClipOp op, - ClipEdgeStyle style) override; - - // Operations that we don't track. - bool onDoSaveBehind(const SkRect*) override; - void onDrawAnnotation(const SkRect&, const char[], SkData*) override; - void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; - void onDrawDrawable(SkDrawable*, const SkMatrix*) override; - void onDrawPatch(const SkPoint[12], - const SkColor[4], - const SkPoint[4], - SkBlendMode, - const SkPaint&) override; - void onDrawPaint(const SkPaint&) override; - void onDrawBehind(const SkPaint&) override; - void onDrawPoints(PointMode, - size_t, - const SkPoint[], - const SkPaint&) override; - void onDrawRegion(const SkRegion&, const SkPaint&) override; - void onDrawOval(const SkRect&, const SkPaint&) override; - void onDrawArc(const SkRect&, - SkScalar, - SkScalar, - bool, - const SkPaint&) override; - void onDrawRRect(const SkRRect&, const SkPaint&) override; - void onDrawBitmapRect(const SkBitmap&, - const SkRect*, - const SkRect&, - const SkPaint*, - SrcRectConstraint) override; - void onDrawImage(const SkImage* image, - SkScalar x, - SkScalar y, - const SkPaint* paint) override; - void onDrawImageRect(const SkImage*, - const SkRect*, - const SkRect&, - const SkPaint*, - SrcRectConstraint) override; - void onDrawImageNine(const SkImage*, - const SkIRect&, - const SkRect&, - const SkPaint*) override; - void onDrawBitmap(const SkBitmap& bitmap, - SkScalar x, - SkScalar y, - const SkPaint* paint) override; - void onDrawBitmapNine(const SkBitmap&, - const SkIRect&, - const SkRect&, - const SkPaint*) override; - void onDrawImageLattice(const SkImage*, - const Lattice&, - const SkRect&, - const SkPaint*) override; - void onDrawBitmapLattice(const SkBitmap&, - const Lattice&, - const SkRect&, - const SkPaint*) override; - void onDrawVerticesObject(const SkVertices*, - const SkVertices::Bone[], - int, - SkBlendMode, - const SkPaint&) override; - void onDrawAtlas(const SkImage*, - const SkRSXform[], - const SkRect[], - const SkColor[], - int, - SkBlendMode, - const SkRect*, - const SkPaint*) override; - void onDrawEdgeAAQuad(const SkRect&, - const SkPoint[4], - QuadAAFlags, - const SkColor4f&, - SkBlendMode) override; - void onDrawEdgeAAImageSet(const ImageSetEntry[], - int, - const SkPoint[], - const SkMatrix[], - const SkPaint*, - SrcRectConstraint) override; - void onClipRegion(const SkRegion&, SkClipOp) override; - - private: - SkNWayCanvas internal_canvas_; - - std::vector draw_calls_; - int current_layer_; -}; - -extern bool operator==(const MockCanvas::SaveData& a, - const MockCanvas::SaveData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::SaveData& data); -extern bool operator==(const MockCanvas::SaveLayerData& a, - const MockCanvas::SaveLayerData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::SaveLayerData& data); -extern bool operator==(const MockCanvas::RestoreData& a, - const MockCanvas::RestoreData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::RestoreData& data); -extern bool operator==(const MockCanvas::ConcatMatrixData& a, - const MockCanvas::ConcatMatrixData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::ConcatMatrixData& data); -extern bool operator==(const MockCanvas::SetMatrixData& a, - const MockCanvas::SetMatrixData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::SetMatrixData& data); -extern bool operator==(const MockCanvas::DrawRectData& a, - const MockCanvas::DrawRectData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawRectData& data); -extern bool operator==(const MockCanvas::DrawPathData& a, - const MockCanvas::DrawPathData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawPathData& data); -extern bool operator==(const MockCanvas::DrawTextData& a, - const MockCanvas::DrawTextData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawTextData& data); -extern bool operator==(const MockCanvas::DrawPictureData& a, - const MockCanvas::DrawPictureData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawPictureData& data); -extern bool operator==(const MockCanvas::DrawShadowData& a, - const MockCanvas::DrawShadowData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawShadowData& data); -extern bool operator==(const MockCanvas::ClipRectData& a, - const MockCanvas::ClipRectData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::ClipRectData& data); -extern bool operator==(const MockCanvas::ClipRRectData& a, - const MockCanvas::ClipRRectData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::ClipRRectData& data); -extern bool operator==(const MockCanvas::ClipPathData& a, - const MockCanvas::ClipPathData& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::ClipPathData& data); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawCallData& data); -extern bool operator==(const MockCanvas::DrawCall& a, - const MockCanvas::DrawCall& b); -extern std::ostream& operator<<(std::ostream& os, - const MockCanvas::DrawCall& draw); - -} // namespace testing -} // namespace flutter - -#endif // TESTING_MOCK_CANVAS_H_ diff --git a/testing/mock_canvas_unittests.cc b/testing/mock_canvas_unittests.cc deleted file mode 100644 index bab2fa43edd99..0000000000000 --- a/testing/mock_canvas_unittests.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/testing/mock_canvas.h" - -#include "flutter/testing/canvas_test.h" -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -using MockCanvasTest = CanvasTest; - -TEST_F(MockCanvasTest, DrawCalls) { - const SkRect rect = SkRect::MakeWH(5.0f, 5.0f); - const SkPaint paint = SkPaint(SkColors::kGreen); - const auto expected_draw_calls = std::vector{ - MockCanvas::DrawCall{0, MockCanvas::DrawRectData{rect, paint}}}; - - mock_canvas().drawRect(rect, paint); - EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); -} - -TEST_F(MockCanvasTest, InvalidDrawCalls) { - EXPECT_DEATH_IF_SUPPORTED(mock_canvas().drawRRect(SkRRect(), SkPaint()), ""); -} - -} // namespace testing -} // namespace flutter diff --git a/testing/thread_test.cc b/testing/thread_test.cc index 2f67b6ee18368..88415169a3c70 100644 --- a/testing/thread_test.cc +++ b/testing/thread_test.cc @@ -8,16 +8,18 @@ namespace flutter { namespace testing { -namespace { -fml::RefPtr GetDefaultTaskRunner() { +// |testing::Test| +void ThreadTest::SetUp() { fml::MessageLoop::EnsureInitializedForCurrentThread(); - return fml::MessageLoop::GetCurrent().GetTaskRunner(); + current_task_runner_ = fml::MessageLoop::GetCurrent().GetTaskRunner(); } -} // namespace - -ThreadTest::ThreadTest() : current_task_runner_(GetDefaultTaskRunner()) {} +// |testing::Test| +void ThreadTest::TearDown() { + current_task_runner_ = nullptr; + extra_threads_.clear(); +} fml::RefPtr ThreadTest::GetCurrentTaskRunner() { return current_task_runner_; diff --git a/testing/thread_test.h b/testing/thread_test.h index 4a7d60fb0312c..8c55dbf80ce68 100644 --- a/testing/thread_test.h +++ b/testing/thread_test.h @@ -21,16 +21,14 @@ namespace testing { /// @brief A fixture that creates threads with running message loops that /// are terminated when the test is done (the threads are joined /// then as well). While this fixture may be used on it's own, it is -/// often sub-classed by other fixtures whose functioning requires +/// often sub-classed but other fixtures whose functioning requires /// threads to be created as necessary. /// class ThreadTest : public ::testing::Test { public: - ThreadTest(); - //---------------------------------------------------------------------------- /// @brief Get the task runner for the thread that the current unit-test - /// is running on. This creates a message loop as necessary. + /// is running on. The creates a message loop is necessary. /// /// @attention Unlike all other threads and task runners, this task runner is /// shared by all tests running in the process. Tests must ensure @@ -58,11 +56,16 @@ class ThreadTest : public ::testing::Test { /// fml::RefPtr CreateNewThread(std::string name = ""); + protected: + // |testing::Test| + void SetUp() override; + + // |testing::Test| + void TearDown() override; + private: fml::RefPtr current_task_runner_; std::vector> extra_threads_; - - FML_DISALLOW_COPY_AND_ASSIGN(ThreadTest); }; } // namespace testing From ea228f2f8330f2a0740a53c26ae9b2b8bf63dee7 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Tue, 3 Dec 2019 12:24:19 -0800 Subject: [PATCH 307/591] Revert "Fix fml_unittests (#14062)" (#14087) This reverts commit 6c605f8a9624a99573c6801395f03bff7ee8cc4c. --- fml/BUILD.gn | 4 ---- runtime/BUILD.gn | 18 +++++++++------ shell/platform/fuchsia/dart_runner/BUILD.gn | 25 ++++++++++++++++----- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 408942435ab83..68b9725fa6b2e 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -253,10 +253,6 @@ executable("fml_unittests") { "$flutter_root/runtime:libdart", "$flutter_root/testing", ] - - if (is_fuchsia && using_fuchsia_sdk) { - libs = [ "${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/lib/libzircon.so" ] - } } if (is_fuchsia) { diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index ff2510513fc76..5e41f19f031e2 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -28,13 +28,17 @@ source_set("test_font") { group("libdart") { public_deps = [] - if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") { - public_deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] - } else { - public_deps += [ - "$flutter_root/lib/snapshot", - "//third_party/dart/runtime:libdart_jit", - ] + if (!(is_fuchsia && using_fuchsia_sdk)) { + if (flutter_runtime_mode == "profile" || + flutter_runtime_mode == "release") { + public_deps += + [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] + } else { + public_deps += [ + "$flutter_root/lib/snapshot", + "//third_party/dart/runtime:libdart_jit", + ] + } } } diff --git a/shell/platform/fuchsia/dart_runner/BUILD.gn b/shell/platform/fuchsia/dart_runner/BUILD.gn index ce7de986e450e..735a5dbfab716 100644 --- a/shell/platform/fuchsia/dart_runner/BUILD.gn +++ b/shell/platform/fuchsia/dart_runner/BUILD.gn @@ -17,10 +17,7 @@ template("runner") { invoker_output_name = invoker.output_name extra_defines = invoker.extra_defines - extra_deps = [] - if (defined(invoker.extra_deps)) { - extra_deps += invoker.extra_deps - } + extra_deps = invoker.extra_deps executable(target_name) { output_name = invoker_output_name @@ -80,12 +77,20 @@ runner("dart_jit_runner_bin") { if (flutter_runtime_mode == "profile") { extra_defines += [ "FLUTTER_PROFILE" ] } + extra_deps = [ + "//third_party/dart/runtime:libdart_jit", + "//third_party/dart/runtime/platform:libdart_platform_jit", + ] } runner("dart_jit_product_runner_bin") { output_name = "dart_jit_product_runner" product = true extra_defines = [ "DART_PRODUCT" ] + extra_deps = [ + "//third_party/dart/runtime:libdart_jit_product", + "//third_party/dart/runtime/platform:libdart_platform_jit_product", + ] } runner("dart_aot_runner_bin") { @@ -95,7 +100,11 @@ runner("dart_aot_runner_bin") { if (flutter_runtime_mode == "profile") { extra_defines += [ "FLUTTER_PROFILE" ] } - extra_deps = [ "embedder:dart_aot_snapshot_cc" ] + extra_deps = [ + "embedder:dart_aot_snapshot_cc", + "//third_party/dart/runtime:libdart_precompiled_runtime", + "//third_party/dart/runtime/platform:libdart_platform_precompiled_runtime", + ] } runner("dart_aot_product_runner_bin") { @@ -105,7 +114,11 @@ runner("dart_aot_product_runner_bin") { "AOT_RUNTIME", "DART_PRODUCT", ] - extra_deps = [ "embedder:dart_aot_product_snapshot_cc" ] + extra_deps = [ + "embedder:dart_aot_product_snapshot_cc", + "//third_party/dart/runtime:libdart_precompiled_runtime_product", + "//third_party/dart/runtime/platform:libdart_platform_precompiled_runtime_product", + ] } template("aot_runner_package") { From 617938024315e205f26ed72ff0f0647775fa6a71 Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Tue, 3 Dec 2019 12:25:53 -0800 Subject: [PATCH 308/591] Update fallback to Roboto url to be HTTPS (#14086) --- lib/web_ui/lib/src/engine/compositor/fonts.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/compositor/fonts.dart b/lib/web_ui/lib/src/engine/compositor/fonts.dart index 9e40600d338a9..b079f4c86ab66 100644 --- a/lib/web_ui/lib/src/engine/compositor/fonts.dart +++ b/lib/web_ui/lib/src/engine/compositor/fonts.dart @@ -5,7 +5,7 @@ part of engine; const String _robotoUrl = - 'http://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf'; + 'https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf'; class SkiaFontCollection { final List> _loadingFontBuffers = >[]; From b1d00c998d16f2662eff2fbe85220d11161a57c5 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 3 Dec 2019 13:20:49 -0800 Subject: [PATCH 309/591] Roll src/third_party/dart a4d799c402..89e31069e8 (14 commits) (#14089) dart-lang/sdk@89e31069e8 Revert "[vm/cfe] Elaborate for-in statements during async transform" dart-lang/sdk@f922ed0962 [vm/concurrency] Update IsolateSpawnMemory benchmark dart-lang/sdk@f7941eb850 [cfe] Handle break/continue in flow analysis dart-lang/sdk@175ceca1fa Bump pub version in DEPS dart-lang/sdk@0dc58fdcea Format long lines in shared_type_tests. dart-lang/sdk@2de9b45c8f Update isFunctionSubtypeOf() to NNBD spec. dart-lang/sdk@980b7ace7d Implement DOWN with NNBD rules. dart-lang/sdk@b050a59d3b (dart2js) merge inferrer-engine interface and implementation dart-lang/sdk@1103d9fdb5 (dart2js): seal-in members used only within the inferrer_engine class dart-lang/sdk@aa56cd39a3 (dart2js): rename 'assignments' to 'inputs' in global analysis dart-lang/sdk@198fde2342 Fix language_2/nnbd/syntax/null_assertion_ambiguous_test dart-lang/sdk@e25744c44e [dartfuzz] Adding support for callable classes dart-lang/sdk@fe64ce38e8 [vm] Fix late static fields (both non-final and final) in AST mode dart-lang/sdk@284f662022 Migrate the corelib_2/ tests starting with "e" through "i". --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 0a64bb69e328a..a293646bc70cf 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'a4d799c402efc36327cad2f42186bb48ea9cf18a', + 'dart_revision': '89e31069e8ac0ee9aa67bf9424074404c7a38ee4', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -78,7 +78,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': 'd15067931a6b671a1c9dcc98b5923347676269cf', + 'dart_pub_rev': 'ff5ad1eab1649a8eee6cc593302d3624d9853049', 'dart_pub_semver_tag': '1.4.2', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 218b5ebeb1a56..5fffd1430931a 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f8a490c0ed4d443c531bcee5cb11ff28 +Signature: 784c0b84be0b49054d62e0f55130d627 UNUSED LICENSES: From 77d104d842a77c23c9339ec08273560c9ae13d07 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 3 Dec 2019 16:50:19 -0500 Subject: [PATCH 310/591] Roll src/third_party/skia 49e564e5e02c..75368c3a0290 (23 commits) (#14090) https://skia.googlesource.com/skia.git/+log/49e564e5e02c..75368c3a0290 git log 49e564e5e02c..75368c3a0290 --date=short --first-parent --format='%ad %ae %s' 2019-12-03 fmalita@chromium.org [skottie] Deferred image frame resolution 2019-12-03 brianosman@google.com Particles: Do a cleanup/standardization pass on the API 2019-12-03 herb@google.com Replace some std::tie with auto [] 2019-12-03 michaelludwig@google.com Reorg helper functions in prep for function pointer caching 2019-12-03 herb@google.com Don't draw strikes that are too small 2019-12-03 mtklein@google.com remove SkColorSpaceXformSteps::Required 2019-12-03 kjlubick@google.com [skottieWASM] make sure we are using CPU or GPU correctly 2019-12-03 fmalita@chromium.org Reland "[skottie] Remove defensive seek(0) from Animation ctor" 2019-12-03 mtklein@google.com replace CSXformSteps::Required() in GrSurfaceContext 2019-12-03 mtklein@google.com Fix sprite blitter choice logic 2019-12-03 mtklein@google.com replace CSXformSteps::Required() in SkShader 2019-12-03 mtklein@google.com add friendly new ctor for xform steps 2019-12-03 jvanverth@google.com Some more tweaks to perspective path clipping 2019-12-03 fmalita@chromium.org Revert "[skottie] Remove defensive seek(0) from Animation ctor" 2019-12-03 mtklein@google.com Revert "replace SkColorSpaceXformSteps::Required()" 2019-12-03 fmalita@chromium.org [skottie] Remove defensive seek(0) from Animation ctor 2019-12-03 mtklein@google.com Fix some GCC warnings. 2019-12-03 reed@google.com remove legacy next from iter 2019-12-03 kjlubick@google.com [canvaskit] Add test and extern for MakeImage 2019-12-03 brianosman@google.com Particles: SkImageBinding to allow sampling an image from script 2019-12-03 brianosman@google.com Particles: Better integration for ResourceProvider 2019-12-03 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 8a4500482e8d..bd8110e59b6b (424 commits) 2019-12-03 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 ea0dcd4bda18..fb40d231c3e2 (10 commits) Created with: gclient setdep -r src/third_party/skia@75368c3a0290 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a293646bc70cf..c169f964c897a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '49e564e5e02cfa80202452f47aa28f422db08c0d', + 'skia_revision': '75368c3a02905730312afe613d143ae72748a977', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0c35c18737dca..dc87ce99e42a2 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6994b850237578149dcd1d33789b2a59 +Signature: 74ec6f1e8303bd60dbc8f945c4df4c31 UNUSED LICENSES: From 2fc1e1bce12b073963cbb86f115cdc6f9f2fe477 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Tue, 3 Dec 2019 14:33:02 -0800 Subject: [PATCH 311/591] Relanding: Add Flow unittests and fixtures (#14091) --- BUILD.gn | 5 +- ci/licenses_golden/licenses_flutter | 13 + flow/BUILD.gn | 74 ++- flow/embedded_views.h | 17 + flow/flow_run_all_unittests.cc | 7 + flow/layers/backdrop_filter_layer.cc | 2 - flow/layers/backdrop_filter_layer.h | 1 - .../layers/backdrop_filter_layer_unittests.cc | 186 +++++++ flow/layers/clip_path_layer.cc | 2 - flow/layers/clip_path_layer.h | 1 - flow/layers/clip_path_layer_unittests.cc | 196 ++++++++ flow/layers/clip_rect_layer.cc | 2 - flow/layers/clip_rect_layer.h | 1 - flow/layers/clip_rect_layer_unittests.cc | 194 ++++++++ flow/layers/clip_rrect_layer.cc | 2 - flow/layers/clip_rrect_layer.h | 1 - flow/layers/clip_rrect_layer_unittests.cc | 199 ++++++++ flow/layers/color_filter_layer.cc | 2 - flow/layers/color_filter_layer.h | 1 - flow/layers/color_filter_layer_unittests.cc | 199 ++++++++ flow/layers/container_layer.cc | 8 +- flow/layers/container_layer.h | 3 +- flow/layers/container_layer_unittests.cc | 202 ++++++++ flow/layers/layer_tree.cc | 2 - flow/layers/layer_tree.h | 2 - flow/layers/layer_tree_unittests.cc | 203 ++++++++ flow/layers/opacity_layer.cc | 2 - flow/layers/opacity_layer.h | 1 - flow/layers/opacity_layer_unittests.cc | 312 ++++++++++++ flow/layers/performance_overlay_layer.cc | 55 ++- flow/layers/performance_overlay_layer.h | 7 + .../performance_overlay_layer_unittests.cc | 86 +++- flow/layers/physical_shape_layer.cc | 95 ++-- flow/layers/physical_shape_layer.h | 12 +- flow/layers/physical_shape_layer_unittests.cc | 274 +++++++++-- flow/layers/picture_layer.cc | 2 - flow/layers/picture_layer.h | 1 - flow/layers/picture_layer_unittests.cc | 105 ++++ flow/layers/platform_view_layer.cc | 2 - flow/layers/platform_view_layer.h | 1 - flow/layers/platform_view_layer_unittests.cc | 38 ++ flow/layers/shader_mask_layer.cc | 2 - flow/layers/shader_mask_layer.h | 1 - flow/layers/shader_mask_layer_unittests.cc | 257 ++++++++++ flow/layers/texture_layer.cc | 2 - flow/layers/texture_layer.h | 1 - flow/layers/texture_layer_unittests.cc | 57 +++ flow/layers/transform_layer.cc | 2 - flow/layers/transform_layer.h | 1 - flow/layers/transform_layer_unittests.cc | 228 +++++++++ flow/matrix_decomposition_unittests.cc | 9 +- flow/mutators_stack_unittests.cc | 1 + flow/raster_cache_unittests.cc | 10 + flow/skia_gpu_object.h | 3 - flow/skia_gpu_object_unittests.cc | 107 +++- flow/testing/layer_test.h | 74 +++ flow/testing/mock_layer.cc | 38 ++ flow/testing/mock_layer.h | 50 ++ flow/testing/mock_layer_unittests.cc | 87 ++++ flow/testing/mock_texture.cc | 31 ++ flow/testing/mock_texture.h | 57 +++ flow/testing/mock_texture_unittests.cc | 43 ++ flow/testing/skia_gpu_object_layer_test.cc | 18 + flow/testing/skia_gpu_object_layer_test.h | 30 ++ flow/texture.cc | 10 +- flow/texture.h | 8 +- flow/texture_unittests.cc | 99 +++- fml/BUILD.gn | 5 +- runtime/runtime_test.cc | 20 +- runtime/runtime_test.h | 15 +- shell/common/shell_test.cc | 36 +- shell/common/shell_test.h | 17 +- .../platform/embedder/tests/embedder_test.cc | 15 +- shell/platform/embedder/tests/embedder_test.h | 8 - .../flutter/meta/flutter_aot_runner.cmx | 1 + .../flutter/meta/flutter_jit_runner.cmx | 1 + testing/BUILD.gn | 7 +- testing/assertions_skia.cc | 118 +++++ testing/assertions_skia.h | 84 +--- testing/canvas_test.h | 33 ++ testing/mock_canvas.cc | 457 ++++++++++++++++++ testing/mock_canvas.h | 318 ++++++++++++ testing/mock_canvas_unittests.cc | 32 ++ testing/thread_test.cc | 14 +- testing/thread_test.h | 15 +- 85 files changed, 4552 insertions(+), 388 deletions(-) create mode 100644 flow/layers/backdrop_filter_layer_unittests.cc create mode 100644 flow/layers/clip_path_layer_unittests.cc create mode 100644 flow/layers/clip_rect_layer_unittests.cc create mode 100644 flow/layers/clip_rrect_layer_unittests.cc create mode 100644 flow/layers/color_filter_layer_unittests.cc create mode 100644 flow/layers/container_layer_unittests.cc create mode 100644 flow/layers/layer_tree_unittests.cc create mode 100644 flow/layers/opacity_layer_unittests.cc create mode 100644 flow/layers/picture_layer_unittests.cc create mode 100644 flow/layers/platform_view_layer_unittests.cc create mode 100644 flow/layers/shader_mask_layer_unittests.cc create mode 100644 flow/layers/texture_layer_unittests.cc create mode 100644 flow/layers/transform_layer_unittests.cc create mode 100644 flow/testing/layer_test.h create mode 100644 flow/testing/mock_layer.cc create mode 100644 flow/testing/mock_layer.h create mode 100644 flow/testing/mock_layer_unittests.cc create mode 100644 flow/testing/mock_texture.cc create mode 100644 flow/testing/mock_texture.h create mode 100644 flow/testing/mock_texture_unittests.cc create mode 100644 flow/testing/skia_gpu_object_layer_test.cc create mode 100644 flow/testing/skia_gpu_object_layer_test.h create mode 100644 testing/assertions_skia.cc create mode 100644 testing/canvas_test.h create mode 100644 testing/mock_canvas.cc create mode 100644 testing/mock_canvas.h create mode 100644 testing/mock_canvas_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index 361a5c6f0e2f6..419ae849544a1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -95,7 +95,10 @@ group("flutter") { # Fuchsia currently only supports a subset of our unit tests if (is_fuchsia) { - public_deps += [ "$flutter_root/fml:fml_tests" ] + public_deps += [ + "$flutter_root/flow:flow_tests", + "$flutter_root/fml:fml_tests", + ] } } diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c296ebe416fbd..1c549bd6e61c3 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -30,24 +30,32 @@ FILE: ../../../flutter/flow/instrumentation.cc FILE: ../../../flutter/flow/instrumentation.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h +FILE: ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/child_scene_layer.cc FILE: ../../../flutter/flow/layers/child_scene_layer.h FILE: ../../../flutter/flow/layers/clip_path_layer.cc FILE: ../../../flutter/flow/layers/clip_path_layer.h +FILE: ../../../flutter/flow/layers/clip_path_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rect_layer.cc FILE: ../../../flutter/flow/layers/clip_rect_layer.h +FILE: ../../../flutter/flow/layers/clip_rect_layer_unittests.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc FILE: ../../../flutter/flow/layers/clip_rrect_layer.h +FILE: ../../../flutter/flow/layers/clip_rrect_layer_unittests.cc FILE: ../../../flutter/flow/layers/color_filter_layer.cc FILE: ../../../flutter/flow/layers/color_filter_layer.h +FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h +FILE: ../../../flutter/flow/layers/container_layer_unittests.cc FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h FILE: ../../../flutter/flow/layers/layer_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h +FILE: ../../../flutter/flow/layers/layer_tree_unittests.cc FILE: ../../../flutter/flow/layers/opacity_layer.cc FILE: ../../../flutter/flow/layers/opacity_layer.h +FILE: ../../../flutter/flow/layers/opacity_layer_unittests.cc FILE: ../../../flutter/flow/layers/performance_overlay_layer.cc FILE: ../../../flutter/flow/layers/performance_overlay_layer.h FILE: ../../../flutter/flow/layers/performance_overlay_layer_unittests.cc @@ -56,14 +64,19 @@ FILE: ../../../flutter/flow/layers/physical_shape_layer.h FILE: ../../../flutter/flow/layers/physical_shape_layer_unittests.cc FILE: ../../../flutter/flow/layers/picture_layer.cc FILE: ../../../flutter/flow/layers/picture_layer.h +FILE: ../../../flutter/flow/layers/picture_layer_unittests.cc FILE: ../../../flutter/flow/layers/platform_view_layer.cc FILE: ../../../flutter/flow/layers/platform_view_layer.h +FILE: ../../../flutter/flow/layers/platform_view_layer_unittests.cc FILE: ../../../flutter/flow/layers/shader_mask_layer.cc FILE: ../../../flutter/flow/layers/shader_mask_layer.h +FILE: ../../../flutter/flow/layers/shader_mask_layer_unittests.cc FILE: ../../../flutter/flow/layers/texture_layer.cc FILE: ../../../flutter/flow/layers/texture_layer.h +FILE: ../../../flutter/flow/layers/texture_layer_unittests.cc FILE: ../../../flutter/flow/layers/transform_layer.cc FILE: ../../../flutter/flow/layers/transform_layer.h +FILE: ../../../flutter/flow/layers/transform_layer_unittests.cc FILE: ../../../flutter/flow/matrix_decomposition.cc FILE: ../../../flutter/flow/matrix_decomposition.h FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index c2f0c98415a3e..137aa76d57811 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -4,8 +4,8 @@ if (is_fuchsia) { import("//build/fuchsia/sdk.gni") + import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") } - import("$flutter_root/testing/testing.gni") source_set("flow") { @@ -102,6 +102,26 @@ test_fixtures("flow_fixtures") { fixtures = [] } +source_set("flow_testing") { + testonly = true + + sources = [ + "testing/layer_test.h", + "testing/mock_layer.cc", + "testing/mock_layer.h", + "testing/mock_texture.cc", + "testing/mock_texture.h", + "testing/skia_gpu_object_layer_test.cc", + "testing/skia_gpu_object_layer_test.h", + ] + + public_deps = [ + ":flow", + "$flutter_root/testing:skia", + "//third_party/googletest:gtest", + ] +} + executable("flow_unittests") { testonly = true @@ -109,22 +129,74 @@ executable("flow_unittests") { "flow_run_all_unittests.cc", "flow_test_utils.cc", "flow_test_utils.h", + "layers/backdrop_filter_layer_unittests.cc", + "layers/clip_path_layer_unittests.cc", + "layers/clip_rect_layer_unittests.cc", + "layers/clip_rrect_layer_unittests.cc", + "layers/color_filter_layer_unittests.cc", + "layers/container_layer_unittests.cc", + "layers/layer_tree_unittests.cc", + "layers/opacity_layer_unittests.cc", "layers/performance_overlay_layer_unittests.cc", "layers/physical_shape_layer_unittests.cc", + "layers/picture_layer_unittests.cc", + "layers/platform_view_layer_unittests.cc", + "layers/shader_mask_layer_unittests.cc", + "layers/texture_layer_unittests.cc", + "layers/transform_layer_unittests.cc", "matrix_decomposition_unittests.cc", "mutators_stack_unittests.cc", "raster_cache_unittests.cc", "skia_gpu_object_unittests.cc", + "testing/mock_layer_unittests.cc", + "testing/mock_texture_unittests.cc", "texture_unittests.cc", ] deps = [ ":flow", ":flow_fixtures", + ":flow_testing", "$flutter_root/fml", + "$flutter_root/testing:skia", "$flutter_root/testing:testing_lib", "//third_party/dart/runtime:libdart_jit", # for tracing "//third_party/googletest:gtest", "//third_party/skia", ] } + +if (is_fuchsia) { + fuchsia_archive("flow_tests") { + testonly = true + + deps = [ + ":flow_unittests", + ] + + binary = "flow_unittests" + + libraries = common_libs + + meta_dir = "$flutter_root/testing/fuchsia/meta" + cmx_file = "$meta_dir/fuchsia_test.cmx" + + resources = [ + { + path = rebase_path( + "$flutter_root/testing/resources/performance_overlay_gold_60fps.png") + dest = "flutter/testing/resources/performance_overlay_gold_60fps.png" + }, + { + path = rebase_path( + "$flutter_root/testing/resources/performance_overlay_gold_90fps.png") + dest = "flutter/testing/resources/performance_overlay_gold_90fps.png" + }, + { + path = rebase_path( + "$flutter_root/testing/resources/performance_overlay_gold_120fps.png") + dest = "flutter/testing/resources/performance_overlay_gold_120fps.png" + }, + ] + } +} diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 919ee83a8bb86..030eb88c8a06d 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -143,6 +143,7 @@ class MutatorsStack { // Returns an iterator pointing to the bottom of the stack. const std::vector>::const_reverse_iterator Bottom() const; + bool is_empty() const { return vector_.empty(); } bool operator==(const MutatorsStack& other) const { if (vector_.size() != other.vector_.size()) { @@ -156,10 +157,26 @@ class MutatorsStack { return true; } + bool operator==(const std::vector& other) const { + if (vector_.size() != other.size()) { + return false; + } + for (size_t i = 0; i < vector_.size(); i++) { + if (*vector_[i] != other[i]) { + return false; + } + } + return true; + } + bool operator!=(const MutatorsStack& other) const { return !operator==(other); } + bool operator!=(const std::vector& other) const { + return !operator==(other); + } + private: std::vector> vector_; }; // MutatorsStack diff --git a/flow/flow_run_all_unittests.cc b/flow/flow_run_all_unittests.cc index 4cf0ba3d7fcdf..39963730172ee 100644 --- a/flow/flow_run_all_unittests.cc +++ b/flow/flow_run_all_unittests.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "flutter/fml/build_config.h" #include "flutter/fml/command_line.h" #include "flutter/fml/logging.h" #include "gtest/gtest.h" @@ -23,8 +24,14 @@ int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); fml::CommandLine cmd = fml::CommandLineFromArgcArgv(argc, argv); + +#if defined(OS_FUCHSIA) + flutter::SetGoldenDir(cmd.GetOptionValueWithDefault( + "golden-dir", "/pkg/data/flutter/testing/resources")); +#else flutter::SetGoldenDir( cmd.GetOptionValueWithDefault("golden-dir", "flutter/testing/resources")); +#endif flutter::SetFontFile(cmd.GetOptionValueWithDefault( "font-file", "flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf")); diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index 573db97f191a2..d5799cc4f95dc 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -9,8 +9,6 @@ namespace flutter { BackdropFilterLayer::BackdropFilterLayer(sk_sp filter) : filter_(std::move(filter)) {} -BackdropFilterLayer::~BackdropFilterLayer() = default; - void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index ede9ceeef41f8..732a1ac27e89a 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -14,7 +14,6 @@ namespace flutter { class BackdropFilterLayer : public ContainerLayer { public: BackdropFilterLayer(sk_sp filter); - ~BackdropFilterLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc new file mode 100644 index 0000000000000..e02745adcc83a --- /dev/null +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -0,0 +1,186 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/backdrop_filter_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/effects/SkImageFilters.h" + +namespace flutter { +namespace testing { + +using BackdropFilterLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(sk_sp()); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(BackdropFilterLayerTest, PaintBeforePrerollDies) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(sk_sp()); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(BackdropFilterLayerTest, EmptyFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(nullptr); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(BackdropFilterLayerTest, SimpleFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + layer_filter, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(BackdropFilterLayerTest, MultipleChildren) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + layer_filter, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(BackdropFilterLayerTest, Nested) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray)); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer1 = std::make_shared(layer_filter1); + auto layer2 = std::make_shared(layer_filter2); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), children_bounds); + EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + layer1->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + layer_filter1, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), + SkPaint(), layer_filter2, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index d08c19b34eeb9..3957837d0d7f5 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -17,8 +17,6 @@ ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) FML_DCHECK(clip_behavior != Clip::none); } -ClipPathLayer::~ClipPathLayer() = default; - void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_path_bounds = clip_path_.getBounds(); diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index fd4d56f0db7f0..c21e53c34e76e 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -12,7 +12,6 @@ namespace flutter { class ClipPathLayer : public ContainerLayer { public: ClipPathLayer(const SkPath& clip_path, Clip clip_behavior = Clip::antiAlias); - ~ClipPathLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc new file mode 100644 index 0000000000000..fb91d81f725d4 --- /dev/null +++ b/flow/layers/clip_path_layer_unittests.cc @@ -0,0 +1,196 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/clip_path_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ClipPathLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(ClipPathLayerTest, ClipNoneBehaviorDies) { + EXPECT_DEATH_IF_SUPPORTED( + auto clip = std::make_shared(SkPath(), Clip::none), + "clip_behavior != Clip::none"); +} + +TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(SkPath(), Clip::hardEdge); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipPathLayerTest, PaintBeforePrerollDies) { + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath layer_path = SkPath().addRect(layer_bounds); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + preroll_context()->cull_rect = kEmptyRect; // Cull everything + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(ClipPathLayerTest, ChildOutsideBounds) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipPathLayerTest, FullyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipPathLayerTest, PartiallyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_path, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index de7590624e408..191132a055788 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -11,8 +11,6 @@ ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) FML_DCHECK(clip_behavior != Clip::none); } -ClipRectLayer::~ClipRectLayer() = default; - void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; if (context->cull_rect.intersect(clip_rect_)) { diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index 76c5a3f01c873..50eef22a46e2f 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -12,7 +12,6 @@ namespace flutter { class ClipRectLayer : public ContainerLayer { public: ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior); - ~ClipRectLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc new file mode 100644 index 0000000000000..2784f62705b3f --- /dev/null +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -0,0 +1,194 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/clip_rect_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ClipRectLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(ClipRectLayerTest, ClipNoneBehaviorDies) { + EXPECT_DEATH_IF_SUPPORTED( + auto clip = std::make_shared(kEmptyRect, Clip::none), + "clip_behavior != Clip::none"); +} + +TEST_F(ClipRectLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(kEmptyRect, Clip::hardEdge); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRectLayerTest, PaintBeforePrerollDies) { + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + preroll_context()->cull_rect = kEmptyRect; // Cull everything + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(ClipRectLayerTest, ChildOutsideBounds) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRectLayerTest, FullyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRectLayerTest, PartiallyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 9899a1658732d..e02f8d2411a2b 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -11,8 +11,6 @@ ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) FML_DCHECK(clip_behavior != Clip::none); } -ClipRRectLayer::~ClipRRectLayer() = default; - void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_rrect_bounds = clip_rrect_.getBounds(); diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index 53f74f30a0776..ce1cca2b568de 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -12,7 +12,6 @@ namespace flutter { class ClipRRectLayer : public ContainerLayer { public: ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior); - ~ClipRRectLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc new file mode 100644 index 0000000000000..5c9ed21a3a33f --- /dev/null +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -0,0 +1,199 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/clip_rrect_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ClipRRectLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(ClipRRectLayerTest, ClipNoneBehaviorDies) { + const SkRRect layer_rrect = SkRRect::MakeEmpty(); + EXPECT_DEATH_IF_SUPPORTED( + auto clip = std::make_shared(layer_rrect, Clip::none), + "clip_behavior != Clip::none"); +} + +TEST_F(ClipRRectLayerTest, PaintingEmptyLayerDies) { + const SkRRect layer_rrect = SkRRect::MakeEmpty(); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRRectLayerTest, PaintBeforePreollDies) { + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + preroll_context()->cull_rect = kEmptyRect; // Cull everything + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(ClipRRectLayerTest, ChildOutsideBounds) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRRectLayerTest, FullyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { + const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index f838b0612b2e5..8e5ea19ac8484 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -9,8 +9,6 @@ namespace flutter { ColorFilterLayer::ColorFilterLayer(sk_sp filter) : filter_(std::move(filter)) {} -ColorFilterLayer::~ColorFilterLayer() = default; - void ColorFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ColorFilterLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index cf1de4cb610fc..628c5b17f6e1c 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -14,7 +14,6 @@ namespace flutter { class ColorFilterLayer : public ContainerLayer { public: ColorFilterLayer(sk_sp filter); - ~ColorFilterLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc new file mode 100644 index 0000000000000..204a02357ad98 --- /dev/null +++ b/flow/layers/color_filter_layer_unittests.cc @@ -0,0 +1,199 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/color_filter_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/effects/SkColorMatrixFilter.h" + +namespace flutter { +namespace testing { + +using ColorFilterLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(ColorFilterLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(sk_sp()); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ColorFilterLayerTest, PaintBeforePreollDies) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(sk_sp()); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(ColorFilterLayerTest, EmptyFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(nullptr); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setColorFilter(nullptr); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ColorFilterLayerTest, SimpleFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setColorFilter(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ColorFilterLayerTest, MultipleChildren) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setColorFilter(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, + filter_paint, nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ColorFilterLayerTest, Nested) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter1 = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto layer_filter2 = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorMAGENTA, SK_ColorBLUE); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer1 = std::make_shared(layer_filter1); + auto layer2 = std::make_shared(layer_filter2); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), children_bounds); + EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint1, filter_paint2; + filter_paint1.setColorFilter(layer_filter1); + filter_paint2.setColorFilter(layer_filter2); + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, + filter_paint1, nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), + filter_paint2, nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index d5c6a2a03a34a..fdb13411a9ac8 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -8,8 +8,6 @@ namespace flutter { ContainerLayer::ContainerLayer() {} -ContainerLayer::~ContainerLayer() = default; - void ContainerLayer::Add(std::shared_ptr layer) { layer->set_parent(this); layers_.push_back(std::move(layer)); @@ -23,6 +21,12 @@ void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(child_paint_bounds); } +void ContainerLayer::Paint(PaintContext& context) const { + FML_DCHECK(needs_painting()); + + PaintChildren(context); +} + void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index ef1c03328d1df..a0c054b1ff15c 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -13,12 +13,11 @@ namespace flutter { class ContainerLayer : public Layer { public: ContainerLayer(); - ~ContainerLayer() override; void Add(std::shared_ptr layer); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; - + void Paint(PaintContext& context) const override; #if defined(OS_FUCHSIA) void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc new file mode 100644 index 0000000000000..c5230fe7e70f3 --- /dev/null +++ b/flow/layers/container_layer_unittests.cc @@ -0,0 +1,202 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/container_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using ContainerLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(ContainerLayerTest, LayerWithParentHasPlatformView) { + auto layer = std::make_shared(); + + preroll_context()->has_platform_view = true; + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), + "!context->has_platform_view"); +} + +TEST_F(ContainerLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ContainerLayerTest, PaintBeforePreollDies) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(ContainerLayerTest, Simple) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPaint child_paint(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, child_paint}}})); +} + +TEST_F(ContainerLayerTest, Multiple) { + SkPath child_path1; + child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child_path2; + child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); + SkPaint child_paint1(SkColors::kGray); + SkPaint child_paint2(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, true /* fake_has_platform_view */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_TRUE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), + kGiantRect); // Siblings are independent + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +TEST_F(ContainerLayerTest, MultipleWithEmpty) { + SkPath child_path1; + child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPaint child_paint1(SkColors::kGray); + SkPaint child_paint2(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(SkPath(), child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); + EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_FALSE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); +} + +TEST_F(ContainerLayerTest, NeedsSystemComposite) { + SkPath child_path1; + child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child_path2; + child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); + SkPaint child_paint1(SkColors::kGray); + SkPaint child_paint2(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, false /* fake_has_platform_view */, + true /* fake_needs_system_composite */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_TRUE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index f0e37c9bed565..caa5531a79c06 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -17,8 +17,6 @@ LayerTree::LayerTree() checkerboard_raster_cache_images_(false), checkerboard_offscreen_layers_(false) {} -LayerTree::~LayerTree() = default; - void LayerTree::RecordBuildTime(fml::TimePoint start) { build_start_ = start; build_finish_ = fml::TimePoint::Now(); diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 124b8a85dea45..b9edeeceba2ed 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -22,8 +22,6 @@ class LayerTree { public: LayerTree(); - ~LayerTree(); - void Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false); diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc new file mode 100644 index 0000000000000..e0961723e3c6e --- /dev/null +++ b/flow/layers/layer_tree_unittests.cc @@ -0,0 +1,203 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/layer_tree.h" + +#include "flutter/flow/compositor_context.h" +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/canvas_test.h" +#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +class LayerTreeTest : public CanvasTest { + public: + void SetUp() override { + root_transform_ = SkMatrix::MakeTrans(1.0f, 1.0f); + scoped_frame_ = compositor_context_.AcquireFrame( + nullptr, &mock_canvas(), nullptr, root_transform_, false, nullptr); + } + + void TearDown() override { scoped_frame_ = nullptr; } + + LayerTree& layer_tree() { return layer_tree_; } + CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); } + const SkMatrix& root_transform() { return root_transform_; } + + private: + LayerTree layer_tree_; + CompositorContext compositor_context_; + SkMatrix root_transform_; + std::unique_ptr scoped_frame_; +}; + +TEST_F(LayerTreeTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(); + + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + + layer_tree().Paint(frame()); +} + +TEST_F(LayerTreeTest, PaintBeforePreollDies) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child_path; + child_path.addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + layer_tree().set_root_layer(layer); + EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + + layer_tree().Paint(frame()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +TEST_F(LayerTreeTest, Simple) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kCyan); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(); + layer->Add(mock_layer); + + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), root_transform()); + + layer_tree().Paint(frame()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, child_paint}}})); +} + +TEST_F(LayerTreeTest, Multiple) { + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path2 = SkPath().addRect(8.0f, 2.0f, 16.5f, 14.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, true /* fake_has_platform_view */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), + kGiantRect); // Siblings are independent + + layer_tree().Paint(frame()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +TEST_F(LayerTreeTest, MultipleWithEmpty) { + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(SkPath(), child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); + EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_FALSE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer_tree().Paint(frame()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); +} + +TEST_F(LayerTreeTest, NeedsSystemComposite) { + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path2 = SkPath().addRect(8.0f, 2.0f, 16.5f, 14.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + auto mock_layer1 = std::make_shared( + child_path1, child_paint1, false /* fake_has_platform_view */, + true /* fake_needs_system_composite */); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect expected_total_bounds = child_path1.getBounds(); + expected_total_bounds.join(child_path2.getBounds()); + layer_tree().set_root_layer(layer); + layer_tree().Preroll(frame()); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_system_composite()); + EXPECT_FALSE(mock_layer2->needs_system_composite()); + EXPECT_TRUE(layer->needs_system_composite()); + EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); + EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); + EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); + + layer_tree().Paint(frame()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child_path2, child_paint2}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 6257700ffbddf..1b407307fd068 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -11,8 +11,6 @@ namespace flutter { OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset) : alpha_(alpha), offset_(offset) {} -OpacityLayer::~OpacityLayer() = default; - void OpacityLayer::EnsureSingleChild() { FML_DCHECK(layers().size() > 0); // OpacityLayer should never be a leaf diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 795d8841ba6ed..f1c18c51918e7 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -26,7 +26,6 @@ class OpacityLayer : public ContainerLayer { // to many leaf layers. Therefore we try to capture that offset here to stop // the propagation as repainting the OpacityLayer is expensive. OpacityLayer(int alpha, const SkPoint& offset); - ~OpacityLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc new file mode 100644 index 0000000000000..6ca78a8538fdf --- /dev/null +++ b/flow/layers/opacity_layer_unittests.cc @@ -0,0 +1,312 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/opacity_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using OpacityLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(OpacityLayerTest, LeafLayer) { + auto layer = + std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); + + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), + "layers\\(\\)\\.size\\(\\) > 0"); +} + +TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { + auto mock_layer = std::make_shared(SkPath()); + auto layer = + std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds()); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_FALSE(mock_layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(OpacityLayerTest, PaintBeforePreollDies) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path); + auto layer = + std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(OpacityLayerTest, FullyOpaque) { + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer_transform = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif + const SkPaint child_paint = SkPaint(SkColors::kGreen); + const SkRect expected_layer_bounds = + layer_transform.mapRect(child_path.getBounds()); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(SK_AlphaOPAQUE, layer_offset); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform), Mutator(SK_AlphaOPAQUE)})); + + const SkPaint opacity_paint = SkPaint(SkColors::kBlack); // A = 1.0f + SkRect opacity_bounds; + expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) + .roundOut(&opacity_bounds); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, + 2}}, + MockCanvas::DrawCall{2, + MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(OpacityLayerTest, FullyTransparent) { + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer_transform = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif + const SkPaint child_paint = SkPaint(SkColors::kGreen); + const SkRect expected_layer_bounds = + layer_transform.mapRect(child_path.getBounds()); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = + std::make_shared(SK_AlphaTRANSPARENT, layer_offset); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ( + mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform), Mutator(SK_AlphaTRANSPARENT)})); + + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{2, + MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(OpacityLayerTest, HalfTransparent) { + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer_transform = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif + const SkPaint child_paint = SkPaint(SkColors::kGreen); + const SkRect expected_layer_bounds = + layer_transform.mapRect(child_path.getBounds()); + const SkAlpha alpha_half = 255 / 2; + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(alpha_half, layer_offset); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform), Mutator(alpha_half)})); + + const SkPaint opacity_paint = + SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha_half))); + SkRect opacity_bounds; + expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) + .roundOut(&opacity_bounds); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, + 2}}, + MockCanvas::DrawCall{2, + MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +TEST_F(OpacityLayerTest, Nested) { + const SkPath child1_path = SkPath().addRect(SkRect::MakeWH(5.0f, 6.0f)); + const SkPath child2_path = SkPath().addRect(SkRect::MakeWH(2.0f, 7.0f)); + const SkPath child3_path = SkPath().addRect(SkRect::MakeWH(6.0f, 6.0f)); + const SkPoint layer1_offset = SkPoint::Make(0.5f, 1.5f); + const SkPoint layer2_offset = SkPoint::Make(2.5f, 0.5f); + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); + const SkMatrix layer1_transform = + SkMatrix::MakeTrans(layer1_offset.fX, layer1_offset.fY); + const SkMatrix layer2_transform = + SkMatrix::MakeTrans(layer2_offset.fX, layer2_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer1_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer1_transform)); + const SkMatrix integral_layer2_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); +#endif + const SkPaint child1_paint = SkPaint(SkColors::kRed); + const SkPaint child2_paint = SkPaint(SkColors::kBlue); + const SkPaint child3_paint = SkPaint(SkColors::kGreen); + const SkAlpha alpha1 = 155; + const SkAlpha alpha2 = 224; + auto mock_layer1 = std::make_shared(child1_path, child1_paint); + auto mock_layer2 = std::make_shared(child2_path, child2_paint); + auto mock_layer3 = std::make_shared(child3_path, child3_paint); + auto layer1 = std::make_shared(alpha1, layer1_offset); + auto layer2 = std::make_shared(alpha2, layer2_offset); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + layer1->Add(mock_layer3); // Ensure something is processed after recursion + + const SkRect expected_layer2_bounds = + layer2_transform.mapRect(child2_path.getBounds()); + SkRect expected_layer1_bounds = expected_layer2_bounds; + expected_layer1_bounds.join(child1_path.getBounds()); + expected_layer1_bounds.join(child3_path.getBounds()); + expected_layer1_bounds = layer1_transform.mapRect(expected_layer1_bounds); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds()); + EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); + EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(mock_layer3->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), + SkMatrix::Concat(initial_transform, layer1_transform)); + // EXPECT_EQ(mock_layer1->parent_mutators(), + // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); + EXPECT_EQ( + mock_layer2->parent_matrix(), + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); + // EXPECT_EQ(mock_layer2->parent_mutators(), + // std::vector({Mutator(layer1_transform), Mutator(alpha1), + // Mutator(layer2_transform), Mutator(alpha2)})); + EXPECT_EQ(mock_layer3->parent_matrix(), + SkMatrix::Concat(initial_transform, layer1_transform)); + // EXPECT_EQ(mock_layer3->parent_mutators(), + // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); + + const SkPaint opacity1_paint = + SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha1))); + const SkPaint opacity2_paint = + SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha2))); + SkRect opacity1_bounds, opacity2_bounds; + expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY) + .roundOut(&opacity1_bounds); + expected_layer2_bounds.makeOffset(-layer2_offset.fX, -layer2_offset.fY) + .roundOut(&opacity2_bounds); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer1_transform}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint, + nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child1_path, child1_paint}}, + MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, + MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 3, MockCanvas::SetMatrixData{integral_layer2_transform}}, +#endif + MockCanvas::DrawCall{ + 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint, + nullptr, 4}}, + MockCanvas::DrawCall{ + 4, MockCanvas::DrawPathData{child2_path, child2_paint}}, + MockCanvas::DrawCall{4, MockCanvas::RestoreData{3}}, + MockCanvas::DrawCall{3, MockCanvas::RestoreData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child3_path, child3_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + layer1->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index ebb279c966866..ef7b6f2c6194c 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -8,26 +8,11 @@ #include "flutter/flow/layers/performance_overlay_layer.h" #include "third_party/skia/include/core/SkFont.h" +#include "third_party/skia/include/core/SkTextBlob.h" namespace flutter { namespace { -void DrawStatisticsText(SkCanvas& canvas, - const std::string& string, - int x, - int y, - const std::string& font_path) { - SkFont font; - if (font_path != "") { - font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); - } - font.setSize(15); - SkPaint paint; - paint.setColor(SK_ColorGRAY); - canvas.drawSimpleText(string.c_str(), string.size(), SkTextEncoding::kUTF8, x, - y, font, paint); -} - void VisualizeStopWatch(SkCanvas& canvas, const Stopwatch& stopwatch, SkScalar x, @@ -47,21 +32,39 @@ void VisualizeStopWatch(SkCanvas& canvas, } if (show_labels) { - double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); - double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); - std::stringstream stream; - stream.setf(std::ios::fixed | std::ios::showpoint); - stream << std::setprecision(1); - stream << label_prefix << " " - << "max " << max_ms_per_frame << " ms/frame, " - << "avg " << average_ms_per_frame << " ms/frame"; - DrawStatisticsText(canvas, stream.str(), x + label_x, y + height + label_y, - font_path); + auto text = PerformanceOverlayLayer::MakeStatisticsText( + stopwatch, label_prefix, font_path); + SkPaint paint; + paint.setColor(SK_ColorGRAY); + canvas.drawTextBlob(text, x + label_x, y + height + label_y, paint); } } } // namespace +sk_sp PerformanceOverlayLayer::MakeStatisticsText( + const Stopwatch& stopwatch, + const std::string& label_prefix, + const std::string& font_path) { + SkFont font; + if (font_path != "") { + font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); + } + font.setSize(15); + + double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); + double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); + std::stringstream stream; + stream.setf(std::ios::fixed | std::ios::showpoint); + stream << std::setprecision(1); + stream << label_prefix << " " + << "max " << max_ms_per_frame << " ms/frame, " + << "avg " << average_ms_per_frame << " ms/frame"; + auto text = stream.str(); + return SkTextBlob::MakeFromText(text.c_str(), text.size(), font, + SkTextEncoding::kUTF8); +} + PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options, const char* font_path) : options_(options) { diff --git a/flow/layers/performance_overlay_layer.h b/flow/layers/performance_overlay_layer.h index b5c3370d2055a..b1434a221e688 100644 --- a/flow/layers/performance_overlay_layer.h +++ b/flow/layers/performance_overlay_layer.h @@ -7,9 +7,12 @@ #include +#include "flutter/flow/instrumentation.h" #include "flutter/flow/layers/layer.h" #include "flutter/fml/macros.h" +class SkTextBlob; + namespace flutter { const int kDisplayRasterizerStatistics = 1 << 0; @@ -19,6 +22,10 @@ const int kVisualizeEngineStatistics = 1 << 3; class PerformanceOverlayLayer : public Layer { public: + static sk_sp MakeStatisticsText(const Stopwatch& stopwatch, + const std::string& label_prefix, + const std::string& font_path); + explicit PerformanceOverlayLayer(uint64_t options, const char* font_path = nullptr); diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 605717c870ee3..769f80803a8fa 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -2,18 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/flow_test_utils.h" #include "flutter/flow/layers/performance_overlay_layer.h" + +#include "flutter/flow/flow_test_utils.h" #include "flutter/flow/raster_cache.h" +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/build_config.h" - +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/utils/SkBase64.h" -#include "gtest/gtest.h" - +#include #include +namespace flutter { +namespace testing { +namespace { + // To get the size of kMockedTimes in compile time. template constexpr int size(const T (&array)[N]) noexcept { @@ -77,7 +88,7 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; -#if !OS_LINUX +#if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX const bool golden_data_matches = golden_data->equals(snapshot_data.get()); @@ -97,12 +108,72 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Golden file mismatch. Please check " << "the difference between " << golden_file_path << " and " << new_golden_file_path << ", and replace the former " - << "with the latter if the difference looks good.\n\n" + << "with the latter if the difference looks good.\nS\n" << "See also the base64 encoded " << new_golden_file_path << ":\n" << b64_char; } } +} // namespace + +using PerformanceOverlayLayerTest = LayerTest; + +TEST_F(PerformanceOverlayLayerTest, PaintingEmptyLayerDies) { + const uint64_t overlay_opts = kVisualizeRasterizerStatistics; + auto layer = std::make_shared(overlay_opts); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + + // Crashes reading a nullptr. + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), ""); +} + +TEST_F(PerformanceOverlayLayerTest, InvalidOptions) { + const SkRect layer_bounds = SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f); + const uint64_t overlay_opts = 0; + auto layer = std::make_shared(overlay_opts); + + // TODO(): Note calling code has to call set_paint_bounds right now. Make + // this a constructor parameter and move the set_paint_bounds into Preroll + layer->set_paint_bounds(layer_bounds); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), layer_bounds); + EXPECT_TRUE(layer->needs_painting()); + + // Nothing is drawn if options are invalid (0). + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { + const SkRect layer_bounds = SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f); + const uint64_t overlay_opts = kDisplayRasterizerStatistics; + auto layer = std::make_shared(overlay_opts); + + // TODO(): Note calling code has to call set_paint_bounds right now. Make + // this a constructor parameter and move the set_paint_bounds into Preroll + layer->set_paint_bounds(layer_bounds); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), layer_bounds); + EXPECT_TRUE(layer->needs_painting()); + + layer->Paint(paint_context()); + auto overlay_text = PerformanceOverlayLayer::MakeStatisticsText( + paint_context().raster_time, "GPU", ""); + auto overlay_text_data = overlay_text->serialize(SkSerialProcs{}); + SkPaint text_paint; + text_paint.setColor(SK_ColorGRAY); + SkPoint text_position = SkPoint::Make(16.0f, 22.0f); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawTextData{overlay_text_data, text_paint, + text_position}}})); +} + TEST(PerformanceOverlayLayerDefault, Gold) { TestPerformanceOverlayLayerGold(60); } @@ -114,3 +185,6 @@ TEST(PerformanceOverlayLayer90fps, Gold) { TEST(PerformanceOverlayLayer120fps, Gold) { TestPerformanceOverlayLayerGold(120); } + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 0a607a88c23b0..91b6c73071d17 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -22,7 +22,9 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, : color_(color), shadow_color_(shadow_color), device_pixel_ratio_(device_pixel_ratio), +#if defined(OS_FUCHSIA) viewport_depth_(viewport_depth), +#endif elevation_(elevation), path_(path), isRect_(false), @@ -48,8 +50,6 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, } } -PhysicalShapeLayer::~PhysicalShapeLayer() = default; - void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); @@ -66,48 +66,11 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, // Let the system compositor draw all shadows for us. set_needs_system_composite(true); #else - // Add some margin to the paint bounds to leave space for the shadow. - // We fill this whole region and clip children to it so we don't need to - // join the child paint bounds. - // The offset is calculated as follows: - - // .--- (kLightRadius) - // -------/ (light) - // | / - // | / - // |/ - // |O - // /| (kLightHeight) - // / | - // / | - // / | - // / | - // ------------- (layer) - // /| | - // / | | (elevation) - // A / | |B - // ------------------------------------------------ (canvas) - // --- (extent of shadow) - // - // E = lt } t = (r + w/2)/h - // } => - // r + w/2 = ht } E = (l/h)(r + w/2) - // - // Where: E = extent of shadow - // l = elevation of layer - // r = radius of the light source - // w = width of the layer - // h = light height - // t = tangent of AOB, i.e., multiplier for elevation to extent - SkRect bounds(path_.getBounds()); - // tangent for x - double tx = (kLightRadius * device_pixel_ratio_ + bounds.width() * 0.5) / - kLightHeight; - // tangent for y - double ty = (kLightRadius * device_pixel_ratio_ + bounds.height() * 0.5) / - kLightHeight; - bounds.outset(elevation_ * tx, elevation_ * ty); - set_paint_bounds(bounds); + // We will draw the shadow in Paint(), so add some margin to the paint + // bounds to leave space for the shadow. We fill this whole region and clip + // children to it so we don't need to join the child paint bounds. + set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_, + device_pixel_ratio_)); #endif // defined(OS_FUCHSIA) } } @@ -192,6 +155,50 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->restoreToCount(saveCount); } +SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkRect& bounds, + float elevation, + float pixel_ratio) { + // The shadow offset is calculated as follows: + // .--- (kLightRadius) + // -------/ (light) + // | / + // | / + // |/ + // |O + // /| (kLightHeight) + // / | + // / | + // / | + // / | + // ------------- (layer) + // /| | + // / | | (elevation) + // A / | |B + // ------------------------------------------------ (canvas) + // --- (extent of shadow) + // + // E = lt } t = (r + w/2)/h + // } => + // r + w/2 = ht } E = (l/h)(r + w/2) + // + // Where: E = extent of shadow + // l = elevation of layer + // r = radius of the light source + // w = width of the layer + // h = light height + // t = tangent of AOB, i.e., multiplier for elevation to extent + // tangent for x + double tx = + (kLightRadius * pixel_ratio + bounds.width() * 0.5) / kLightHeight; + // tangent for y + double ty = + (kLightRadius * pixel_ratio + bounds.height() * 0.5) / kLightHeight; + SkRect shadow_bounds(bounds); + shadow_bounds.outset(elevation * tx, elevation * ty); + + return shadow_bounds; +} + void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index f884fe02fc5bd..1b5564c3662b3 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -18,8 +18,10 @@ class PhysicalShapeLayer : public ContainerLayer { float elevation, const SkPath& path, Clip clip_behavior); - ~PhysicalShapeLayer() override; + static SkRect ComputeShadowBounds(const SkRect& bounds, + float elevation, + float pixel_ratio); static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, @@ -35,19 +37,21 @@ class PhysicalShapeLayer : public ContainerLayer { void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) + float total_elevation() const { return total_elevation_; } + private: SkColor color_; SkColor shadow_color_; SkScalar device_pixel_ratio_; - float viewport_depth_; +#if defined(OS_FUCHSIA) + float viewport_depth_ = 0.0f; +#endif float elevation_ = 0.0f; float total_elevation_ = 0.0f; SkPath path_; bool isRect_; SkRRect frameRRect_; Clip clip_behavior_; - - friend class PhysicalShapeLayer_TotalElevation_Test; }; } // namespace flutter diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index 972424a2fec6d..b9382318e8378 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -4,65 +4,245 @@ #include "flutter/flow/layers/physical_shape_layer.h" -#include "gtest/gtest.h" +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" namespace flutter { +namespace testing { -TEST(PhysicalShapeLayer, TotalElevation) { - std::shared_ptr layers[4]; +using PhysicalShapeLayerTest = LayerTest; - SkColor dummy_color = 0; - SkPath dummy_path; - for (int i = 0; i < 4; i += 1) { - layers[i] = - std::make_shared(dummy_color, dummy_color, - 1.0f, // pixel ratio, - 1.0f, // depth - (float)(i + 1), // elevation - dummy_path, Clip::none); - } +#ifndef NDEBUG +TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) { + auto layer = + std::make_shared(SK_ColorBLACK, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + SkPath(), Clip::none); - layers[0]->Add(layers[1]); - layers[0]->Add(layers[2]); - layers[2]->Add(layers[3]); + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(PhysicalShapeLayerTest, PaintBeforePreollDies) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = + std::make_shared(SK_ColorBLACK, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + SkPath(), Clip::none); + layer->Add(mock_layer); - const Stopwatch unused_stopwatch; - TextureRegistry unused_texture_registry; - MutatorsStack unused_stack; - PrerollContext preroll_context{ - nullptr, // raster_cache (don't consult the cache) - nullptr, // gr_context (used for the raster cache) - nullptr, // external view embedder - unused_stack, // mutator stack - nullptr, // SkColorSpace* dst_color_space - kGiantRect, // SkRect cull_rect - unused_stopwatch, // frame time (dont care) - unused_stopwatch, // engine time (dont care) - unused_texture_registry, // texture registry (not supported) - false, // checkerboard_offscreen_layers - 0.0f, // total elevation - }; - - SkMatrix identity; - identity.setIdentity(); - - layers[0]->Preroll(&preroll_context, identity); - - // It should look like this: - // layers[0] +1.0f + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { + SkPath layer_path; + layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto layer = + std::make_shared(SK_ColorGREEN, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + layer_path, Clip::none); + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); +} + +TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) { + SkPath layer_path; + layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPath child1_path; + child1_path.addRect(4, 0, 12, 12).close(); + SkPath child2_path; + child2_path.addRect(3, 2, 5, 15).close(); + auto child1 = std::make_shared(SK_ColorRED, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + child1_path, Clip::none); + auto child2 = + std::make_shared(SK_ColorBLUE, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + child2_path, Clip::none); + auto layer = + std::make_shared(SK_ColorGREEN, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + 0.0f, // elevation + layer_path, Clip::none); + layer->Add(child1); + layer->Add(child2); + + SkRect child_paint_bounds; + layer->Preroll(preroll_context(), SkMatrix()); + child_paint_bounds.join(child1->paint_bounds()); + child_paint_bounds.join(child2->paint_bounds()); + EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); + EXPECT_NE(layer->paint_bounds(), child_paint_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + SkPaint child1_paint; + child1_paint.setColor(SK_ColorRED); + child1_paint.setAntiAlias(true); + SkPaint child2_paint; + child2_paint.setColor(SK_ColorBLUE); + child2_paint.setAntiAlias(true); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child1_path, child1_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ + child2_path, child2_paint}}})); +} + +TEST_F(PhysicalShapeLayerTest, ElevationSimple) { + constexpr float initial_elevation = 20.0f; + SkPath layer_path; + layer_path.addRect(0, 0, 8, 8).close(); + auto layer = std::make_shared( + SK_ColorGREEN, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + initial_elevation, layer_path, Clip::none); + + layer->Preroll(preroll_context(), SkMatrix()); + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not do any painting there. +#if defined(OS_FUCHSIA) + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_system_composite()); +#else + EXPECT_EQ(layer->paint_bounds(), + PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), + initial_elevation, 1.0f)); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); +#endif + EXPECT_EQ(layer->total_elevation(), initial_elevation); + + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not use the direct |Paint()| path there. +#if !defined(OS_FUCHSIA) + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); +#endif +} + +TEST_F(PhysicalShapeLayerTest, ElevationComplex) { + // The layer tree should look like this: + // layers[0] +1.0f = 1.0f // | \ // | \ // | \ - // | layers[2] +3.0f + // | layers[2] +3.0f = 4.0f // | | - // | layers[3] +4.0f + // | layers[3] +4.0f = 8.0f // | // | - // layers[1] + 2.0f - EXPECT_EQ(layers[0]->total_elevation_, 1.0f); - EXPECT_EQ(layers[1]->total_elevation_, 3.0f); - EXPECT_EQ(layers[2]->total_elevation_, 4.0f); - EXPECT_EQ(layers[3]->total_elevation_, 8.0f); + // layers[1] + 2.0f = 3.0f + constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f}; + constexpr float total_elevations[4] = {1.0f, 3.0f, 4.0f, 8.0f}; + SkPath layer_path; + layer_path.addRect(0, 0, 80, 80).close(); + + std::shared_ptr layers[4]; + for (int i = 0; i < 4; i += 1) { + layers[i] = std::make_shared( + SK_ColorBLACK, SK_ColorBLACK, + 1.0f, // pixel ratio + 1.0f, // depth + initial_elevations[i], layer_path, Clip::none); + } + layers[0]->Add(layers[1]); + layers[0]->Add(layers[2]); + layers[2]->Add(layers[3]); + + layers[0]->Preroll(preroll_context(), SkMatrix()); + for (int i = 0; i < 4; i += 1) { + // On Fuchsia, the system compositor handles all elevated + // PhysicalShapeLayers and their shadows , so we do not do any painting + // there. +#if defined(OS_FUCHSIA) + EXPECT_EQ(layers[i]->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layers[i]->needs_painting()); + EXPECT_TRUE(layers[i]->needs_system_composite()); +#else + EXPECT_EQ(layers[i]->paint_bounds(), + (PhysicalShapeLayer::ComputeShadowBounds( + layer_path.getBounds(), initial_elevations[i], + 1.0f /* pixel_ratio */))); + EXPECT_TRUE(layers[i]->needs_painting()); + EXPECT_FALSE(layers[i]->needs_system_composite()); +#endif + EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]); + } + + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not use the direct |Paint()| path there. +#if !defined(OS_FUCHSIA) + SkPaint layer_paint; + layer_paint.setColor(SK_ColorBLACK); + layer_paint.setAntiAlias(true); + layers[0]->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}, + MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, + MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); +#endif } +} // namespace testing } // namespace flutter diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index c4275e76c13cf..230b648f50e80 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -17,8 +17,6 @@ PictureLayer::PictureLayer(const SkPoint& offset, is_complex_(is_complex), will_change_(will_change) {} -PictureLayer::~PictureLayer() = default; - void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkPicture* sk_picture = picture(); diff --git a/flow/layers/picture_layer.h b/flow/layers/picture_layer.h index 9c40cbef37cbd..e733e7455ca6c 100644 --- a/flow/layers/picture_layer.h +++ b/flow/layers/picture_layer.h @@ -19,7 +19,6 @@ class PictureLayer : public Layer { SkiaGPUObject picture, bool is_complex, bool will_change); - ~PictureLayer() override; SkPicture* picture() const { return picture_.get().get(); } diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc new file mode 100644 index 0000000000000..687c870eeac66 --- /dev/null +++ b/flow/layers/picture_layer_unittests.cc @@ -0,0 +1,105 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/flow/layers/picture_layer.h" + +#include "flutter/flow/testing/skia_gpu_object_layer_test.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkPicture.h" + +#ifndef SUPPORT_FRACTIONAL_TRANSLATION +#include "flutter/flow/raster_cache.h" +#endif + +namespace flutter { +namespace testing { + +using PictureLayerTest = SkiaGPUObjectLayerTest; + +#ifndef NDEBUG +TEST_F(PictureLayerTest, PaintBeforePrerollInvalidPictureDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(), false, false); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "picture_\\.get\\(\\)"); +} + +TEST_F(PictureLayerTest, PaintBeforePreollDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); + + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(PictureLayerTest, PaintingEmptyLayerDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkRect picture_bounds = SkRect::MakeEmpty(); + auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(PictureLayerTest, InvalidPictureDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(), false, false); + + // Crashes reading a nullptr. + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); +} + +TEST_F(PictureLayerTest, SimplePicture) { + const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); + const SkMatrix layer_offset_matrix = + SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); + auto layer = std::make_shared( + layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), + picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_EQ(layer->picture(), mock_picture.get()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + layer->Paint(paint_context()); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, + MockCanvas::ConcatMatrixData{layer_offset_matrix}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( + layer_offset_matrix)}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), + SkMatrix()}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 3f72993f97d66..81541b7a0cde8 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -11,8 +11,6 @@ PlatformViewLayer::PlatformViewLayer(const SkPoint& offset, int64_t view_id) : offset_(offset), size_(size), view_id_(view_id) {} -PlatformViewLayer::~PlatformViewLayer() = default; - void PlatformViewLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), diff --git a/flow/layers/platform_view_layer.h b/flow/layers/platform_view_layer.h index 7ce7ccb58a856..242b3734dd3b1 100644 --- a/flow/layers/platform_view_layer.h +++ b/flow/layers/platform_view_layer.h @@ -14,7 +14,6 @@ namespace flutter { class PlatformViewLayer : public Layer { public: PlatformViewLayer(const SkPoint& offset, const SkSize& size, int64_t view_id); - ~PlatformViewLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/platform_view_layer_unittests.cc b/flow/layers/platform_view_layer_unittests.cc new file mode 100644 index 0000000000000..123f9ab9925f6 --- /dev/null +++ b/flow/layers/platform_view_layer_unittests.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/platform_view_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using PlatformViewLayerTest = LayerTest; + +TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(8.0f, 8.0f); + const int64_t view_id = 0; + auto layer = + std::make_shared(layer_offset, layer_size, view_id); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_FALSE(preroll_context()->has_platform_view); + EXPECT_EQ(layer->paint_bounds(), + SkRect::MakeSize(layer_size) + .makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + + layer->Paint(paint_context()); + EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index 36e7b7332aeae..8e681ec725458 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -11,8 +11,6 @@ ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, SkBlendMode blend_mode) : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {} -ShaderMaskLayer::~ShaderMaskLayer() = default; - void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index 01836f4f2fb54..7f633c0372d45 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -16,7 +16,6 @@ class ShaderMaskLayer : public ContainerLayer { ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode); - ~ShaderMaskLayer() override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc new file mode 100644 index 0000000000000..d8997c3e65b2f --- /dev/null +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -0,0 +1,257 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/shader_mask_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkShader.h" +#include "third_party/skia/include/effects/SkPerlinNoiseShader.h" + +namespace flutter { +namespace testing { + +using ShaderMaskLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) { + auto layer = + std::make_shared(nullptr, kEmptyRect, SkBlendMode::kSrc); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(ShaderMaskLayerTest, PaintBeforePreollDies) { + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + auto mock_layer = std::make_shared(child_path); + auto layer = + std::make_shared(nullptr, kEmptyRect, SkBlendMode::kSrc); + layer->Add(mock_layer); + + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(ShaderMaskLayerTest, EmptyFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(nullptr, layer_bounds, + SkBlendMode::kSrc); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setBlendMode(SkBlendMode::kSrc); + filter_paint.setShader(nullptr); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH( + layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ShaderMaskLayerTest, SimpleFilter) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_filter, layer_bounds, + SkBlendMode::kSrc); + layer->Add(mock_layer); + + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setBlendMode(SkBlendMode::kSrc); + filter_paint.setShader(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH( + layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ShaderMaskLayerTest, MultipleChildren) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer = std::make_shared(layer_filter, layer_bounds, + SkBlendMode::kSrc); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint; + filter_paint.setBlendMode(SkBlendMode::kSrc); + filter_paint.setShader(layer_filter); + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH( + layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(ShaderMaskLayerTest, Nested) { + const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 7.5f, 8.5f); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); + const SkPath child_path1 = SkPath().addRect(child_bounds); + const SkPath child_path2 = + SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); + const SkPaint child_paint1 = SkPaint(SkColors::kYellow); + const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + auto layer_filter1 = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto layer_filter2 = + SkPerlinNoiseShader::MakeImprovedNoise(2.0f, 2.0f, 2, 2.0f); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(child_path2, child_paint2); + auto layer1 = std::make_shared(layer_filter1, layer_bounds, + SkBlendMode::kSrc); + auto layer2 = std::make_shared(layer_filter2, layer_bounds, + SkBlendMode::kSrc); + layer2->Add(mock_layer2); + layer1->Add(mock_layer1); + layer1->Add(layer2); + + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), children_bounds); + EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); + EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); + + SkPaint filter_paint1, filter_paint2; + filter_paint1.setBlendMode(SkBlendMode::kSrc); + filter_paint2.setBlendMode(SkBlendMode::kSrc); + filter_paint1.setShader(layer_filter1); + filter_paint2.setShader(layer_filter2); + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, + 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(), + nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 2, + MockCanvas::DrawRectData{ + SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), + filter_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkMatrix::MakeTrans( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, + MockCanvas::DrawRectData{ + SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), + filter_paint1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index c7716dd59bc29..848f69c8a115a 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -14,8 +14,6 @@ TextureLayer::TextureLayer(const SkPoint& offset, bool freeze) : offset_(offset), size_(size), texture_id_(texture_id), freeze_(freeze) {} -TextureLayer::~TextureLayer() = default; - void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); diff --git a/flow/layers/texture_layer.h b/flow/layers/texture_layer.h index 7c04471afa0c1..20f6c709d6107 100644 --- a/flow/layers/texture_layer.h +++ b/flow/layers/texture_layer.h @@ -17,7 +17,6 @@ class TextureLayer : public Layer { const SkSize& size, int64_t texture_id, bool freeze); - ~TextureLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/texture_layer_unittests.cc b/flow/layers/texture_layer_unittests.cc new file mode 100644 index 0000000000000..9adf273e61122 --- /dev/null +++ b/flow/layers/texture_layer_unittests.cc @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/texture_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/flow/testing/mock_texture.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using TextureLayerTest = LayerTest; + +TEST_F(TextureLayerTest, InvalidTexture) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(8.0f, 8.0f); + auto layer = + std::make_shared(layer_offset, layer_size, 0, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), + (SkRect::MakeSize(layer_size) + .makeOffset(layer_offset.fX, layer_offset.fY))); + EXPECT_TRUE(layer->needs_painting()); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +TEST_F(TextureLayerTest, PaintingEmptyLayerDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(0.0f, 0.0f); + const int64_t texture_id = 0; + auto mock_texture = std::make_shared(texture_id); + auto layer = std::make_shared(layer_offset, layer_size, + texture_id, false); + + // Ensure the texture is located by the Layer. + preroll_context()->texture_registry.RegisterTexture(mock_texture); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_FALSE(layer->needs_painting()); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_texture->paint_calls(), + std::vector({MockTexture::PaintCall{ + mock_canvas(), layer->paint_bounds(), false, nullptr}})); + EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 5a7af132c68f2..9513e8bc0fec6 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -24,8 +24,6 @@ TransformLayer::TransformLayer(const SkMatrix& transform) } } -TransformLayer::~TransformLayer() = default; - void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix child_matrix; child_matrix.setConcat(matrix, transform_); diff --git a/flow/layers/transform_layer.h b/flow/layers/transform_layer.h index f19a963ced9fe..a21e7d4f10c5b 100644 --- a/flow/layers/transform_layer.h +++ b/flow/layers/transform_layer.h @@ -14,7 +14,6 @@ namespace flutter { class TransformLayer : public ContainerLayer { public: TransformLayer(const SkMatrix& transform); - ~TransformLayer() override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; diff --git a/flow/layers/transform_layer_unittests.cc b/flow/layers/transform_layer_unittests.cc new file mode 100644 index 0000000000000..b99f35b2c7a25 --- /dev/null +++ b/flow/layers/transform_layer_unittests.cc @@ -0,0 +1,228 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/transform_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_layer.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using TransformLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(TransformLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(SkMatrix()); // identity + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(TransformLayerTest, PaintBeforePreollDies) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = std::make_shared(SkMatrix()); // identity + layer->Add(mock_layer); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(TransformLayerTest, Identity) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = std::make_shared(SkMatrix()); // identity + layer->Add(mock_layer); + + preroll_context()->cull_rect = cull_rect; + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity + EXPECT_EQ(mock_layer->parent_cull_rect(), cull_rect); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(SkMatrix())})); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, SkPaint()}}})); +} + +TEST_F(TransformLayerTest, Simple) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + SkMatrix layer_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix inverse_layer_transform; + EXPECT_TRUE(layer_transform.invert(&inverse_layer_transform)); + + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer = std::make_shared(layer_transform); + layer->Add(mock_layer); + + preroll_context()->cull_rect = cull_rect; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->paint_bounds(), + layer_transform.mapRect(mock_layer->paint_bounds())); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_EQ(mock_layer->parent_matrix(), + SkMatrix::Concat(initial_transform, layer_transform)); + EXPECT_EQ(mock_layer->parent_cull_rect(), + inverse_layer_transform.mapRect(cull_rect)); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_transform)})); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, SkPaint()}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(TransformLayerTest, Nested) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + SkMatrix layer1_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix layer2_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix inverse_layer1_transform, inverse_layer2_transform; + EXPECT_TRUE(layer1_transform.invert(&inverse_layer1_transform)); + EXPECT_TRUE(layer2_transform.invert(&inverse_layer2_transform)); + + auto mock_layer = std::make_shared(child_path, SkPaint()); + auto layer1 = std::make_shared(layer1_transform); + auto layer2 = std::make_shared(layer2_transform); + layer1->Add(layer2); + layer2->Add(mock_layer); + + preroll_context()->cull_rect = cull_rect; + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer2->paint_bounds(), + layer2_transform.mapRect(mock_layer->paint_bounds())); + EXPECT_EQ(layer1->paint_bounds(), + layer1_transform.mapRect(layer2->paint_bounds())); + EXPECT_TRUE(mock_layer->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_EQ( + mock_layer->parent_matrix(), + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); + EXPECT_EQ(mock_layer->parent_cull_rect(), + inverse_layer2_transform.mapRect( + inverse_layer1_transform.mapRect(cull_rect))); + EXPECT_EQ( + mock_layer->parent_mutators(), + std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); + + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer1_transform}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{layer2_transform}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, SkPaint()}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +TEST_F(TransformLayerTest, NestedSeparated) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); + SkMatrix initial_transform = SkMatrix::MakeTrans(-0.5f, -0.5f); + SkMatrix layer1_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix layer2_transform = SkMatrix::MakeTrans(2.5f, 2.5f); + SkMatrix inverse_layer1_transform, inverse_layer2_transform; + EXPECT_TRUE(layer1_transform.invert(&inverse_layer1_transform)); + EXPECT_TRUE(layer2_transform.invert(&inverse_layer2_transform)); + + auto mock_layer1 = + std::make_shared(child_path, SkPaint(SkColors::kBlue)); + auto mock_layer2 = + std::make_shared(child_path, SkPaint(SkColors::kGreen)); + auto layer1 = std::make_shared(layer1_transform); + auto layer2 = std::make_shared(layer2_transform); + layer1->Add(mock_layer1); + layer1->Add(layer2); + layer2->Add(mock_layer2); + + preroll_context()->cull_rect = cull_rect; + layer1->Preroll(preroll_context(), initial_transform); + SkRect expected_layer1_bounds = layer2->paint_bounds(); + expected_layer1_bounds.join(mock_layer1->paint_bounds()); + layer1_transform.mapRect(&expected_layer1_bounds); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer2->paint_bounds(), + layer2_transform.mapRect(mock_layer2->paint_bounds())); + EXPECT_EQ(mock_layer1->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); + EXPECT_TRUE(mock_layer2->needs_painting()); + EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting()); + EXPECT_TRUE(layer1->needs_painting()); + EXPECT_EQ(mock_layer1->parent_matrix(), + SkMatrix::Concat(initial_transform, layer1_transform)); + EXPECT_EQ( + mock_layer2->parent_matrix(), + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); + EXPECT_EQ(mock_layer1->parent_cull_rect(), + inverse_layer1_transform.mapRect(cull_rect)); + EXPECT_EQ(mock_layer2->parent_cull_rect(), + inverse_layer2_transform.mapRect( + inverse_layer1_transform.mapRect(cull_rect))); + EXPECT_EQ(mock_layer1->parent_mutators(), + std::vector({Mutator(layer1_transform)})); + EXPECT_EQ( + mock_layer2->parent_mutators(), + std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); + + layer1->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer1_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, + SkPaint(SkColors::kBlue)}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{layer2_transform}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, + SkPaint(SkColors::kGreen)}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/matrix_decomposition_unittests.cc b/flow/matrix_decomposition_unittests.cc index cf96025276737..8aa511e4a0a97 100644 --- a/flow/matrix_decomposition_unittests.cc +++ b/flow/matrix_decomposition_unittests.cc @@ -12,6 +12,9 @@ #include "flutter/flow/matrix_decomposition.h" #include "gtest/gtest.h" +namespace flutter { +namespace testing { + TEST(MatrixDecomposition, Rotation) { SkMatrix44 matrix = SkMatrix44::I(); @@ -93,7 +96,8 @@ TEST(MatrixDecomposition, Combination) { } TEST(MatrixDecomposition, ScaleFloatError) { - for (float scale = 0.0001f; scale < 2.0f; scale += 0.000001f) { + constexpr float scale_increment = 0.00001f; + for (float scale = 0.0001f; scale < 2.0f; scale += scale_increment) { SkMatrix44 matrix = SkMatrix44::I(); matrix.setScale(scale, scale, 1.0f); @@ -152,3 +156,6 @@ TEST(MatrixDecomposition, ScaleFloatError) { ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[1]); ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[2]); } + +} // namespace testing +} // namespace flutter diff --git a/flow/mutators_stack_unittests.cc b/flow/mutators_stack_unittests.cc index 97cfe9a54a7c7..1d31a81623307 100644 --- a/flow/mutators_stack_unittests.cc +++ b/flow/mutators_stack_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/flow/embedded_views.h" + #include "gtest/gtest.h" namespace flutter { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 64f4405ebe5a0..bd83d807875f2 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -3,10 +3,15 @@ // found in the LICENSE file. #include "flutter/flow/raster_cache.h" + #include "gtest/gtest.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +namespace flutter { +namespace testing { +namespace { + sk_sp GetSamplePicture() { SkPictureRecorder recorder; recorder.beginRecording(SkRect::MakeWH(150, 100)); @@ -17,6 +22,8 @@ sk_sp GetSamplePicture() { return recorder.finishRecordingAsPicture(); } +} // namespace + TEST(RasterCache, SimpleInitialization) { flutter::RasterCache cache; ASSERT_TRUE(true); @@ -93,3 +100,6 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) { ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true, false)); // 5 } + +} // namespace testing +} // namespace flutter diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 4823ec14208c5..2a09f982a4386 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -54,14 +54,11 @@ class SkiaGPUObject { using SkiaObjectType = T; SkiaGPUObject() = default; - SkiaGPUObject(sk_sp object, fml::RefPtr queue) : object_(std::move(object)), queue_(std::move(queue)) { FML_DCHECK(object_); } - SkiaGPUObject(SkiaGPUObject&&) = default; - ~SkiaGPUObject() { reset(); } SkiaGPUObject& operator=(SkiaGPUObject&&) = default; diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index aa259a6909eec..9df82ad11c312 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/flow/skia_gpu_object.h" + #include "flutter/fml/message_loop.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" @@ -13,8 +14,6 @@ namespace flutter { namespace testing { -using SkiaGpuObjectTest = flutter::testing::ThreadTest; - class TestSkObject : public SkRefCnt { public: TestSkObject(std::shared_ptr latch, @@ -22,7 +21,9 @@ class TestSkObject : public SkRefCnt { : latch_(latch), dtor_task_queue_id_(dtor_task_queue_id) {} ~TestSkObject() { - *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); + if (dtor_task_queue_id_) { + *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); + } latch_->Signal(); } @@ -31,19 +32,107 @@ class TestSkObject : public SkRefCnt { fml::TaskQueueId* dtor_task_queue_id_; }; -TEST_F(SkiaGpuObjectTest, UnrefQueue) { - fml::RefPtr task_runner = CreateNewThread(); - fml::RefPtr queue = fml::MakeRefCounted( - task_runner, fml::TimeDelta::FromSeconds(0)); +class SkiaGpuObjectTest : public ThreadTest { + public: + SkiaGpuObjectTest() + : unref_task_runner_(CreateNewThread()), + unref_queue_(fml::MakeRefCounted( + unref_task_runner(), + fml::TimeDelta::FromSeconds(0))), + delayed_unref_queue_(fml::MakeRefCounted( + unref_task_runner(), + fml::TimeDelta::FromSeconds(3))) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + } + + fml::RefPtr unref_task_runner() { + return unref_task_runner_; + } + fml::RefPtr unref_queue() { return unref_queue_; } + fml::RefPtr delayed_unref_queue() { + return delayed_unref_queue_; + } + + private: + fml::RefPtr unref_task_runner_; + fml::RefPtr unref_queue_; + fml::RefPtr delayed_unref_queue_; +}; +TEST_F(SkiaGpuObjectTest, QueueSimple) { std::shared_ptr latch = std::make_shared(); fml::TaskQueueId dtor_task_queue_id(0); SkRefCnt* ref_object = new TestSkObject(latch, &dtor_task_queue_id); - queue->Unref(ref_object); + unref_queue()->Unref(ref_object); + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectDestructor) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + + { + auto object = sk_make_sp(latch, &dtor_task_queue_id); + SkiaGPUObject sk_object(object, unref_queue()); + ASSERT_EQ(sk_object.get(), object); + ASSERT_EQ(dtor_task_queue_id, 0); + } + + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectReset) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + SkiaGPUObject sk_object( + sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); + + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectResetBeforeDestructor) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + + { + auto object = sk_make_sp(latch, &dtor_task_queue_id); + SkiaGPUObject sk_object(object, unref_queue()); + ASSERT_EQ(sk_object.get(), object); + ASSERT_EQ(dtor_task_queue_id, 0); + + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + } + + latch->Wait(); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + +TEST_F(SkiaGpuObjectTest, ObjectResetTwice) { + std::shared_ptr latch = + std::make_shared(); + fml::TaskQueueId dtor_task_queue_id(0); + SkiaGPUObject sk_object( + sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); + + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + sk_object.reset(); + ASSERT_EQ(sk_object.get(), nullptr); + latch->Wait(); - ASSERT_EQ(dtor_task_queue_id, task_runner->GetTaskQueueId()); + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); } } // namespace testing diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h new file mode 100644 index 0000000000000..e38d690a2eeb7 --- /dev/null +++ b/flow/testing/layer_test.h @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLOW_TESTING_LAYER_TEST_H_ +#define FLOW_TESTING_LAYER_TEST_H_ + +#include "flutter/flow/layers/layer.h" + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/testing/canvas_test.h" +#include "flutter/testing/mock_canvas.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" + +namespace flutter { +namespace testing { + +// This fixture allows generating tests which can |Paint()| and |Preroll()| +// |Layer|'s. +// |LayerTest| is a default implementation based on |::testing::Test|. +// +// |BaseT| should be the base test type, such as |::testing::Test| below. +template +class LayerTestBase : public CanvasTestBase { + using TestT = CanvasTestBase; + + public: + LayerTestBase() + : preroll_context_({ + nullptr, /* raster_cache */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ + mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), + kGiantRect, /* cull_rect */ + raster_time_, ui_time_, texture_registry_, + false, /* checkerboard_offscreen_layers */ + 0.0f /* total_elevation */ + }), + paint_context_({ + TestT::mock_canvas().internal_canvas(), /* internal_nodes_canvas */ + &TestT::mock_canvas(), /* leaf_nodes_canvas */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ + raster_time_, ui_time_, texture_registry_, + nullptr, /* raster_cache */ + false, /* checkerboard_offscreen_layers */ + }) {} + + TextureRegistry& texture_regitry() { return texture_registry_; } + PrerollContext* preroll_context() { return &preroll_context_; } + Layer::PaintContext& paint_context() { return paint_context_; } + + private: + Stopwatch raster_time_; + Stopwatch ui_time_; + MutatorsStack mutators_stack_; + TextureRegistry texture_registry_; + + PrerollContext preroll_context_; + Layer::PaintContext paint_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(LayerTestBase); +}; +using LayerTest = LayerTestBase<::testing::Test>; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_LAYER_TEST_H_ diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc new file mode 100644 index 0000000000000..1065f43054674 --- /dev/null +++ b/flow/testing/mock_layer.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_layer.h" + +namespace flutter { +namespace testing { + +MockLayer::MockLayer(SkPath path, + SkPaint paint, + bool fake_has_platform_view, + bool fake_needs_system_composite) + : fake_paint_path_(path), + fake_paint_(paint), + fake_has_platform_view_(fake_has_platform_view), + fake_needs_system_composite_(fake_needs_system_composite) {} + +void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + parent_mutators_ = context->mutators_stack; + parent_matrix_ = matrix; + parent_cull_rect_ = context->cull_rect; + parent_elevation_ = context->total_elevation; + parent_has_platform_view_ = context->has_platform_view; + + context->has_platform_view = fake_has_platform_view_; + set_paint_bounds(fake_paint_path_.getBounds()); + set_needs_system_composite(fake_needs_system_composite_); +} + +void MockLayer::Paint(PaintContext& context) const { + FML_DCHECK(needs_painting()); + + context.leaf_nodes_canvas->drawPath(fake_paint_path_, fake_paint_); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h new file mode 100644 index 0000000000000..b55452a37812c --- /dev/null +++ b/flow/testing/mock_layer.h @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLOW_TESTING_MOCK_LAYER_H_ +#define FLOW_TESTING_MOCK_LAYER_H_ + +#include "flutter/flow/layers/layer.h" + +namespace flutter { +namespace testing { + +// Mock implementation of the |Layer| interface that does nothing but paint +// the specified |path| into the canvas. It records the |PrerollContext| and +// |PaintContext| data passed in by its parent |Layer|, so the test can later +// verify the data against expected values. +class MockLayer : public Layer { + public: + MockLayer(SkPath path, + SkPaint paint = SkPaint(), + bool fake_has_platform_view = false, + bool fake_needs_system_composite = false); + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Paint(PaintContext& context) const override; + + const MutatorsStack& parent_mutators() { return parent_mutators_; } + const SkMatrix& parent_matrix() { return parent_matrix_; } + const SkRect& parent_cull_rect() { return parent_cull_rect_; } + float parent_elevation() { return parent_elevation_; } + bool parent_has_platform_view() { return parent_has_platform_view_; } + + private: + MutatorsStack parent_mutators_; + SkMatrix parent_matrix_; + SkRect parent_cull_rect_ = SkRect::MakeEmpty(); + SkPath fake_paint_path_; + SkPaint fake_paint_; + float parent_elevation_ = 0; + bool parent_has_platform_view_ = false; + bool fake_has_platform_view_ = false; + bool fake_needs_system_composite_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(MockLayer); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_MOCK_LAYER_H_ diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc new file mode 100644 index 0000000000000..d5a215fde5c76 --- /dev/null +++ b/flow/testing/mock_layer_unittests.cc @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_layer.h" + +#include "flutter/flow/testing/layer_test.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +namespace flutter { +namespace testing { + +using MockLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(MockLayerTest, PaintBeforePreollDies) { + SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + auto layer = std::make_shared(path, SkPaint()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} + +TEST_F(MockLayerTest, PaintingEmptyLayerDies) { + auto layer = std::make_shared(SkPath(), SkPaint()); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkPath().getBounds()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(\\)"); +} +#endif + +TEST_F(MockLayerTest, SimpleParams) { + const SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPaint paint = SkPaint(SkColors::kBlue); + const SkMatrix start_matrix = SkMatrix::MakeTrans(1.0f, 2.0f); + const SkMatrix scale_matrix = SkMatrix::MakeScale(0.5f, 0.5f); + const SkRect cull_rect = SkRect::MakeWH(5.0f, 5.0f); + const float parent_elevation = 5.0f; + const bool parent_has_platform_view = true; + auto layer = std::make_shared(path, paint); + + preroll_context()->mutators_stack.PushTransform(scale_matrix); + preroll_context()->cull_rect = cull_rect; + preroll_context()->total_elevation = parent_elevation; + preroll_context()->has_platform_view = parent_has_platform_view; + layer->Preroll(preroll_context(), start_matrix); + EXPECT_EQ(preroll_context()->has_platform_view, false); + EXPECT_EQ(layer->paint_bounds(), path.getBounds()); + EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_EQ(layer->parent_mutators(), std::vector{Mutator(scale_matrix)}); + EXPECT_EQ(layer->parent_matrix(), start_matrix); + EXPECT_EQ(layer->parent_cull_rect(), cull_rect); + EXPECT_EQ(layer->parent_elevation(), parent_elevation); + EXPECT_EQ(layer->parent_has_platform_view(), parent_has_platform_view); + + layer->Paint(paint_context()); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{path, paint}}})); +} + +TEST_F(MockLayerTest, FakePlatformView) { + auto layer = std::make_shared(SkPath(), SkPaint(), + true /* fake_has_platform_view */); + EXPECT_EQ(preroll_context()->has_platform_view, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->has_platform_view, true); +} + +TEST_F(MockLayerTest, FakeSystemComposite) { + auto layer = std::make_shared( + SkPath(), SkPaint(), false /* fake_has_platform_view */, + true /* fake_needs_system_composite */); + EXPECT_EQ(layer->needs_system_composite(), false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->needs_system_composite(), true); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_texture.cc b/flow/testing/mock_texture.cc new file mode 100644 index 0000000000000..26e49b764cdaf --- /dev/null +++ b/flow/testing/mock_texture.cc @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_texture.h" + +namespace flutter { +namespace testing { + +MockTexture::MockTexture(int64_t textureId) : Texture(textureId) {} + +void MockTexture::Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) { + paint_calls_.emplace_back(PaintCall{canvas, bounds, freeze, context}); +} + +bool operator==(const MockTexture::PaintCall& a, + const MockTexture::PaintCall& b) { + return &a.canvas == &b.canvas && a.bounds == b.bounds && + a.context == b.context && a.freeze == b.freeze; +} + +std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) { + return os << &data.canvas << " " << data.bounds << " " << data.context << " " + << data.freeze; +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_texture.h b/flow/testing/mock_texture.h new file mode 100644 index 0000000000000..c5339ebb77a66 --- /dev/null +++ b/flow/testing/mock_texture.h @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/texture.h" +#include "flutter/testing/assertions_skia.h" + +#include +#include + +namespace flutter { +namespace testing { + +// Mock implementation of the |Texture| interface that does not interact with +// the GPU. It simply records the list of various calls made so the test can +// later verify them against expected data. +class MockTexture : public Texture { + public: + struct PaintCall { + SkCanvas& canvas; + SkRect bounds; + bool freeze; + GrContext* context; + }; + + explicit MockTexture(int64_t textureId); + + // Called from GPU thread. + void Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) override; + + void OnGrContextCreated() override { gr_context_created_ = true; } + void OnGrContextDestroyed() override { gr_context_destroyed_ = true; } + void MarkNewFrameAvailable() override {} + void OnTextureUnregistered() override { unregistered_ = true; } + + const std::vector& paint_calls() { return paint_calls_; } + bool gr_context_created() { return gr_context_created_; } + bool gr_context_destroyed() { return gr_context_destroyed_; } + bool unregistered() { return unregistered_; } + + private: + std::vector paint_calls_; + bool gr_context_created_ = false; + bool gr_context_destroyed_ = false; + bool unregistered_ = false; +}; + +extern bool operator==(const MockTexture::PaintCall& a, + const MockTexture::PaintCall& b); +extern std::ostream& operator<<(std::ostream& os, + const MockTexture::PaintCall& data); + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_texture_unittests.cc b/flow/testing/mock_texture_unittests.cc new file mode 100644 index 0000000000000..107eb76307928 --- /dev/null +++ b/flow/testing/mock_texture_unittests.cc @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_texture.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(MockTextureTest, Callbacks) { + auto texture = std::make_shared(0); + + ASSERT_FALSE(texture->gr_context_created()); + texture->OnGrContextCreated(); + ASSERT_TRUE(texture->gr_context_created()); + + ASSERT_FALSE(texture->gr_context_destroyed()); + texture->OnGrContextDestroyed(); + ASSERT_TRUE(texture->gr_context_destroyed()); + + ASSERT_FALSE(texture->unregistered()); + texture->OnTextureUnregistered(); + ASSERT_TRUE(texture->unregistered()); +} + +TEST(MockTextureTest, PaintCalls) { + SkCanvas canvas; + const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); + const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); + const auto expected_paint_calls = + std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr}, + MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr}}; + auto texture = std::make_shared(0); + + texture->Paint(canvas, paint_bounds1, false, nullptr); + texture->Paint(canvas, paint_bounds2, true, nullptr); + EXPECT_EQ(texture->paint_calls(), expected_paint_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/skia_gpu_object_layer_test.cc b/flow/testing/skia_gpu_object_layer_test.cc new file mode 100644 index 0000000000000..1fca8ec8f3b81 --- /dev/null +++ b/flow/testing/skia_gpu_object_layer_test.cc @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/skia_gpu_object_layer_test.h" + +#include "flutter/fml/time/time_delta.h" + +namespace flutter { +namespace testing { + +SkiaGPUObjectLayerTest::SkiaGPUObjectLayerTest() + : unref_queue_(fml::MakeRefCounted( + GetCurrentTaskRunner(), + fml::TimeDelta::FromSeconds(0))) {} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/skia_gpu_object_layer_test.h b/flow/testing/skia_gpu_object_layer_test.h new file mode 100644 index 0000000000000..d573ac0b41007 --- /dev/null +++ b/flow/testing/skia_gpu_object_layer_test.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ +#define FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ + +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/flow/testing/layer_test.h" +#include "flutter/testing/thread_test.h" + +namespace flutter { +namespace testing { + +// This fixture allows generating tests that create |SkiaGPUObject|'s which +// are destroyed on a |SkiaUnrefQueue|. +class SkiaGPUObjectLayerTest : public LayerTestBase { + public: + SkiaGPUObjectLayerTest(); + + fml::RefPtr unref_queue() { return unref_queue_; } + + private: + fml::RefPtr unref_queue_; +}; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_SKIA_GPU_OBJECT_LAYER_TEST_H_ diff --git a/flow/texture.cc b/flow/texture.cc index 6f25c6df89593..15c93d360366e 100644 --- a/flow/texture.cc +++ b/flow/texture.cc @@ -6,9 +6,11 @@ namespace flutter { -TextureRegistry::TextureRegistry() = default; +Texture::Texture(int64_t id) : id_(id) {} -TextureRegistry::~TextureRegistry() = default; +Texture::~Texture() = default; + +TextureRegistry::TextureRegistry() = default; void TextureRegistry::RegisterTexture(std::shared_ptr texture) { mapping_[texture->Id()] = texture; @@ -36,8 +38,4 @@ std::shared_ptr TextureRegistry::GetTexture(int64_t id) { return it != mapping_.end() ? it->second : nullptr; } -Texture::Texture(int64_t id) : id_(id) {} - -Texture::~Texture() = default; - } // namespace flutter diff --git a/flow/texture.h b/flow/texture.h index 6e06445884b66..812588d382bb1 100644 --- a/flow/texture.h +++ b/flow/texture.h @@ -14,12 +14,9 @@ namespace flutter { class Texture { - protected: - Texture(int64_t id); - public: - // Called from GPU thread. - virtual ~Texture(); + Texture(int64_t id); // Called from UI or GPU thread. + virtual ~Texture(); // Called from GPU thread. // Called from GPU thread. virtual void Paint(SkCanvas& canvas, @@ -50,7 +47,6 @@ class Texture { class TextureRegistry { public: TextureRegistry(); - ~TextureRegistry(); // Called from GPU thread. void RegisterTexture(std::shared_ptr texture); diff --git a/flow/texture_unittests.cc b/flow/texture_unittests.cc index d292e3965af87..f3eb0fc0931ac 100644 --- a/flow/texture_unittests.cc +++ b/flow/texture_unittests.cc @@ -2,44 +2,97 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/flow/testing/mock_texture.h" #include "flutter/flow/texture.h" + #include "gtest/gtest.h" namespace flutter { namespace testing { -class MockTexture : public Texture { - public: - MockTexture(int64_t textureId) : Texture(textureId) {} +TEST(TextureRegistryTest, UnregisterTextureCallbackTriggered) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(1); + + registry.RegisterTexture(mock_texture1); + registry.RegisterTexture(mock_texture2); + ASSERT_EQ(registry.GetTexture(0), mock_texture1); + ASSERT_EQ(registry.GetTexture(1), mock_texture2); + ASSERT_FALSE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); + + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); + + registry.UnregisterTexture(1); + ASSERT_EQ(registry.GetTexture(1), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_TRUE(mock_texture2->unregistered()); +} + +TEST(TextureRegistryTest, GrContextCallbackTriggered) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(1); + + registry.RegisterTexture(mock_texture1); + registry.RegisterTexture(mock_texture2); + ASSERT_FALSE(mock_texture1->gr_context_created()); + ASSERT_FALSE(mock_texture2->gr_context_created()); + ASSERT_FALSE(mock_texture1->gr_context_destroyed()); + ASSERT_FALSE(mock_texture2->gr_context_destroyed()); - ~MockTexture() override = default; + registry.OnGrContextCreated(); + ASSERT_TRUE(mock_texture1->gr_context_created()); + ASSERT_TRUE(mock_texture2->gr_context_created()); - // Called from GPU thread. - void Paint(SkCanvas& canvas, - const SkRect& bounds, - bool freeze, - GrContext* context) override {} + registry.UnregisterTexture(0); + registry.OnGrContextDestroyed(); + ASSERT_FALSE(mock_texture1->gr_context_destroyed()); + ASSERT_TRUE(mock_texture2->gr_context_created()); +} - void OnGrContextCreated() override {} +TEST(TextureRegistryTest, RegisterTextureTwice) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(0); - void OnGrContextDestroyed() override {} + registry.RegisterTexture(mock_texture1); + ASSERT_EQ(registry.GetTexture(0), mock_texture1); + registry.RegisterTexture(mock_texture2); + ASSERT_EQ(registry.GetTexture(0), mock_texture2); + ASSERT_FALSE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); + + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_FALSE(mock_texture1->unregistered()); + ASSERT_TRUE(mock_texture2->unregistered()); +} - void MarkNewFrameAvailable() override {} +TEST(TextureRegistryTest, ReuseSameTextureSlot) { + TextureRegistry registry; + auto mock_texture1 = std::make_shared(0); + auto mock_texture2 = std::make_shared(0); - void OnTextureUnregistered() override { unregistered_ = true; } + registry.RegisterTexture(mock_texture1); + ASSERT_EQ(registry.GetTexture(0), mock_texture1); - bool unregistered() { return unregistered_; } + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_FALSE(mock_texture2->unregistered()); - private: - bool unregistered_ = false; -}; + registry.RegisterTexture(mock_texture2); + ASSERT_EQ(registry.GetTexture(0), mock_texture2); -TEST(TextureRegistry, UnregisterTextureCallbackTriggered) { - TextureRegistry textureRegistry; - std::shared_ptr mockTexture = std::make_shared(0); - textureRegistry.RegisterTexture(mockTexture); - textureRegistry.UnregisterTexture(0); - ASSERT_TRUE(mockTexture->unregistered()); + registry.UnregisterTexture(0); + ASSERT_EQ(registry.GetTexture(0), nullptr); + ASSERT_TRUE(mock_texture1->unregistered()); + ASSERT_TRUE(mock_texture2->unregistered()); } } // namespace testing diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 68b9725fa6b2e..21ce86c58ceed 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -2,12 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/fuchsia/sdk.gni") -import("$flutter_root/testing/testing.gni") - if (is_fuchsia) { + import("//build/fuchsia/sdk.gni") import("$flutter_root/tools/fuchsia/fuchsia_archive.gni") } +import("$flutter_root/testing/testing.gni") source_set("fml") { sources = [ diff --git a/runtime/runtime_test.cc b/runtime/runtime_test.cc index 455a52c95a67a..295d3daf2bc0e 100644 --- a/runtime/runtime_test.cc +++ b/runtime/runtime_test.cc @@ -11,9 +11,10 @@ namespace flutter { namespace testing { RuntimeTest::RuntimeTest() - : native_resolver_(std::make_shared()) {} - -RuntimeTest::~RuntimeTest() = default; + : native_resolver_(std::make_shared()), + assets_dir_(fml::OpenDirectory(GetFixturesPath(), + false, + fml::FilePermission::kRead)) {} void RuntimeTest::SetSnapshotsAndAssets(Settings& settings) { if (!assets_dir_.is_valid()) { @@ -67,19 +68,6 @@ Settings RuntimeTest::CreateSettingsForFixture() { return settings; } -// |testing::ThreadTest| -void RuntimeTest::SetUp() { - assets_dir_ = - fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); - ThreadTest::SetUp(); -} - -// |testing::ThreadTest| -void RuntimeTest::TearDown() { - ThreadTest::TearDown(); - assets_dir_.reset(); -} - void RuntimeTest::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_resolver_->AddNativeCallback(std::move(name), callback); diff --git a/runtime/runtime_test.h b/runtime/runtime_test.h index 8c60a6e858a6c..abce2e94970f9 100644 --- a/runtime/runtime_test.h +++ b/runtime/runtime_test.h @@ -19,24 +19,17 @@ class RuntimeTest : public ThreadTest { public: RuntimeTest(); - ~RuntimeTest(); - Settings CreateSettingsForFixture(); void AddNativeCallback(std::string name, Dart_NativeFunction callback); - protected: - // |testing::ThreadTest| - void SetUp() override; - - // |testing::ThreadTest| - void TearDown() override; - private: - fml::UniqueFD assets_dir_; + void SetSnapshotsAndAssets(Settings& settings); + std::shared_ptr native_resolver_; + fml::UniqueFD assets_dir_; - void SetSnapshotsAndAssets(Settings& settings); + FML_DISALLOW_COPY_AND_ASSIGN(RuntimeTest); }; } // namespace testing diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index ed7f1febaf51c..97a382dcacbbd 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -19,9 +19,13 @@ namespace flutter { namespace testing { ShellTest::ShellTest() - : native_resolver_(std::make_shared()) {} - -ShellTest::~ShellTest() = default; + : native_resolver_(std::make_shared()), + thread_host_("io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::Platform | ThreadHost::Type::IO | + ThreadHost::Type::UI | ThreadHost::Type::GPU), + assets_dir_(fml::OpenDirectory(GetFixturesPath(), + false, + fml::FilePermission::kRead)) {} void ShellTest::SendEnginePlatformMessage( Shell* shell, @@ -225,10 +229,10 @@ Settings ShellTest::CreateSettingsForFixture() { TaskRunners ShellTest::GetTaskRunnersForFixture() { return { "test", - thread_host_->platform_thread->GetTaskRunner(), // platform - thread_host_->gpu_thread->GetTaskRunner(), // gpu - thread_host_->ui_thread->GetTaskRunner(), // ui - thread_host_->io_thread->GetTaskRunner() // io + thread_host_.platform_thread->GetTaskRunner(), // platform + thread_host_.gpu_thread->GetTaskRunner(), // gpu + thread_host_.ui_thread->GetTaskRunner(), // ui + thread_host_.io_thread->GetTaskRunner() // io }; } @@ -267,24 +271,6 @@ void ShellTest::DestroyShell(std::unique_ptr shell, latch.Wait(); } -// |testing::ThreadTest| -void ShellTest::SetUp() { - ThreadTest::SetUp(); - assets_dir_ = - fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); - thread_host_ = std::make_unique( - "io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI | - ThreadHost::Type::GPU); -} - -// |testing::ThreadTest| -void ShellTest::TearDown() { - ThreadTest::TearDown(); - assets_dir_.reset(); - thread_host_.reset(); -} - void ShellTest::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_resolver_->AddNativeCallback(std::move(name), callback); diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 270ea812dbf8a..0ae092a1c91bf 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -26,8 +26,6 @@ class ShellTest : public ThreadTest { public: ShellTest(); - ~ShellTest(); - Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); @@ -78,19 +76,14 @@ class ShellTest : public ThreadTest { // is unpredictive. static int UnreportedTimingsCount(Shell* shell); - protected: - // |testing::ThreadTest| - void SetUp() override; - - // |testing::ThreadTest| - void TearDown() override; - private: - fml::UniqueFD assets_dir_; + void SetSnapshotsAndAssets(Settings& settings); + std::shared_ptr native_resolver_; - std::unique_ptr thread_host_; + ThreadHost thread_host_; + fml::UniqueFD assets_dir_; - void SetSnapshotsAndAssets(Settings& settings); + FML_DISALLOW_COPY_AND_ASSIGN(ShellTest); }; class ShellTestVsyncClock { diff --git a/shell/platform/embedder/tests/embedder_test.cc b/shell/platform/embedder/tests/embedder_test.cc index cc699c26e463a..bca2e0eb5f258 100644 --- a/shell/platform/embedder/tests/embedder_test.cc +++ b/shell/platform/embedder/tests/embedder_test.cc @@ -9,14 +9,12 @@ namespace testing { EmbedderTest::EmbedderTest() = default; -EmbedderTest::~EmbedderTest() = default; - std::string EmbedderTest::GetFixturesDirectory() const { return GetFixturesPath(); } EmbedderTestContext& EmbedderTest::GetEmbedderContext() { - // Setup the embedder context lazily instead of in the SetUp method because we + // Setup the embedder context lazily instead of in the constructor because we // don't to do all the work if the test won't end up using context. if (!embedder_context_) { embedder_context_ = @@ -25,16 +23,5 @@ EmbedderTestContext& EmbedderTest::GetEmbedderContext() { return *embedder_context_; } -// |testing::Test| -void EmbedderTest::SetUp() { - ThreadTest::SetUp(); -} - -// |testing::Test| -void EmbedderTest::TearDown() { - embedder_context_.reset(); - ThreadTest::TearDown(); -} - } // namespace testing } // namespace flutter diff --git a/shell/platform/embedder/tests/embedder_test.h b/shell/platform/embedder/tests/embedder_test.h index f170a106553dc..15cc101ae24a6 100644 --- a/shell/platform/embedder/tests/embedder_test.h +++ b/shell/platform/embedder/tests/embedder_test.h @@ -19,8 +19,6 @@ class EmbedderTest : public ThreadTest { public: EmbedderTest(); - ~EmbedderTest() override; - std::string GetFixturesDirectory() const; EmbedderTestContext& GetEmbedderContext(); @@ -28,12 +26,6 @@ class EmbedderTest : public ThreadTest { private: std::unique_ptr embedder_context_; - // |testing::Test| - void SetUp() override; - - // |testing::Test| - void TearDown() override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTest); }; diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index bdfec3cd2e4d0..244761694a8dc 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -16,6 +16,7 @@ "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx index 912b534df93a2..1119b279ddd6c 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -16,6 +16,7 @@ "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/testing/BUILD.gn b/testing/BUILD.gn index a1028b0f18952..8bf9d7bc3922f 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -54,7 +54,11 @@ source_set("skia") { testonly = true sources = [ + "$flutter_root/testing/assertions_skia.cc", "$flutter_root/testing/assertions_skia.h", + "$flutter_root/testing/canvas_test.h", + "$flutter_root/testing/mock_canvas.cc", + "$flutter_root/testing/mock_canvas.h", ] public_deps = [ @@ -118,7 +122,8 @@ if (current_toolchain == host_toolchain) { testonly = true sources = [ - "$flutter_root/testing/test_metal_surface_unittests.cc", + "mock_canvas_unittests.cc", + "test_metal_surface_unittests.cc", ] deps = [ diff --git a/testing/assertions_skia.cc b/testing/assertions_skia.cc new file mode 100644 index 0000000000000..e8b7ce992b8a8 --- /dev/null +++ b/testing/assertions_skia.cc @@ -0,0 +1,118 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/assertions_skia.h" + +namespace flutter { +namespace testing { + +std::ostream& operator<<(std::ostream& os, const SkClipOp& o) { + switch (o) { + case SkClipOp::kDifference: + os << "ClipOpDifference"; + break; + case SkClipOp::kIntersect: + os << "ClipOpIntersect"; + break; +#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS + case SkClipOp::kUnion_deprecated: + os << "ClipOpUnion_deprecated"; + break; + case SkClipOp::kXOR_deprecated: + os << "ClipOpXOR_deprecated"; + break; + case SkClipOp::kReverseDifference_deprecated: + os << "ClipOpReverseDifference_deprecated"; + break; + case SkClipOp::kReplace_deprecated: + os << "ClipOpReplace_deprectaed"; + break; +#else + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway2: + os << "ClipOpReserved2"; + break; + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway3: + os << "ClipOpReserved3"; + break; + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway4: + os << "ClipOpReserved4"; + break; + case SkClipOp::kExtraEnumNeedInternallyPleaseIgnoreWillGoAway5: + os << "ClipOpReserved5"; + break; +#endif + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { + os << std::endl; + os << "Scale X: " << m[SkMatrix::kMScaleX] << ", "; + os << "Skew X: " << m[SkMatrix::kMSkewX] << ", "; + os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl; + os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", "; + os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", "; + os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl; + os << "Persp X: " << m[SkMatrix::kMPersp0] << ", "; + os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", "; + os << "Persp Z: " << m[SkMatrix::kMPersp2]; + os << std::endl; + return os; +} + +std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { + os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " + << m.get(0, 3) << std::endl; + os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " + << m.get(1, 3) << std::endl; + os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " + << m.get(2, 3) << std::endl; + os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " + << m.get(3, 3); + return os; +} + +std::ostream& operator<<(std::ostream& os, const SkVector3& v) { + return os << v.x() << ", " << v.y() << ", " << v.z(); +} + +std::ostream& operator<<(std::ostream& os, const SkVector4& v) { + return os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", " + << v.fData[3]; +} + +std::ostream& operator<<(std::ostream& os, const SkRect& r) { + return os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", " + << r.fBottom; +} + +std::ostream& operator<<(std::ostream& os, const SkRRect& r) { + return os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", " + << r.rect().fRight << ", " << r.rect().fBottom; +} + +std::ostream& operator<<(std::ostream& os, const SkPath& r) { + return os << "Valid: " << r.isValid() << ", FillType: " << r.getFillType() + << ", Bounds: " << r.getBounds(); +} + +std::ostream& operator<<(std::ostream& os, const SkPoint& r) { + return os << "XY: " << r.fX << ", " << r.fY; +} + +std::ostream& operator<<(std::ostream& os, const SkISize& size) { + return os << size.width() << ", " << size.height(); +} + +std::ostream& operator<<(std::ostream& os, const SkColor4f& r) { + return os << r.fR << ", " << r.fG << ", " << r.fB << ", " << r.fA; +} + +std::ostream& operator<<(std::ostream& os, const SkPaint& r) { + return os << "Color: " << r.getColor4f() << ", Style: " << r.getStyle() + << ", AA: " << r.isAntiAlias() << ", Shader: " << r.getShader(); +} + +} // namespace testing +} // namespace flutter diff --git a/testing/assertions_skia.h b/testing/assertions_skia.h index 2b501189a23ae..f1eec1897c426 100644 --- a/testing/assertions_skia.h +++ b/testing/assertions_skia.h @@ -7,73 +7,31 @@ #include +#include "third_party/skia/include/core/SkClipOp.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkMatrix44.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint3.h" #include "third_party/skia/include/core/SkRRect.h" -//------------------------------------------------------------------------------ -// Printing -//------------------------------------------------------------------------------ - -inline std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { - os << std::endl; - os << "Scale X: " << m[SkMatrix::kMScaleX] << ", "; - os << "Skew X: " << m[SkMatrix::kMSkewX] << ", "; - os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl; - os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", "; - os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", "; - os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl; - os << "Persp X: " << m[SkMatrix::kMPersp0] << ", "; - os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", "; - os << "Persp Z: " << m[SkMatrix::kMPersp2]; - os << std::endl; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { - os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " - << m.get(0, 3) << std::endl; - os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " - << m.get(1, 3) << std::endl; - os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " - << m.get(2, 3) << std::endl; - os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " - << m.get(3, 3); - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkVector3& v) { - os << v.x() << ", " << v.y() << ", " << v.z(); - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkVector4& v) { - os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", " - << v.fData[3]; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkRect& r) { - os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", " - << r.fBottom; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkRRect& r) { - os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", " - << r.rect().fRight << ", " << r.rect().fBottom; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkPoint& r) { - os << "XY: " << r.fX << ", " << r.fY; - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const SkISize& size) { - os << size.width() << ", " << size.height(); - return os; -} +namespace flutter { +namespace testing { + +extern std::ostream& operator<<(std::ostream& os, const SkClipOp& o); +extern std::ostream& operator<<(std::ostream& os, const SkMatrix& m); +extern std::ostream& operator<<(std::ostream& os, const SkMatrix44& m); +extern std::ostream& operator<<(std::ostream& os, const SkVector3& v); +extern std::ostream& operator<<(std::ostream& os, const SkVector4& v); +extern std::ostream& operator<<(std::ostream& os, const SkRect& r); +extern std::ostream& operator<<(std::ostream& os, const SkRRect& r); +extern std::ostream& operator<<(std::ostream& os, const SkPath& r); +extern std::ostream& operator<<(std::ostream& os, const SkPoint& r); +extern std::ostream& operator<<(std::ostream& os, const SkISize& size); +extern std::ostream& operator<<(std::ostream& os, const SkColor4f& r); +extern std::ostream& operator<<(std::ostream& os, const SkPaint& r); + +} // namespace testing +} // namespace flutter #endif // FLUTTER_TESTING_ASSERTIONS_SKIA_H_ diff --git a/testing/canvas_test.h b/testing/canvas_test.h new file mode 100644 index 0000000000000..80c157a305dc4 --- /dev/null +++ b/testing/canvas_test.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TESTING_CANVAS_TEST_H_ +#define TESTING_CANVAS_TEST_H_ + +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +// This fixture allows creating tests that make use of a mock |SkCanvas|. +template +class CanvasTestBase : public BaseT { + public: + CanvasTestBase() = default; + + MockCanvas& mock_canvas() { return canvas_; } + + private: + MockCanvas canvas_; + + FML_DISALLOW_COPY_AND_ASSIGN(CanvasTestBase); +}; +using CanvasTest = CanvasTestBase<::testing::Test>; + +} // namespace testing +} // namespace flutter + +#endif // TESTING_CANVAS_TEST_H_ diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc new file mode 100644 index 0000000000000..6782ffdeda733 --- /dev/null +++ b/testing/mock_canvas.cc @@ -0,0 +1,457 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/mock_canvas.h" + +#include "flutter/fml/logging.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkSerialProcs.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +namespace flutter { +namespace testing { + +constexpr SkISize kSize = SkISize::Make(64, 64); + +MockCanvas::MockCanvas() + : SkCanvasVirtualEnforcer(kSize.fWidth, kSize.fHeight), + internal_canvas_(imageInfo().width(), imageInfo().height()), + current_layer_(0) { + internal_canvas_.addCanvas(this); +} + +MockCanvas::~MockCanvas() { + EXPECT_EQ(current_layer_, 0); +} + +void MockCanvas::willSave() { + draw_calls_.emplace_back( + DrawCall{current_layer_, SaveData{current_layer_ + 1}}); + current_layer_++; // Must go here; func params order of eval is undefined +} + +SkCanvas::SaveLayerStrategy MockCanvas::getSaveLayerStrategy( + const SaveLayerRec& rec) { + // saveLayer calls this prior to running, so we use it to track saveLayer + // calls + draw_calls_.emplace_back(DrawCall{ + current_layer_, + SaveLayerData{rec.fBounds ? *rec.fBounds : SkRect(), + rec.fPaint ? *rec.fPaint : SkPaint(), + rec.fBackdrop ? sk_ref_sp(rec.fBackdrop) + : sk_sp(), + current_layer_ + 1}}); + current_layer_++; // Must go here; func params order of eval is undefined + return kNoLayer_SaveLayerStrategy; +} + +void MockCanvas::willRestore() { + FML_DCHECK(current_layer_ > 0); + + draw_calls_.emplace_back( + DrawCall{current_layer_, RestoreData{current_layer_ - 1}}); + current_layer_--; // Must go here; func params order of eval is undefined +} + +void MockCanvas::didConcat(const SkMatrix& matrix) { + draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{matrix}}); +} + +void MockCanvas::didSetMatrix(const SkMatrix& matrix) { + draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{matrix}}); +} + +void MockCanvas::onDrawTextBlob(const SkTextBlob* text, + SkScalar x, + SkScalar y, + const SkPaint& paint) { + // This duplicates existing logic in SkCanvas::onDrawPicture + // that should probably be split out so it doesn't need to be here as well. + SkRect storage; + const SkRect* bounds = nullptr; + if (paint.canComputeFastBounds()) { + storage = text->bounds().makeOffset(x, y); + SkRect tmp; + if (this->quickReject(paint.computeFastBounds(storage, &tmp))) { + return; + } + bounds = &storage; + } + + draw_calls_.emplace_back(DrawCall{ + current_layer_, DrawTextData{text ? text->serialize(SkSerialProcs{}) + : SkData::MakeUninitialized(0), + paint, SkPoint::Make(x, y)}}); +} + +void MockCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { + draw_calls_.emplace_back(DrawCall{current_layer_, DrawRectData{rect, paint}}); +} + +void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { + draw_calls_.emplace_back(DrawCall{current_layer_, DrawPathData{path, paint}}); +} + +void MockCanvas::onDrawShadowRec(const SkPath& path, + const SkDrawShadowRec& rec) { + (void)rec; // Can't use b/c Skia keeps this type anonymous. + draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}}); +} + +void MockCanvas::onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) { + // This duplicates existing logic in SkCanvas::onDrawPicture + // that should probably be split out so it doesn't need to be here as well. + if (!paint || paint->canComputeFastBounds()) { + SkRect bounds = picture->cullRect(); + if (paint) { + paint->computeFastBounds(bounds, &bounds); + } + if (matrix) { + matrix->mapRect(&bounds); + } + if (this->quickReject(bounds)) { + return; + } + } + + draw_calls_.emplace_back(DrawCall{ + current_layer_, + DrawPictureData{ + picture ? picture->serialize() : SkData::MakeUninitialized(0), + paint ? *paint : SkPaint(), matrix ? *matrix : SkMatrix()}}); +} + +void MockCanvas::onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle style) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipRectData{rect, op, style}}); +} + +void MockCanvas::onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle style) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipRRectData{rrect, op, style}}); +} + +void MockCanvas::onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle style) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipPathData{path, op, style}}); +} + +bool MockCanvas::onDoSaveBehind(const SkRect*) { + FML_DCHECK(false); + return false; +} + +void MockCanvas::onDrawAnnotation(const SkRect&, const char[], SkData*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawDrawable(SkDrawable*, const SkMatrix*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawPatch(const SkPoint[12], + const SkColor[4], + const SkPoint[4], + SkBlendMode, + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawPaint(const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBehind(const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawPoints(PointMode, + size_t, + const SkPoint[], + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawRegion(const SkRegion&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawOval(const SkRect&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawArc(const SkRect&, + SkScalar, + SkScalar, + bool, + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmap(const SkBitmap&, + SkScalar, + SkScalar, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImage(const SkImage*, + SkScalar, + SkScalar, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmapRect(const SkBitmap&, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImageRect(const SkImage*, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImageNine(const SkImage*, + const SkIRect&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmapNine(const SkBitmap&, + const SkIRect&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawImageLattice(const SkImage*, + const Lattice&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawBitmapLattice(const SkBitmap&, + const Lattice&, + const SkRect&, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawVerticesObject(const SkVertices*, + const SkVertices::Bone[], + int, + SkBlendMode, + const SkPaint&) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawAtlas(const SkImage*, + const SkRSXform[], + const SkRect[], + const SkColor[], + int, + SkBlendMode, + const SkRect*, + const SkPaint*) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawEdgeAAQuad(const SkRect&, + const SkPoint[4], + QuadAAFlags, + const SkColor4f&, + SkBlendMode) { + FML_DCHECK(false); +} + +void MockCanvas::onDrawEdgeAAImageSet(const ImageSetEntry[], + int, + const SkPoint[], + const SkMatrix[], + const SkPaint*, + SrcRectConstraint) { + FML_DCHECK(false); +} + +void MockCanvas::onClipRegion(const SkRegion&, SkClipOp) { + FML_DCHECK(false); +} + +bool operator==(const MockCanvas::SaveData& a, const MockCanvas::SaveData& b) { + return a.save_to_layer == b.save_to_layer; +} + +std::ostream& operator<<(std::ostream& os, const MockCanvas::SaveData& data) { + return os << data.save_to_layer; +} + +bool operator==(const MockCanvas::SaveLayerData& a, + const MockCanvas::SaveLayerData& b) { + return a.save_bounds == b.save_bounds && a.restore_paint == b.restore_paint && + a.backdrop_filter == b.backdrop_filter && + a.save_to_layer == b.save_to_layer; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::SaveLayerData& data) { + return os << data.save_bounds << " " << data.restore_paint << " " + << data.backdrop_filter << " " << data.save_to_layer; +} + +bool operator==(const MockCanvas::RestoreData& a, + const MockCanvas::RestoreData& b) { + return a.restore_to_layer == b.restore_to_layer; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::RestoreData& data) { + return os << data.restore_to_layer; +} + +bool operator==(const MockCanvas::ConcatMatrixData& a, + const MockCanvas::ConcatMatrixData& b) { + return a.matrix == b.matrix; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ConcatMatrixData& data) { + return os << data.matrix; +} + +bool operator==(const MockCanvas::SetMatrixData& a, + const MockCanvas::SetMatrixData& b) { + return a.matrix == b.matrix; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::SetMatrixData& data) { + return os << data.matrix; +} + +bool operator==(const MockCanvas::DrawRectData& a, + const MockCanvas::DrawRectData& b) { + return a.rect == b.rect && a.paint == b.paint; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawRectData& data) { + return os << data.rect << " " << data.paint; +} + +bool operator==(const MockCanvas::DrawPathData& a, + const MockCanvas::DrawPathData& b) { + return a.path == b.path && a.paint == b.paint; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPathData& data) { + return os << data.path << " " << data.paint; +} + +bool operator==(const MockCanvas::DrawTextData& a, + const MockCanvas::DrawTextData& b) { + return a.serialized_text->equals(b.serialized_text.get()) && + a.paint == b.paint && a.offset == b.offset; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawTextData& data) { + return os << data.serialized_text << " " << data.paint << " " << data.offset; +} + +bool operator==(const MockCanvas::DrawPictureData& a, + const MockCanvas::DrawPictureData& b) { + return a.serialized_picture->equals(b.serialized_picture.get()) && + a.paint == b.paint && a.matrix == b.matrix; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPictureData& data) { + return os << data.serialized_picture << " " << data.paint << " " + << data.matrix; +} + +bool operator==(const MockCanvas::DrawShadowData& a, + const MockCanvas::DrawShadowData& b) { + return a.path == b.path; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawShadowData& data) { + return os << data.path; +} + +bool operator==(const MockCanvas::ClipRectData& a, + const MockCanvas::ClipRectData& b) { + return a.rect == b.rect && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRectData& data) { + return os << data.rect << " " << data.clip_op << " " << data.style; +} + +bool operator==(const MockCanvas::ClipRRectData& a, + const MockCanvas::ClipRRectData& b) { + return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRRectData& data) { + return os << data.rrect << " " << data.clip_op << " " << data.style; +} + +bool operator==(const MockCanvas::ClipPathData& a, + const MockCanvas::ClipPathData& b) { + return a.path == b.path && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipPathData& data) { + return os << data.path << " " << data.clip_op << " " << data.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawCallData& data) { + std::visit([&os](auto& d) { os << d; }, data); + return os; +} + +bool operator==(const MockCanvas::DrawCall& a, const MockCanvas::DrawCall& b) { + return a.layer == b.layer && a.data == b.data; +} + +std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawCall& draw) { + return os << "[Layer: " << draw.layer << ", Data: " << draw.data << "]"; +} + +} // namespace testing +} // namespace flutter diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h new file mode 100644 index 0000000000000..cc4c2b11a9c2e --- /dev/null +++ b/testing/mock_canvas.h @@ -0,0 +1,318 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TESTING_MOCK_CANVAS_H_ +#define TESTING_MOCK_CANVAS_H_ + +#include +#include +#include + +#include "flutter/testing/assertions_skia.h" +#include "gtest/gtest.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" +#include "third_party/skia/include/core/SkClipOp.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" + +namespace flutter { +namespace testing { + +static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); + +// Mock |SkCanvas|, useful for writing tests that use Skia but do not interact +// with the GPU. +// +// The |MockCanvas| stores a list of |DrawCall| that the test can later verify +// against the expected list of primitives to be drawn. +class MockCanvas : public SkCanvasVirtualEnforcer { + public: + using SkCanvas::kHard_ClipEdgeStyle; + using SkCanvas::kSoft_ClipEdgeStyle; + + struct SaveData { + int save_to_layer; + }; + + struct SaveLayerData { + SkRect save_bounds; + SkPaint restore_paint; + sk_sp backdrop_filter; + int save_to_layer; + }; + + struct RestoreData { + int restore_to_layer; + }; + + struct ConcatMatrixData { + SkMatrix matrix; + }; + + struct SetMatrixData { + SkMatrix matrix; + }; + + struct DrawRectData { + SkRect rect; + SkPaint paint; + }; + + struct DrawPathData { + SkPath path; + SkPaint paint; + }; + + struct DrawTextData { + sk_sp serialized_text; + SkPaint paint; + SkPoint offset; + }; + + struct DrawPictureData { + sk_sp serialized_picture; + SkPaint paint; + SkMatrix matrix; + }; + + struct DrawShadowData { + SkPath path; + }; + + struct ClipRectData { + SkRect rect; + SkClipOp clip_op; + ClipEdgeStyle style; + }; + + struct ClipRRectData { + SkRRect rrect; + SkClipOp clip_op; + ClipEdgeStyle style; + }; + + struct ClipPathData { + SkPath path; + SkClipOp clip_op; + ClipEdgeStyle style; + }; + + // Discriminated union of all the different |DrawCall| types. It is roughly + // equivalent to the different methods in |SkCanvas|' public API. + using DrawCallData = std::variant; + + // A single call made against this canvas. + struct DrawCall { + int layer; + DrawCallData data; + }; + + MockCanvas(); + ~MockCanvas() override; + + SkNWayCanvas* internal_canvas() { return &internal_canvas_; } + + const std::vector& draw_calls() const { return draw_calls_; } + + protected: + // Save/restore/set operations that we track. + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; + void willRestore() override; + void didRestore() override {} + void didConcat(const SkMatrix& matrix) override; + void didSetMatrix(const SkMatrix& matrix) override; + + // Draw and clip operations that we track. + void onDrawRect(const SkRect& rect, const SkPaint& paint) override; + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + void onDrawTextBlob(const SkTextBlob* text, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override; + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override; + void onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle style) override; + void onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle style) override; + void onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle style) override; + + // Operations that we don't track. + bool onDoSaveBehind(const SkRect*) override; + void onDrawAnnotation(const SkRect&, const char[], SkData*) override; + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawDrawable(SkDrawable*, const SkMatrix*) override; + void onDrawPatch(const SkPoint[12], + const SkColor[4], + const SkPoint[4], + SkBlendMode, + const SkPaint&) override; + void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint&) override; + void onDrawPoints(PointMode, + size_t, + const SkPoint[], + const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, + SkScalar, + SkScalar, + bool, + const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawBitmapRect(const SkBitmap&, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImage(const SkImage* image, + SkScalar x, + SkScalar y, + const SkPaint* paint) override; + void onDrawImageRect(const SkImage*, + const SkRect*, + const SkRect&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImageNine(const SkImage*, + const SkIRect&, + const SkRect&, + const SkPaint*) override; + void onDrawBitmap(const SkBitmap& bitmap, + SkScalar x, + SkScalar y, + const SkPaint* paint) override; + void onDrawBitmapNine(const SkBitmap&, + const SkIRect&, + const SkRect&, + const SkPaint*) override; + void onDrawImageLattice(const SkImage*, + const Lattice&, + const SkRect&, + const SkPaint*) override; + void onDrawBitmapLattice(const SkBitmap&, + const Lattice&, + const SkRect&, + const SkPaint*) override; + void onDrawVerticesObject(const SkVertices*, + const SkVertices::Bone[], + int, + SkBlendMode, + const SkPaint&) override; + void onDrawAtlas(const SkImage*, + const SkRSXform[], + const SkRect[], + const SkColor[], + int, + SkBlendMode, + const SkRect*, + const SkPaint*) override; + void onDrawEdgeAAQuad(const SkRect&, + const SkPoint[4], + QuadAAFlags, + const SkColor4f&, + SkBlendMode) override; + void onDrawEdgeAAImageSet(const ImageSetEntry[], + int, + const SkPoint[], + const SkMatrix[], + const SkPaint*, + SrcRectConstraint) override; + void onClipRegion(const SkRegion&, SkClipOp) override; + + private: + SkNWayCanvas internal_canvas_; + + std::vector draw_calls_; + int current_layer_; +}; + +extern bool operator==(const MockCanvas::SaveData& a, + const MockCanvas::SaveData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::SaveData& data); +extern bool operator==(const MockCanvas::SaveLayerData& a, + const MockCanvas::SaveLayerData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::SaveLayerData& data); +extern bool operator==(const MockCanvas::RestoreData& a, + const MockCanvas::RestoreData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::RestoreData& data); +extern bool operator==(const MockCanvas::ConcatMatrixData& a, + const MockCanvas::ConcatMatrixData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ConcatMatrixData& data); +extern bool operator==(const MockCanvas::SetMatrixData& a, + const MockCanvas::SetMatrixData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::SetMatrixData& data); +extern bool operator==(const MockCanvas::DrawRectData& a, + const MockCanvas::DrawRectData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawRectData& data); +extern bool operator==(const MockCanvas::DrawPathData& a, + const MockCanvas::DrawPathData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPathData& data); +extern bool operator==(const MockCanvas::DrawTextData& a, + const MockCanvas::DrawTextData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawTextData& data); +extern bool operator==(const MockCanvas::DrawPictureData& a, + const MockCanvas::DrawPictureData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawPictureData& data); +extern bool operator==(const MockCanvas::DrawShadowData& a, + const MockCanvas::DrawShadowData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawShadowData& data); +extern bool operator==(const MockCanvas::ClipRectData& a, + const MockCanvas::ClipRectData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRectData& data); +extern bool operator==(const MockCanvas::ClipRRectData& a, + const MockCanvas::ClipRRectData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipRRectData& data); +extern bool operator==(const MockCanvas::ClipPathData& a, + const MockCanvas::ClipPathData& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipPathData& data); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawCallData& data); +extern bool operator==(const MockCanvas::DrawCall& a, + const MockCanvas::DrawCall& b); +extern std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawCall& draw); + +} // namespace testing +} // namespace flutter + +#endif // TESTING_MOCK_CANVAS_H_ diff --git a/testing/mock_canvas_unittests.cc b/testing/mock_canvas_unittests.cc new file mode 100644 index 0000000000000..7e8d624642064 --- /dev/null +++ b/testing/mock_canvas_unittests.cc @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/mock_canvas.h" + +#include "flutter/testing/canvas_test.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +using MockCanvasTest = CanvasTest; + +#ifndef NDEBUG +TEST_F(MockCanvasTest, DrawRRectDies) { + EXPECT_DEATH_IF_SUPPORTED(mock_canvas().drawRRect(SkRRect(), SkPaint()), ""); +} +#endif + +TEST_F(MockCanvasTest, DrawCalls) { + const SkRect rect = SkRect::MakeWH(5.0f, 5.0f); + const SkPaint paint = SkPaint(SkColors::kGreen); + const auto expected_draw_calls = std::vector{ + MockCanvas::DrawCall{0, MockCanvas::DrawRectData{rect, paint}}}; + + mock_canvas().drawRect(rect, paint); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +} // namespace testing +} // namespace flutter diff --git a/testing/thread_test.cc b/testing/thread_test.cc index 88415169a3c70..2f67b6ee18368 100644 --- a/testing/thread_test.cc +++ b/testing/thread_test.cc @@ -8,18 +8,16 @@ namespace flutter { namespace testing { +namespace { -// |testing::Test| -void ThreadTest::SetUp() { +fml::RefPtr GetDefaultTaskRunner() { fml::MessageLoop::EnsureInitializedForCurrentThread(); - current_task_runner_ = fml::MessageLoop::GetCurrent().GetTaskRunner(); + return fml::MessageLoop::GetCurrent().GetTaskRunner(); } -// |testing::Test| -void ThreadTest::TearDown() { - current_task_runner_ = nullptr; - extra_threads_.clear(); -} +} // namespace + +ThreadTest::ThreadTest() : current_task_runner_(GetDefaultTaskRunner()) {} fml::RefPtr ThreadTest::GetCurrentTaskRunner() { return current_task_runner_; diff --git a/testing/thread_test.h b/testing/thread_test.h index 8c55dbf80ce68..4a7d60fb0312c 100644 --- a/testing/thread_test.h +++ b/testing/thread_test.h @@ -21,14 +21,16 @@ namespace testing { /// @brief A fixture that creates threads with running message loops that /// are terminated when the test is done (the threads are joined /// then as well). While this fixture may be used on it's own, it is -/// often sub-classed but other fixtures whose functioning requires +/// often sub-classed by other fixtures whose functioning requires /// threads to be created as necessary. /// class ThreadTest : public ::testing::Test { public: + ThreadTest(); + //---------------------------------------------------------------------------- /// @brief Get the task runner for the thread that the current unit-test - /// is running on. The creates a message loop is necessary. + /// is running on. This creates a message loop as necessary. /// /// @attention Unlike all other threads and task runners, this task runner is /// shared by all tests running in the process. Tests must ensure @@ -56,16 +58,11 @@ class ThreadTest : public ::testing::Test { /// fml::RefPtr CreateNewThread(std::string name = ""); - protected: - // |testing::Test| - void SetUp() override; - - // |testing::Test| - void TearDown() override; - private: fml::RefPtr current_task_runner_; std::vector> extra_threads_; + + FML_DISALLOW_COPY_AND_ASSIGN(ThreadTest); }; } // namespace testing From 90e28c027c0be00b5544fba4389d1f6d54e058e3 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 3 Dec 2019 16:23:47 -0800 Subject: [PATCH 312/591] Roll src/third_party/dart 89e31069e8..19fc1016da (6 commits) (#14093) dart-lang/sdk@19fc1016da [dart:core] BREAKING CHANGE: Fix analysis errors in NNBD fork dart-lang/sdk@6c42984383 Update dartdoc to 0.29.2. dart-lang/sdk@1ebf5cfb39 Fix false positive UNUSED_FIELD on extensions dart-lang/sdk@b7094364fe [vm] Re-add late field initializer code dart-lang/sdk@441a17d1f9 [cfe] Fix flow analysis for type parameter promotion, initializers, switch continue and for loops dart-lang/sdk@9719f75ef3 Add InvalidLanguageVersionOverride Hint; fixes #37504 --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index c169f964c897a..85d301ec197cb 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '89e31069e8ac0ee9aa67bf9424074404c7a38ee4', + 'dart_revision': '19fc1016da374ac2dac2393a4f61d670b7860963', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -231,7 +231,7 @@ deps = { Var('dart_git') + '/dart2js_info.git' + '@' + Var('dart_dart2js_info_tag'), 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@v0.29.1', + Var('dart_git') + '/dartdoc.git@v0.29.2', 'src/third_party/dart/third_party/pkg/ffi': Var('dart_git') + '/ffi.git' + '@' + Var('dart_ffi_tag'), diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 5fffd1430931a..1681ef8072bd2 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 784c0b84be0b49054d62e0f55130d627 +Signature: 144c047a35c6bd7b19d2fff30cf2ad9f UNUSED LICENSES: From 3e6d6bc612fde37ad06b30263f7700e523f0a0be Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Tue, 3 Dec 2019 18:41:36 -0800 Subject: [PATCH 313/591] add pointer data santizing in flutter web engine (#14082) --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 1 + .../lib/src/engine/pointer_binding.dart | 274 ++++---- .../lib/src/engine/pointer_converter.dart | 640 ++++++++++++++++++ .../test/engine/pointer_binding_test.dart | 265 +++++++- 5 files changed, 1029 insertions(+), 152 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/pointer_converter.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1c549bd6e61c3..299844a2a98a4 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -417,6 +417,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_binding.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_converter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/recording_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/render_vertices.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index da577462e95dc..d9f9dca215784 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -62,6 +62,7 @@ part 'engine/picture.dart'; part 'engine/platform_views.dart'; part 'engine/plugins.dart'; part 'engine/pointer_binding.dart'; +part 'engine/pointer_converter.dart'; part 'engine/recording_canvas.dart'; part 'engine/render_vertices.dart'; part 'engine/rrect_renderer.dart'; diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index e70b9ad1f92b5..46e0c2f751142 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -15,23 +15,17 @@ class PointerBinding { static PointerBinding get instance => _instance; static PointerBinding _instance; - // Set of pointerIds that are added before routing hover and mouse wheel - // events. - // - // The device needs to send a one time PointerChange.add before hover and - // wheel events. - Set _activePointerIds = {}; - PointerBinding(this.domRenderer) { if (_instance == null) { _instance = this; + _pointerDataConverter = PointerDataConverter(); _detector = const PointerSupportDetector(); _adapter = _createAdapter(); } assert(() { registerHotRestartListener(() { _adapter?.clearListeners(); - _activePointerIds.clear(); + _pointerDataConverter?.clearPointerState(); }); return true; }()); @@ -40,7 +34,7 @@ class PointerBinding { final DomRenderer domRenderer; PointerSupportDetector _detector; BaseAdapter _adapter; - + PointerDataConverter _pointerDataConverter; /// Should be used in tests to define custom detection of pointer support. /// /// ```dart @@ -62,22 +56,22 @@ class PointerBinding { newDetector ??= const PointerSupportDetector(); // When changing the detector, we need to swap the adapter. if (newDetector != _detector) { - _activePointerIds.clear(); _detector = newDetector; _adapter?.clearListeners(); _adapter = _createAdapter(); + _pointerDataConverter?.clearPointerState(); } } BaseAdapter _createAdapter() { if (_detector.hasPointerEvents) { - return PointerAdapter(_onPointerData, domRenderer); + return PointerAdapter(_onPointerData, domRenderer, _pointerDataConverter); } if (_detector.hasTouchEvents) { - return TouchAdapter(_onPointerData, domRenderer); + return TouchAdapter(_onPointerData, domRenderer, _pointerDataConverter); } if (_detector.hasMouseEvents) { - return MouseAdapter(_onPointerData, domRenderer); + return MouseAdapter(_onPointerData, domRenderer, _pointerDataConverter); } return null; } @@ -123,11 +117,19 @@ class _PressedButton { /// Common functionality that's shared among adapters. abstract class BaseAdapter { - static final Map _listeners = - {}; + BaseAdapter(this._callback, this.domRenderer, this._pointerDataConverter) { + _setup(); + } + /// Listeners that are registered through dart to js api. + static final Map _listeners = + {}; + /// Listeners that are registered through native javascript api. + static final Map _nativeListeners = + {}; final DomRenderer domRenderer; PointerDataCallback _callback; + PointerDataConverter _pointerDataConverter; // A set of the buttons that are currently being pressed. Set<_PressedButton> _pressedButtons = Set<_PressedButton>(); @@ -144,10 +146,6 @@ abstract class BaseAdapter { } } - BaseAdapter(this._callback, this.domRenderer) { - _setup(); - } - /// Each subclass is expected to override this method to attach its own event /// listeners and convert events into pointer events. void _setup(); @@ -156,9 +154,21 @@ abstract class BaseAdapter { void clearListeners() { final html.Element glassPane = domRenderer.glassPaneElement; _listeners.forEach((String eventName, html.EventListener listener) { - glassPane.removeEventListener(eventName, listener, true); + glassPane.removeEventListener(eventName, listener, true); + }); + // For native listener, we will need to remove it through native javascript + // api. + _nativeListeners.forEach((String eventName, html.EventListener listener) { + js_util.callMethod( + domRenderer.glassPaneElement, + 'removeEventListener', [ + 'wheel', + listener, + ] + ); }); _listeners.clear(); + _nativeListeners.clear(); } void _addEventListener(String eventName, html.EventListener handler) { @@ -177,6 +187,75 @@ abstract class BaseAdapter { domRenderer.glassPaneElement .addEventListener(eventName, loggedHandler, true); } + + /// Converts a floating number timestamp (in milliseconds) to a [Duration] by + /// splitting it into two integer components: milliseconds + microseconds. + Duration _eventTimeStampToDuration(num milliseconds) { + final int ms = milliseconds.toInt(); + final int micro = + ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); + return Duration(milliseconds: ms, microseconds: micro); + } + + List _convertWheelEventToPointerData( + html.WheelEvent event, + ) { + const int domDeltaPixel = 0x00; + const int domDeltaLine = 0x01; + const int domDeltaPage = 0x02; + + // Flutter only supports pixel scroll delta. Convert deltaMode values + // to pixels. + double deltaX = event.deltaX; + double deltaY = event.deltaY; + switch (event.deltaMode) { + case domDeltaLine: + deltaX *= 32.0; + deltaY *= 32.0; + break; + case domDeltaPage: + deltaX *= ui.window.physicalSize.width; + deltaY *= ui.window.physicalSize.height; + break; + case domDeltaPixel: + default: + break; + } + final List data = []; + _pointerDataConverter.convert( + data, + change: ui.PointerChange.hover, + timeStamp: _eventTimeStampToDuration(event.timeStamp), + kind: ui.PointerDeviceKind.mouse, + signalKind: ui.PointerSignalKind.scroll, + device: _mouseDeviceId, + physicalX: event.client.x * ui.window.devicePixelRatio, + physicalY: event.client.y * ui.window.devicePixelRatio, + buttons: event.buttons, + pressure: 1.0, + pressureMin: 0.0, + pressureMax: 1.0, + scrollDeltaX: deltaX, + scrollDeltaY: deltaY, + ); + return data; + } + + void _addWheelEventListener(html.EventListener handler) { + final dynamic eventOptions = js_util.newObject(); + final html.EventListener jsHandler = js.allowInterop((html.Event event) => handler(event)); + _nativeListeners['wheel'] = jsHandler; + js_util.setProperty(eventOptions, 'passive', false); + js_util.callMethod( + domRenderer.glassPaneElement, + 'addEventListener', [ + 'wheel', + jsHandler, + eventOptions + ] + ); + + } } const int _kPrimaryMouseButton = 0x1; @@ -207,16 +286,17 @@ int _deviceFromHtmlEvent(event) { /// Adapter class to be used with browsers that support native pointer events. class PointerAdapter extends BaseAdapter { - PointerAdapter(PointerDataCallback callback, DomRenderer domRenderer) - : super(callback, domRenderer); + PointerAdapter( + PointerDataCallback callback, + DomRenderer domRenderer, + PointerDataConverter _pointerDataConverter + ) : super(callback, domRenderer, _pointerDataConverter); @override void _setup() { _addEventListener('pointerdown', (html.Event event) { final int pointerButton = _pointerButtonFromHtmlEvent(event); final int device = _deviceFromHtmlEvent(event); - // The pointerdown event will cause an 'add' event on the framework side. - PointerBinding._instance._activePointerIds.add(device); if (_isButtonDown(device, pointerButton)) { // TODO(flutter_web): Remove this temporary fix for right click // on web platform once context guesture is implemented. @@ -239,13 +319,6 @@ class PointerAdapter extends BaseAdapter { ? ui.PointerChange.move : ui.PointerChange.hover, pointerEvent); - _ensureMouseDeviceAdded( - data, - pointerEvent.client.x, - pointerEvent.client.y, - pointerEvent.buttons, - pointerEvent.timeStamp, - pointerEvent.pointerId); _callback(data); }); @@ -270,7 +343,8 @@ class PointerAdapter extends BaseAdapter { _callback(_convertEventToPointerData(ui.PointerChange.cancel, event)); }); - _addWheelEventListener((html.WheelEvent event) { + _addWheelEventListener((html.Event event) { + assert(event is html.WheelEvent); if (_debugLogPointerEvents) { print(event.type); } @@ -289,7 +363,8 @@ class PointerAdapter extends BaseAdapter { final List data = []; for (int i = 0; i < allEvents.length; i++) { final html.PointerEvent event = allEvents[i]; - data.add(ui.PointerData( + _pointerDataConverter.convert( + data, change: change, timeStamp: _eventTimeStampToDuration(event.timeStamp), kind: _pointerTypeToDeviceKind(event.pointerType), @@ -301,7 +376,7 @@ class PointerAdapter extends BaseAdapter { pressureMin: 0.0, pressureMax: 1.0, tilt: _computeHighestTilt(event), - )); + ); } return data; } @@ -343,8 +418,11 @@ class PointerAdapter extends BaseAdapter { /// Adapter to be used with browsers that support touch events. class TouchAdapter extends BaseAdapter { - TouchAdapter(PointerDataCallback callback, DomRenderer domRenderer) - : super(callback, domRenderer); + TouchAdapter( + PointerDataCallback callback, + DomRenderer domRenderer, + PointerDataConverter _pointerDataConverter + ) : super(callback, domRenderer, _pointerDataConverter); @override void _setup() { @@ -381,11 +459,12 @@ class TouchAdapter extends BaseAdapter { html.TouchEvent event, ) { final html.TouchList touches = event.changedTouches; - final List data = List(touches.length); + final List data = List(); final int len = touches.length; for (int i = 0; i < len; i++) { final html.Touch touch = touches[i]; - data[i] = ui.PointerData( + _pointerDataConverter.convert( + data, change: change, timeStamp: _eventTimeStampToDuration(event.timeStamp), kind: ui.PointerDeviceKind.touch, @@ -408,8 +487,11 @@ const int _mouseDeviceId = -1; /// Adapter to be used with browsers that support mouse events. class MouseAdapter extends BaseAdapter { - MouseAdapter(PointerDataCallback callback, DomRenderer domRenderer) - : super(callback, domRenderer); + MouseAdapter( + PointerDataCallback callback, + DomRenderer domRenderer, + PointerDataConverter _pointerDataConverter + ) : super(callback, domRenderer, _pointerDataConverter); @override void _setup() { @@ -442,7 +524,8 @@ class MouseAdapter extends BaseAdapter { _callback(_convertEventToPointerData(ui.PointerChange.up, event)); }); - _addWheelEventListener((html.WheelEvent event) { + _addWheelEventListener((html.Event event) { + assert(event is html.WheelEvent); if (_debugLogPointerEvents) { print(event.type); } @@ -455,16 +538,9 @@ class MouseAdapter extends BaseAdapter { ui.PointerChange change, html.MouseEvent event, ) { - final List data = []; - // The mousedown event will cause an 'add' event on the framework side. - if (event.type == 'mousedown') { - PointerBinding._instance._activePointerIds.add(_mouseDeviceId); - } - if (event.type == 'mousemove') { - _ensureMouseDeviceAdded(data, event.client.x, event.client.y, - event.buttons, event.timeStamp, _mouseDeviceId); - } - data.add(ui.PointerData( + List data = []; + _pointerDataConverter.convert( + data, change: change, timeStamp: _eventTimeStampToDuration(event.timeStamp), kind: ui.PointerDeviceKind.mouse, @@ -476,101 +552,7 @@ class MouseAdapter extends BaseAdapter { pressure: 1.0, pressureMin: 0.0, pressureMax: 1.0, - )); + ); return data; } } - -/// Convert a floating number timestamp (in milliseconds) to a [Duration] by -/// splitting it into two integer components: milliseconds + microseconds. -Duration _eventTimeStampToDuration(num milliseconds) { - final int ms = milliseconds.toInt(); - final int micro = - ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); - return Duration(milliseconds: ms, microseconds: micro); -} - -void _ensureMouseDeviceAdded(List data, double clientX, - double clientY, int buttons, double timeStamp, int deviceId) { - if (PointerBinding.instance._activePointerIds.contains(deviceId)) { - return; - } - PointerBinding.instance._activePointerIds.add(deviceId); - // Only send [PointerChange.add] the first time. - data.insert( - 0, - ui.PointerData( - change: ui.PointerChange.add, - timeStamp: _eventTimeStampToDuration(timeStamp), - kind: ui.PointerDeviceKind.mouse, - // In order for Flutter to actually add this pointer, we need to set the - // signal to none. - signalKind: ui.PointerSignalKind.none, - device: deviceId, - physicalX: clientX * ui.window.devicePixelRatio, - physicalY: clientY * ui.window.devicePixelRatio, - buttons: buttons, - pressure: 1.0, - pressureMin: 0.0, - pressureMax: 1.0, - scrollDeltaX: 0, - scrollDeltaY: 0, - )); -} - -List _convertWheelEventToPointerData( - html.WheelEvent event, -) { - const int domDeltaPixel = 0x00; - const int domDeltaLine = 0x01; - const int domDeltaPage = 0x02; - - // Flutter only supports pixel scroll delta. Convert deltaMode values - // to pixels. - double deltaX = event.deltaX; - double deltaY = event.deltaY; - switch (event.deltaMode) { - case domDeltaLine: - deltaX *= 32.0; - deltaY *= 32.0; - break; - case domDeltaPage: - deltaX *= ui.window.physicalSize.width; - deltaY *= ui.window.physicalSize.height; - break; - case domDeltaPixel: - default: - break; - } - - final List data = []; - _ensureMouseDeviceAdded(data, event.client.x, event.client.y, event.buttons, - event.timeStamp, _mouseDeviceId); - data.add(ui.PointerData( - change: ui.PointerChange.hover, - timeStamp: _eventTimeStampToDuration(event.timeStamp), - kind: ui.PointerDeviceKind.mouse, - signalKind: ui.PointerSignalKind.scroll, - device: _mouseDeviceId, - physicalX: event.client.x * ui.window.devicePixelRatio, - physicalY: event.client.y * ui.window.devicePixelRatio, - buttons: event.buttons, - pressure: 1.0, - pressureMin: 0.0, - pressureMax: 1.0, - scrollDeltaX: deltaX, - scrollDeltaY: deltaY, - )); - return data; -} - -void _addWheelEventListener(void listener(html.WheelEvent e)) { - final dynamic eventOptions = js_util.newObject(); - js_util.setProperty(eventOptions, 'passive', false); - js_util.callMethod(PointerBinding.instance.domRenderer.glassPaneElement, - 'addEventListener', [ - 'wheel', - js.allowInterop((html.WheelEvent event) => listener(event)), - eventOptions - ]); -} diff --git a/lib/web_ui/lib/src/engine/pointer_converter.dart b/lib/web_ui/lib/src/engine/pointer_converter.dart new file mode 100644 index 0000000000000..c6b081558913e --- /dev/null +++ b/lib/web_ui/lib/src/engine/pointer_converter.dart @@ -0,0 +1,640 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +class _PointerState { + _PointerState(this.x, this.y); + + /// The identifier used in framework hit test. + int get pointer => _pointer; + int _pointer; + static int _pointerCount = 0; + void startNewPointer() { + _pointerCount += 1; + _pointer = _pointerCount; + } + + bool down = false; + + double x; + double y; +} + +/// Converter to convert web pointer data into a form that framework can +/// understand. +/// +/// This converter calculates pointer location delta and pointer identifier for +/// each pointer. Both are required by framework to correctly trigger gesture +/// activity. It also attempts to sanitize pointer data input sequence by always +/// synthesizing an add pointer data prior to hover or down if it the pointer is +/// not previously added. +/// +/// For example: +/// before: +/// hover -> down -> move -> up +/// after: +/// add(synthesize) -> hover -> down -> move -> up +/// +/// before: +/// down -> move -> up +/// after: +/// add(synthesize) -> down -> move -> up +class PointerDataConverter { + PointerDataConverter(); + + // Map from browser pointer identifiers to PointerEvent pointer identifiers. + final Map _pointers = {}; + + /// Clears the existing pointer states. + /// + /// This method is invoked during hot reload to make sure we have a clean + /// converter after hot reload. + void clearPointerState() { + _pointers.clear(); + _PointerState._pointerCount = 0; + } + + _PointerState _ensureStateForPointer(int device, double x, double y) { + return _pointers.putIfAbsent( + device, + () => _PointerState(x, y), + ); + } + + ui.PointerData _generateCompletePointerData({ + Duration timeStamp, + ui.PointerChange change, + ui.PointerDeviceKind kind, + ui.PointerSignalKind signalKind, + int device, + double physicalX, + double physicalY, + int buttons, + bool obscured, + double pressure, + double pressureMin, + double pressureMax, + double distance, + double distanceMax, + double size, + double radiusMajor, + double radiusMinor, + double radiusMin, + double radiusMax, + double orientation, + double tilt, + int platformData, + double scrollDeltaX, + double scrollDeltaY, + }) { + assert(_pointers.containsKey(device)); + final _PointerState state = _pointers[device]; + final double deltaX = physicalX - state.x; + final double deltaY = physicalY - state.y; + state.x = physicalX; + state.y = physicalY; + return ui.PointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + pointerIdentifier: state.pointer ?? 0, + physicalX: physicalX, + physicalY: physicalY, + physicalDeltaX: deltaX, + physicalDeltaY: deltaY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ); + } + + bool _locationHasChanged(int device, double physicalX, double physicalY) { + assert(_pointers.containsKey(device)); + final _PointerState state = _pointers[device]; + return state.x != physicalX || state.y != physicalY; + } + + ui.PointerData _synthesizePointerData({ + Duration timeStamp, + ui.PointerChange change, + ui.PointerDeviceKind kind, + int device, + double physicalX, + double physicalY, + int buttons, + bool obscured, + double pressure, + double pressureMin, + double pressureMax, + double distance, + double distanceMax, + double size, + double radiusMajor, + double radiusMinor, + double radiusMin, + double radiusMax, + double orientation, + double tilt, + int platformData, + double scrollDeltaX, + double scrollDeltaY, + }) { + assert(_pointers.containsKey(device)); + final _PointerState state = _pointers[device]; + final double deltaX = physicalX - state.x; + final double deltaY = physicalY - state.y; + state.x = physicalX; + state.y = physicalY; + return ui.PointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + // All the pointer data except scroll should not have a signal kind, and + // there is no use case for synthetic scroll event. We should be + // safe to default it to ui.PointerSignalKind.none. + signalKind: ui.PointerSignalKind.none, + device: device, + pointerIdentifier: state.pointer ?? 0, + physicalX: physicalX, + physicalY: physicalY, + physicalDeltaX: deltaX, + physicalDeltaY: deltaY, + buttons: buttons, + obscured: obscured, + synthesized: true, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ); + } + + /// Converts the given html pointer event metrics into a sequence of framework-compatible + /// pointer data and stores it into [result] + void convert( + List result, { + Duration timeStamp = Duration.zero, + ui.PointerChange change = ui.PointerChange.cancel, + ui.PointerDeviceKind kind = ui.PointerDeviceKind.touch, + ui.PointerSignalKind signalKind, + int device = 0, + double physicalX = 0.0, + double physicalY = 0.0, + int buttons = 0, + bool obscured = false, + double pressure = 0.0, + double pressureMin = 0.0, + double pressureMax = 0.0, + double distance = 0.0, + double distanceMax = 0.0, + double size = 0.0, + double radiusMajor = 0.0, + double radiusMinor = 0.0, + double radiusMin = 0.0, + double radiusMax = 0.0, + double orientation = 0.0, + double tilt = 0.0, + int platformData = 0, + double scrollDeltaX = 0.0, + double scrollDeltaY = 0.0, + }) { + assert(change != null); + if (signalKind == null || + signalKind == ui.PointerSignalKind.none) { + switch (change) { + case ui.PointerChange.add: + assert(!_pointers.containsKey(device)); + _ensureStateForPointer(device, physicalX, physicalY); + assert(!_locationHasChanged(device, physicalX, physicalY)); + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + case ui.PointerChange.hover: + final bool alreadyAdded = _pointers.containsKey(device); + final _PointerState state = _ensureStateForPointer( + device, physicalX, physicalY); + assert(!state.down); + if (!alreadyAdded) { + // Synthesizes an add pointer data. + result.add( + _synthesizePointerData( + timeStamp: timeStamp, + change: ui.PointerChange.add, + kind: kind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + } + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + case ui.PointerChange.down: + final bool alreadyAdded = _pointers.containsKey(device); + final _PointerState state = _ensureStateForPointer( + device, physicalX, physicalY); + assert(!state.down); + if (!alreadyAdded) { + // Synthesizes an add pointer data. + result.add( + _synthesizePointerData( + timeStamp: timeStamp, + change: ui.PointerChange.add, + kind: kind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + } + assert(!_locationHasChanged(device, physicalX, physicalY)); + state.startNewPointer(); + state.down = true; + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + case ui.PointerChange.move: + assert(_pointers.containsKey(device)); + final _PointerState state = _pointers[device]; + assert(state.down); + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + case ui.PointerChange.up: + case ui.PointerChange.cancel: + assert(_pointers.containsKey(device)); + final _PointerState state = _pointers[device]; + assert(state.down); + assert(!_locationHasChanged(device, physicalX, physicalY)); + state.down = false; + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + case ui.PointerChange.remove: + assert(_pointers.containsKey(device)); + final _PointerState state = _pointers[device]; + assert(!state.down); + assert(!_locationHasChanged(device, physicalX, physicalY)); + _pointers.remove(device); + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + } + } else { + switch (signalKind) { + case ui.PointerSignalKind.scroll: + final bool alreadyAdded = _pointers.containsKey(device); + final _PointerState state = _ensureStateForPointer( + device, physicalX, physicalY); + if (!alreadyAdded) { + // Synthesizes an add pointer data. + result.add( + _synthesizePointerData( + timeStamp: timeStamp, + change: ui.PointerChange.add, + kind: kind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + } + if (_locationHasChanged(device, physicalX, physicalY)) { + // Synthesize a hover/move of the pointer to the scroll location + // before sending the scroll event, if necessary, so that clients + // don't have to worry about native ordering of hover and scroll + // events. + if (state.down) { + result.add( + _synthesizePointerData( + timeStamp: timeStamp, + change: ui.PointerChange.move, + kind: kind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + } else { + result.add( + _synthesizePointerData( + timeStamp: timeStamp, + change: ui.PointerChange.hover, + kind: kind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + } + } + result.add( + _generateCompletePointerData( + timeStamp: timeStamp, + change: change, + kind: kind, + signalKind: signalKind, + device: device, + physicalX: physicalX, + physicalY: physicalY, + buttons: buttons, + obscured: obscured, + pressure: pressure, + pressureMin: pressureMin, + pressureMax: pressureMax, + distance: distance, + distanceMax: distanceMax, + size: size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: orientation, + tilt: tilt, + platformData: platformData, + scrollDeltaX: scrollDeltaX, + scrollDeltaY: scrollDeltaY, + ) + ); + break; + case ui.PointerSignalKind.none: + assert(false); // This branch should already have 'none' filtered out. + break; + case ui.PointerSignalKind.unknown: + // Ignore unknown signals. + break; + } + } + } +} diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index 3638376cc6b8b..cee6b8f16de14 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -56,7 +56,11 @@ void main() { })); expect(packets, hasLength(3)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); expect(packets[1].data[0].change, equals(ui.PointerChange.up)); expect(packets[2].data[0].change, equals(ui.PointerChange.down)); }); @@ -78,10 +82,20 @@ void main() { })); expect(packets, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, equals(true)); expect(packets[0].data[0].device, equals(1)); - expect(packets[1].data[0].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].device, equals(1)); + // An add will be synthesized. + expect(packets[1].data, hasLength(2)); + expect(packets[1].data[0].change, equals(ui.PointerChange.add)); + expect(packets[1].data[0].synthesized, equals(true)); expect(packets[1].data[0].device, equals(2)); + expect(packets[1].data[1].change, equals(ui.PointerChange.down)); + expect(packets[1].data[1].device, equals(2)); }); test('creates an add event if the first pointer activity is a hover', () { @@ -99,10 +113,11 @@ void main() { expect(packets.single.data, hasLength(2)); expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[0].synthesized, equals(true)); expect(packets.single.data[1].change, equals(ui.PointerChange.hover)); }); - test('does not create an add event if got a pointerdown', () { + test('does create an add event if got a pointerdown', () { List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); @@ -114,9 +129,247 @@ void main() { })); expect(packets, hasLength(1)); - expect(packets.single.data, hasLength(1)); + expect(packets.single.data, hasLength(2)); + + expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[1].change, equals(ui.PointerChange.down)); + }); + + test('does calculate delta and pointer identifier correctly', () { + List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + glassPane.dispatchEvent(html.PointerEvent('pointermove', { + 'pointerId': 1, + 'button': 1, + 'clientX': 10.0, + 'clientY': 10.0, + })); + + glassPane.dispatchEvent(html.PointerEvent('pointermove', { + 'pointerId': 1, + 'button': 1, + 'clientX': 20.0, + 'clientY': 20.0, + })); + + glassPane.dispatchEvent(html.PointerEvent('pointerdown', { + 'pointerId': 1, + 'button': 1, + 'clientX': 20.0, + 'clientY': 20.0, + })); + + glassPane.dispatchEvent(html.PointerEvent('pointermove', { + 'pointerId': 1, + 'button': 1, + 'clientX': 40.0, + 'clientY': 30.0, + })); + + glassPane.dispatchEvent(html.PointerEvent('pointerup', { + 'pointerId': 1, + 'button': 1, + 'clientX': 40.0, + 'clientY': 30.0, + })); + + glassPane.dispatchEvent(html.PointerEvent('pointermove', { + 'pointerId': 1, + 'button': 1, + 'clientX': 20.0, + 'clientY': 10.0, + })); + + glassPane.dispatchEvent(html.PointerEvent('pointerdown', { + 'pointerId': 1, + 'button': 1, + 'clientX': 20.0, + 'clientY': 10.0, + })); + + expect(packets, hasLength(7)); + + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].physicalX, equals(10.0)); + expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].pointerIdentifier, equals(0)); + expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].physicalX, equals(10.0)); + expect(packets[0].data[1].physicalY, equals(10.0)); + expect(packets[0].data[1].physicalDeltaX, equals(0.0)); + expect(packets[0].data[1].physicalDeltaY, equals(0.0)); + + expect(packets[1].data, hasLength(1)); + expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[0].pointerIdentifier, equals(0)); + expect(packets[1].data[0].synthesized, equals(false)); + expect(packets[1].data[0].physicalX, equals(20.0)); + expect(packets[1].data[0].physicalY, equals(20.0)); + expect(packets[1].data[0].physicalDeltaX, equals(10.0)); + expect(packets[1].data[0].physicalDeltaY, equals(10.0)); + + expect(packets[2].data, hasLength(1)); + expect(packets[2].data[0].change, equals(ui.PointerChange.down)); + expect(packets[2].data[0].pointerIdentifier, equals(1)); + expect(packets[2].data[0].synthesized, equals(false)); + expect(packets[2].data[0].physicalX, equals(20.0)); + expect(packets[2].data[0].physicalY, equals(20.0)); + expect(packets[2].data[0].physicalDeltaX, equals(0.0)); + expect(packets[2].data[0].physicalDeltaY, equals(0.0)); + + expect(packets[3].data, hasLength(1)); + expect(packets[3].data[0].change, equals(ui.PointerChange.move)); + expect(packets[3].data[0].pointerIdentifier, equals(1)); + expect(packets[3].data[0].synthesized, equals(false)); + expect(packets[3].data[0].physicalX, equals(40.0)); + expect(packets[3].data[0].physicalY, equals(30.0)); + expect(packets[3].data[0].physicalDeltaX, equals(20.0)); + expect(packets[3].data[0].physicalDeltaY, equals(10.0)); + + expect(packets[4].data, hasLength(1)); + expect(packets[4].data[0].change, equals(ui.PointerChange.up)); + expect(packets[4].data[0].pointerIdentifier, equals(1)); + expect(packets[4].data[0].synthesized, equals(false)); + expect(packets[4].data[0].physicalX, equals(40.0)); + expect(packets[4].data[0].physicalY, equals(30.0)); + expect(packets[4].data[0].physicalDeltaX, equals(0.0)); + expect(packets[4].data[0].physicalDeltaY, equals(0.0)); + + expect(packets[5].data, hasLength(1)); + expect(packets[5].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[5].data[0].pointerIdentifier, equals(1)); + expect(packets[5].data[0].synthesized, equals(false)); + expect(packets[5].data[0].physicalX, equals(20.0)); + expect(packets[5].data[0].physicalY, equals(10.0)); + expect(packets[5].data[0].physicalDeltaX, equals(-20.0)); + expect(packets[5].data[0].physicalDeltaY, equals(-20.0)); + + expect(packets[6].data, hasLength(1)); + expect(packets[6].data[0].change, equals(ui.PointerChange.down)); + expect(packets[6].data[0].pointerIdentifier, equals(2)); + expect(packets[6].data[0].synthesized, equals(false)); + expect(packets[6].data[0].physicalX, equals(20.0)); + expect(packets[6].data[0].physicalY, equals(10.0)); + expect(packets[6].data[0].physicalDeltaX, equals(0.0)); + expect(packets[6].data[0].physicalDeltaY, equals(0.0)); + }); + + test('does synthesize add or hover or more for scroll', () { + List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + glassPane.dispatchEvent(html.WheelEvent('wheel', + button: 1, + clientX: 10, + clientY: 10, + deltaX: 10, + deltaY: 10, + )); + + glassPane.dispatchEvent(html.WheelEvent('wheel', + button: 1, + clientX: 20, + clientY: 50, + deltaX: 10, + deltaY: 10, + )); + + glassPane.dispatchEvent(html.PointerEvent('pointerdown', { + 'pointerId': -1, + 'button': 1, + 'clientX': 20.0, + 'clientY': 50.0, + })); + + glassPane.dispatchEvent(html.WheelEvent('wheel', + button: 1, + clientX: 30, + clientY: 60, + deltaX: 10, + deltaY: 10, + )); + + expect(packets, hasLength(4)); + + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].physicalX, equals(10.0)); + expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[0].data[1].pointerIdentifier, equals(0)); + expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].physicalX, equals(10.0)); + expect(packets[0].data[1].physicalY, equals(10.0)); + expect(packets[0].data[1].physicalDeltaX, equals(0.0)); + expect(packets[0].data[1].physicalDeltaY, equals(0.0)); + + // A hover will be synthesized. + expect(packets[1].data, hasLength(2)); + expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[0].pointerIdentifier, equals(0)); + expect(packets[1].data[0].synthesized, equals(true)); + expect(packets[1].data[0].physicalX, equals(20.0)); + expect(packets[1].data[0].physicalY, equals(50.0)); + expect(packets[1].data[0].physicalDeltaX, equals(10.0)); + expect(packets[1].data[0].physicalDeltaY, equals(40.0)); + + expect(packets[1].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[1].data[1].pointerIdentifier, equals(0)); + expect(packets[1].data[1].synthesized, equals(false)); + expect(packets[1].data[1].physicalX, equals(20.0)); + expect(packets[1].data[1].physicalY, equals(50.0)); + expect(packets[1].data[1].physicalDeltaX, equals(0.0)); + expect(packets[1].data[1].physicalDeltaY, equals(0.0)); + + // No synthetic pointer data for down event. + expect(packets[2].data, hasLength(1)); + expect(packets[2].data[0].change, equals(ui.PointerChange.down)); + expect(packets[2].data[0].signalKind, equals(null)); + expect(packets[2].data[0].pointerIdentifier, equals(1)); + expect(packets[2].data[0].synthesized, equals(false)); + expect(packets[2].data[0].physicalX, equals(20.0)); + expect(packets[2].data[0].physicalY, equals(50.0)); + expect(packets[2].data[0].physicalDeltaX, equals(0.0)); + expect(packets[2].data[0].physicalDeltaY, equals(0.0)); + + // A move will be synthesized instead of hover because the button is currently down. + expect(packets[3].data, hasLength(2)); + expect(packets[3].data[0].change, equals(ui.PointerChange.move)); + expect(packets[3].data[0].pointerIdentifier, equals(1)); + expect(packets[3].data[0].synthesized, equals(true)); + expect(packets[3].data[0].physicalX, equals(30.0)); + expect(packets[3].data[0].physicalY, equals(60.0)); + expect(packets[3].data[0].physicalDeltaX, equals(10.0)); + expect(packets[3].data[0].physicalDeltaY, equals(10.0)); - expect(packets.single.data[0].change, equals(ui.PointerChange.down)); + expect(packets[3].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[3].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[3].data[1].pointerIdentifier, equals(1)); + expect(packets[3].data[1].synthesized, equals(false)); + expect(packets[3].data[1].physicalX, equals(30.0)); + expect(packets[3].data[1].physicalY, equals(60.0)); + expect(packets[3].data[1].physicalDeltaX, equals(0.0)); + expect(packets[3].data[1].physicalDeltaY, equals(0.0)); }); }); } From 1cdfc99314df68a258b63ac4fb1b3671ce42bb71 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 3 Dec 2019 22:20:02 -0800 Subject: [PATCH 314/591] Roll src/third_party/dart 19fc1016da..2a13b1fe26 (6 commits) (#14107) dart-lang/sdk@2a13b1fe26 Reland "[dartdevc] Break dart:_debugger dependency on dart:html" dart-lang/sdk@48573044b3 Replace helper methods in AbstractTypeSystemTest with ElementsTypesMixin. dart-lang/sdk@7a911ce3f1 [ package:vm_service ] Fix issue where an exception could be thrown if remote service protocl version was older than that supported by package:vm_service dart-lang/sdk@f4e44dd705 [vm] Make kernel buffers live exactly as long as their derived views. dart-lang/sdk@5ecfd7058a Drop fallback to looser as check dart-lang/sdk@9c220059a1 Implement DOWN for FunctionType(s) with NNBD rules. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 85d301ec197cb..798ddaf1aebdc 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '19fc1016da374ac2dac2393a4f61d670b7860963', + 'dart_revision': '2a13b1fe2699eab55c1e25742dc8e1c1501bf486', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1681ef8072bd2..1426e0c960942 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 144c047a35c6bd7b19d2fff30cf2ad9f +Signature: 88d6da4b86d1b10cb93b13653483c196 UNUSED LICENSES: From 027c9614cd42906e04fb6b2895349efc6d82c712 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 4 Dec 2019 07:11:50 -0800 Subject: [PATCH 315/591] Roll src/third_party/dart 2a13b1fe26..413867d678 (3 commits) (#14125) dart-lang/sdk@413867d678 [infra] Add a script to convert multi-tests to the new static error test framework dart-lang/sdk@2ea8ff5ae7 [cfe] Don't continue shorting in explicit extension null aware access dart-lang/sdk@7c3b2b75c8 [infra] Fix deflaking in test.dart --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 798ddaf1aebdc..987238e9c413f 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '2a13b1fe2699eab55c1e25742dc8e1c1501bf486', + 'dart_revision': '413867d678331e83128eac67d7996cf5ccb18210', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 07aab98212cb2bc3c1cfdd80c1ce9130299f5d66 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Wed, 4 Dec 2019 10:59:06 -0500 Subject: [PATCH 316/591] Fix one more use of deprecated path fill type API (#14127) --- testing/assertions_skia.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/assertions_skia.cc b/testing/assertions_skia.cc index e8b7ce992b8a8..c070f1c6cc93d 100644 --- a/testing/assertions_skia.cc +++ b/testing/assertions_skia.cc @@ -93,7 +93,8 @@ std::ostream& operator<<(std::ostream& os, const SkRRect& r) { } std::ostream& operator<<(std::ostream& os, const SkPath& r) { - return os << "Valid: " << r.isValid() << ", FillType: " << r.getFillType() + return os << "Valid: " << r.isValid() + << ", FillType: " << static_cast(r.getFillType()) << ", Bounds: " << r.getBounds(); } From fbc049be8908368cbaf0205000bfdca53f07660f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 4 Dec 2019 11:12:29 -0500 Subject: [PATCH 317/591] Roll src/third_party/skia 75368c3a0290..ccca30aad770 (12 commits) (#14129) https://skia.googlesource.com/skia.git/+log/75368c3a0290..ccca30aad770 git log 75368c3a0290..ccca30aad770 --date=short --first-parent --format='%ad %ae %s' 2019-12-04 rosasco@google.com Notes on how to build SKQP for Fuchsia. 2019-12-04 reed@google.com Revert "remove legacy SkPath enum guards" 2019-12-04 thomasanderson@chromium.org Always check for GL_ARB_sync 2019-12-04 borenet@google.com [infra] Auto-submit the CL to update supported branch configs 2019-12-04 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src bd8110e59b6b..d02b0cb4d389 (540 commits) 2019-12-04 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader b64fbfec4dcd..bbd0694f9ab2 (3 commits) 2019-12-04 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 fb40d231c3e2..249cb200173f (15 commits) 2019-12-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-03 herb@google.com Make GrTextBlob initial position const 2019-12-03 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-03 reed@google.com remove legacy SkPath enum guards 2019-12-03 herb@google.com Start cleanup of GrTextBlob Created with: gclient setdep -r src/third_party/skia@ccca30aad770 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 987238e9c413f..a47a900cc4cfc 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '75368c3a02905730312afe613d143ae72748a977', + 'skia_revision': 'ccca30aad77057fbceb7c752298d6b014e6124eb', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index dc87ce99e42a2..706272f6888ff 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 74ec6f1e8303bd60dbc8f945c4df4c31 +Signature: 9c6af5b78e12bbd4d35385b64ff60302 UNUSED LICENSES: From fdaa7cf1217510ae7da018fb307efb913b08e7d0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 4 Dec 2019 11:15:09 -0500 Subject: [PATCH 318/591] Roll fuchsia/sdk/core/mac-amd64 from OSk8h... to XCAOU... (#14128) Roll fuchsia/sdk/core/mac-amd64 from OSk8h... to XCAOU... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a47a900cc4cfc..dc25595f4eb47 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'OSk8h97wpfy2e7hem2Bg7lEkZqW-XCr80qpQGWrNRKUC' + 'version': 'XCAOUqFKaG7LugHzLoN1fxiJVhguZDnPrdYo7HCwsoMC' } ], 'condition': 'host_os == "mac"', From 65b126b71ae96e893dcafad5e1dd1b87365dd824 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 4 Dec 2019 10:19:27 -0800 Subject: [PATCH 319/591] Roll src/third_party/dart 413867d678..a9c77229c2 (1 commits) (#14130) dart-lang/sdk@a9c77229c2 [vm/compiler] Avoid undefined behavior in range analysis shift operation --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index dc25595f4eb47..2cce7c1e52fe5 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '413867d678331e83128eac67d7996cf5ccb18210', + 'dart_revision': 'a9c77229c20db10521dcf565b2d64aae3d740d9a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1426e0c960942..1ccc601e2ff0f 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 88d6da4b86d1b10cb93b13653483c196 +Signature: 6daeaf1ba161a771918102a7ef9c0889 UNUSED LICENSES: From e6887328a110bc29b09d04e5bbf6eadabc115ba0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 4 Dec 2019 13:15:47 -0800 Subject: [PATCH 320/591] Fix platform view offsets incorrectly taking into account device pixel ratios. (#14135) This issue was hidden by an incorrect test expectation that has been corrected. Fixes b/144555069 Fixes https://github.com/flutter/flutter/issues/45991 --- shell/platform/embedder/embedder_layers.cc | 15 +++++++-------- .../platform/embedder/tests/embedder_unittests.cc | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc index 90ba77b6428e0..dc284e22746e9 100644 --- a/shell/platform/embedder/embedder_layers.cc +++ b/shell/platform/embedder/embedder_layers.cc @@ -184,16 +184,15 @@ void EmbedderLayers::PushPlatformViewLayer( layer.type = kFlutterLayerContentTypePlatformView; layer.platform_view = platform_views_referenced_.back().get(); - const auto layer_bounds = SkRect::MakeXYWH(params.offsetPixels.x(), // - params.offsetPixels.y(), // - params.sizePoints.width(), // - params.sizePoints.height() // - ); + const auto layer_bounds = + SkRect::MakeXYWH(params.offsetPixels.x(), // + params.offsetPixels.y(), // + params.sizePoints.width() * device_pixel_ratio_, // + params.sizePoints.height() * device_pixel_ratio_ // + ); const auto transformed_layer_bounds = - SkMatrix::Concat(root_surface_transformation_, - SkMatrix::MakeScale(device_pixel_ratio_)) - .mapRect(layer_bounds); + root_surface_transformation_.mapRect(layer_bounds); layer.offset.x = transformed_layer_bounds.x(); layer.offset.y = transformed_layer_bounds.y(); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 47f86c281e454..c8560a6d11e7a 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -2803,7 +2803,7 @@ TEST_F(EmbedderTest, layer.type = kFlutterLayerContentTypePlatformView; layer.platform_view = &platform_view; layer.size = FlutterSizeMake(800.0, 560.0); - layer.offset = FlutterPointMake(0.0, 80.0); + layer.offset = FlutterPointMake(0.0, 40.0); ASSERT_EQ(*layers[1], layer); } @@ -2902,7 +2902,7 @@ TEST_F( layer.type = kFlutterLayerContentTypePlatformView; layer.platform_view = &platform_view; layer.size = FlutterSizeMake(560.0, 800.0); - layer.offset = FlutterPointMake(80.0, 0.0); + layer.offset = FlutterPointMake(40.0, 0.0); ASSERT_EQ(*layers[1], layer); } From 8d1c59811c1f96af679cb60094fd8268d49e1f35 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 4 Dec 2019 13:27:14 -0800 Subject: [PATCH 321/591] [web] Fix text measurement when constraint width is infinite (#14132) --- lib/web_ui/lib/src/engine/text/ruler.dart | 38 ++++++++++++++-------- lib/web_ui/test/text/measurement_test.dart | 22 +++++++++++++ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index d33ee463614cd..7ea7cc4973dc7 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -210,9 +210,25 @@ class TextDimensions { } /// Updated element style width. - void updateWidth(String cssWidth) { + void updateConstraintWidth(double width, String ellipsis) { _invalidateBoundsCache(); - _element.style.width = cssWidth; + + if (width.isInfinite) { + _element.style + ..width = null + ..whiteSpace = 'pre'; + } else if (ellipsis != null) { + // Width is finite, but we don't want to let the text soft-wrap when + // ellipsis overflow is enabled. + _element.style + ..width = '${width}px' + ..whiteSpace = 'pre'; + } else { + // Width is finite and there's no ellipsis overflow. + _element.style + ..width = '${width}px' + ..whiteSpace = 'pre-wrap'; + } } void _invalidateBoundsCache() { @@ -475,17 +491,8 @@ class ParagraphRuler { ..display = 'block' ..overflowWrap = 'break-word'; - // TODO(flutter_web): Implement the ellipsis overflow for multi-line text - // too. As a pre-requisite, we need to be able to programmatically find - // line breaks. - if (style.ellipsis == null) { - elementStyle.whiteSpace = 'pre-wrap'; - } else { - // The height measurement is affected by whether the text has the ellipsis - // overflow property or not. This is because when ellipsis is set, we may - // not render all the lines, but stop at the first line that overflows. + if (style.ellipsis != null) { elementStyle - ..whiteSpace = 'pre' ..overflow = 'hidden' ..textOverflow = 'ellipsis'; } @@ -595,7 +602,10 @@ class ParagraphRuler { // The extra 0.5 is because sometimes the browser needs slightly more space // than the size it reports back. When that happens the text may be wrap // when we thought it didn't. - constrainedDimensions.updateWidth('${constraints.width + 0.5}px'); + constrainedDimensions.updateConstraintWidth( + constraints.width + 0.5, + style.ellipsis, + ); } /// Returns text position in a paragraph that contains multiple @@ -701,7 +711,7 @@ class ParagraphRuler { ..appendText(before) ..append(rangeSpan) ..appendText(after); - constrainedDimensions.updateWidth('${constraints.width}px'); + constrainedDimensions.updateConstraintWidth(constraints.width, null); // Measure the rects of [rangeSpan]. final List> clientRects = rangeSpan.getClientRects(); diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index 1119ffef2bc84..3478124486376 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -406,6 +406,28 @@ void main() async { } }); + testMeasurements( + 'wraps multi-line text correctly when constraint width is infinite', + (TextMeasurementService instance) { + final EngineParagraph paragraph = build(ahemStyle, '123\n456 789'); + final MeasurementResult result = + instance.measure(paragraph, infiniteConstraints); + + expect(result.isSingleLine, false); + expect(result.maxIntrinsicWidth, 70); + expect(result.minIntrinsicWidth, 30); + expect(result.width, double.infinity); + expect(result.height, 20); + + if (instance is CanvasTextMeasurementService) { + expect(result.lines, [ + line('123', hardBreak: true, width: 30.0, lineNumber: 0), + line('456 789', hardBreak: true, width: 70.0, lineNumber: 1), + ]); + } + }, + ); + testMeasurements( 'takes letter spacing into account', (TextMeasurementService instance) { From 5d9509ae056b04c30295df27f201f31af9777842 Mon Sep 17 00:00:00 2001 From: Yegor Date: Wed, 4 Dec 2019 13:28:23 -0800 Subject: [PATCH 322/591] [web][felt] fix source map path (#14134) --- lib/web_ui/dev/test_platform.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 3296941a283d9..e9f5281710c99 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -770,7 +770,7 @@ class BrowserManager { final String mapPath = p.join( env.environment.webUiRootDir.path, 'build', - '$path.js.map', + '$path.browser_test.dart.js.map', ); final JSStackTraceMapper mapper = JSStackTraceMapper( await File(mapPath).readAsString(), From 47ef4cdddfbb9da1e052f45b2a7b641d2cf1f4c1 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Wed, 4 Dec 2019 15:11:54 -0800 Subject: [PATCH 323/591] Expanded our scenario_app docs. (#14136) --- testing/scenario_app/README.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/testing/scenario_app/README.md b/testing/scenario_app/README.md index 082794fb8e0da..bbb34fd8608d8 100644 --- a/testing/scenario_app/README.md +++ b/testing/scenario_app/README.md @@ -8,11 +8,39 @@ that it should be buildable as a presubmit or postsubmit to the engine even in the face of changes to Dart or dart:ui that require upstream changes in the Flutter tooling. -To add a new scenario, create a new subclass of `Scenario` and add it to the -map in `main.dart`. For an example, see animated_color_square.dart, which draws +## Running for iOS + +```sh +cd ${ENGINE_REPO}/.. +gclient sync +./flutter/tools/gn --unoptimized --runtime-mode debug --simulator --ios +ninja -C out/ios_debug_sim_unopt +cd ${ENGINE_REPO}/testing/scenario_app +./run_ios_tests.sh +``` + +## Adding a New Scenario + +Create a new subclass of [Scenario](https://github.com/flutter/engine/blob/5d9509ae056b04c30295df27f201f31af9777842/testing/scenario_app/lib/src/scenario.dart#L9) and add it to the +map in [main.dart](https://github.com/flutter/engine/blob/5d9509ae056b04c30295df27f201f31af9777842/testing/scenario_app/lib/main.dart#L17). For an example, see [animated_color_square.dart](https://github.com/flutter/engine/blob/5d9509ae056b04c30295df27f201f31af9777842/testing/scenario_app/lib/src/animated_color_square.dart#L15), which draws a continuously animating colored square that bounces off the sides of the viewport. +### iOS Platform View Tests + +For PlatformView tests on iOS, you'll also have to edit the dictionaries in +[AppDelegate.m](https://github.com/flutter/engine/blob/5d9509ae056b04c30295df27f201f31af9777842/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m#L29) and [PlatformViewGoldenTestManager.m](https://github.com/flutter/engine/blob/5d9509ae056b04c30295df27f201f31af9777842/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGoldenTestManager.m#L24) so that the correct golden image can be found. Also, you'll have to add a [GoldenPlatformViewTests](https://github.com/flutter/engine/blob/5d9509ae056b04c30295df27f201f31af9777842/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.h#L18) in [PlatformViewUITests.m](https://github.com/flutter/engine/blob/af2ffc02b72af2a89242ca3c89e18269b1584ce5/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m). + +### Generating Golden Images on iOS + +Screenshots are saved as +[XCTAttachment](https://developer.apple.com/documentation/xctest/activities_and_attachments/adding_attachments_to_tests_and_activities?language=objc)'s. +If you look at the output from running the tests you'll find a path in the form: +`/Users/$USER/Library/Developer/Xcode/DerivedData/Scenarios-$HASH`. +Inside that directory you'll find +`./Build/Products/Debug-iphonesimulator/ScenariosUITests-Runner.app/PlugIns/ScenariosUITests.xctest/` which is where all the images that were +compared against golden reside. + ## Building for iOS In this folder, after building the `ios_host` and `ios_profile` engine targets, From 79599036deb89034c7cefdebc4dea82b9677a9da Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 4 Dec 2019 18:56:59 -0500 Subject: [PATCH 324/591] Roll src/third_party/skia ccca30aad770..6344c2937997 (13 commits) (#14133) https://skia.googlesource.com/skia.git/+log/ccca30aad770..6344c2937997 git log ccca30aad770..6344c2937997 --date=short --first-parent --format='%ad %ae %s' 2019-12-04 reed@google.com some cleanups for halfplanes 2019-12-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-04 egdaniel@google.com Remove unneeded abandon/release check in GrGpuResource message processing. 2019-12-04 bsalomon@google.com Remove SkSize& SkSize::operator=(const SkISize&) 2019-12-04 halcanary@google.com SkQP: model-creation improvements 2019-12-04 mtklein@google.com work around GCC false positive warning? 2019-12-04 egdaniel@google.com Handle failures in submission of vulkan command buffers. 2019-12-04 mtklein@google.com replace SkIRect[1] with SkIRect 2019-12-04 bsalomon@google.com Fix for 420/422 chroma subsampling of odd dimension images when converting from YUV to RGB on GPU. 2019-12-04 reed@google.com Reland "remove legacy SkPath enum guards" 2019-12-04 robertphillips@google.com Add onPrePrepareDraws to GrFillRectOp 2019-12-04 mtklein@google.com simpler tmp arrays in GrGLPath 2019-12-04 senorblanco@chromium.org Update dawn.h -> webgpu.h, dawn_cpp.h -> webgpu_cpp.h. Created with: gclient setdep -r src/third_party/skia@6344c2937997 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2cce7c1e52fe5..a10c5cd33e11a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'ccca30aad77057fbceb7c752298d6b014e6124eb', + 'skia_revision': '6344c2937997f12221a5c233803a3f2d19f2111f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 706272f6888ff..9ffd257809279 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9c6af5b78e12bbd4d35385b64ff60302 +Signature: c40417f6dd64a7cc0b09b7dfc92ebfe1 UNUSED LICENSES: @@ -3083,6 +3083,7 @@ FILE: ../../../third_party/skia/gm/skbug_8664.cpp FILE: ../../../third_party/skia/gm/skbug_8955.cpp FILE: ../../../third_party/skia/gm/skvm.cpp FILE: ../../../third_party/skia/gm/video_decoder.cpp +FILE: ../../../third_party/skia/gm/yuv420_odd_dim.cpp FILE: ../../../third_party/skia/include/core/SkTileMode.h FILE: ../../../third_party/skia/include/gpu/GrContextThreadSafeProxy.h FILE: ../../../third_party/skia/include/gpu/dawn/GrDawnTypes.h From 9f8e44722a88f4574869ae71e548f1fd30cb1687 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Wed, 4 Dec 2019 16:17:43 -0800 Subject: [PATCH 325/591] Started specifying the OS version for running the tests. (#14094) --- testing/scenario_app/run_ios_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/scenario_app/run_ios_tests.sh b/testing/scenario_app/run_ios_tests.sh index 026aad25423b5..fca3af48dd6fa 100755 --- a/testing/scenario_app/run_ios_tests.sh +++ b/testing/scenario_app/run_ios_tests.sh @@ -21,7 +21,7 @@ pushd ios/Scenarios set -o pipefail && xcodebuild -sdk iphonesimulator \ -scheme Scenarios \ - -destination 'platform=iOS Simulator,name=iPhone SE' \ + -destination 'platform=iOS Simulator,OS=12.2,name=iPhone SE' \ test \ FLUTTER_ENGINE=$FLUTTER_ENGINE | $PRETTY From 1bf04ad9d0aa6abd217076a6c0803824cdcadcce Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 4 Dec 2019 20:24:10 -0500 Subject: [PATCH 326/591] Disable fml_tests until they're fixed on Fuchsia (#14137) --- BUILD.gn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BUILD.gn b/BUILD.gn index 419ae849544a1..6711b06124153 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -97,7 +97,8 @@ group("flutter") { if (is_fuchsia) { public_deps += [ "$flutter_root/flow:flow_tests", - "$flutter_root/fml:fml_tests", + # TODO(gw280): Re-enable fml_tests on Fuchsia (https://github.com/flutter/flutter/issues/46122) + # "$flutter_root/fml:fml_tests", ] } } From d117ac979c28363a0a6b02d4a54945212a88b6f9 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Wed, 4 Dec 2019 19:29:31 -0800 Subject: [PATCH 327/591] Wire up Opacity on Fuchsia, round 2 (#14024) * Remove erroneous ChildView opacity * Wire frame metrics through contexts * Maintain layer stack inside of SceneBuilder * Remove EnsureSingleChild * Centralize system-composite and elevation logic * Wire up OpacityLayer to Scenic --- ci/licenses_golden/licenses_flutter | 4 + flow/BUILD.gn | 4 + flow/layers/child_scene_layer.cc | 17 ++- flow/layers/container_layer.cc | 3 +- flow/layers/container_layer.h | 2 +- flow/layers/elevated_container_layer.cc | 49 ++++++++ flow/layers/elevated_container_layer.h | 34 +++++ .../layers/fuchsia_system_composited_layer.cc | 55 ++++++++ flow/layers/fuchsia_system_composited_layer.h | 37 ++++++ flow/layers/layer.cc | 7 +- flow/layers/layer.h | 23 ++-- flow/layers/layer_tree.cc | 60 ++++++--- flow/layers/layer_tree.h | 20 ++- flow/layers/layer_tree_unittests.cc | 17 +-- flow/layers/opacity_layer.cc | 119 ++++++++++++------ flow/layers/opacity_layer.h | 59 ++++++--- flow/layers/opacity_layer_unittests.cc | 2 +- flow/layers/physical_shape_layer.cc | 76 +++-------- flow/layers/physical_shape_layer.h | 46 +++---- flow/layers/physical_shape_layer_unittests.cc | 48 +------ flow/raster_cache.cc | 4 +- flow/scene_update_context.cc | 113 ++++++++--------- flow/scene_update_context.h | 57 +++++---- flow/testing/layer_test.h | 9 +- flow/view_holder.cc | 20 +-- flow/view_holder.h | 4 - lib/ui/compositing.dart | 11 -- lib/ui/compositing/scene.cc | 26 ++-- lib/ui/compositing/scene.h | 2 +- lib/ui/compositing/scene_builder.cc | 80 +++++------- lib/ui/compositing/scene_builder.h | 13 +- lib/ui/compositing/scene_host.cc | 16 +-- lib/ui/compositing/scene_host.h | 11 +- lib/ui/painting/engine_layer.cc | 2 - lib/ui/painting/engine_layer.h | 2 +- lib/ui/window/viewport_metrics.cc | 16 ++- lib/ui/window/viewport_metrics.h | 5 +- lib/web_ui/lib/src/ui/compositing.dart | 12 -- shell/common/engine.cc | 9 +- shell/common/persistent_cache_unittests.cc | 2 +- shell/common/shell_test.cc | 22 +++- shell/common/shell_test.h | 4 +- shell/common/shell_unittests.cc | 26 ++++ .../embedder/tests/embedder_unittests.cc | 1 - 44 files changed, 668 insertions(+), 481 deletions(-) create mode 100644 flow/layers/elevated_container_layer.cc create mode 100644 flow/layers/elevated_container_layer.h create mode 100644 flow/layers/fuchsia_system_composited_layer.cc create mode 100644 flow/layers/fuchsia_system_composited_layer.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 299844a2a98a4..7eec11ad58215 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -48,6 +48,10 @@ FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h FILE: ../../../flutter/flow/layers/container_layer_unittests.cc +FILE: ../../../flutter/flow/layers/elevated_container_layer.cc +FILE: ../../../flutter/flow/layers/elevated_container_layer.h +FILE: ../../../flutter/flow/layers/fuchsia_system_composited_layer.cc +FILE: ../../../flutter/flow/layers/fuchsia_system_composited_layer.h FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h FILE: ../../../flutter/flow/layers/layer_tree.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 137aa76d57811..f3ed537fe9113 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -28,6 +28,8 @@ source_set("flow") { "layers/color_filter_layer.h", "layers/container_layer.cc", "layers/container_layer.h", + "layers/elevated_container_layer.cc", + "layers/elevated_container_layer.h", "layers/layer.cc", "layers/layer.h", "layers/layer_tree.cc", @@ -76,6 +78,8 @@ source_set("flow") { sources += [ "layers/child_scene_layer.cc", "layers/child_scene_layer.h", + "layers/fuchsia_system_composited_layer.cc", + "layers/fuchsia_system_composited_layer.h", "scene_update_context.cc", "scene_update_context.h", "view_holder.cc", diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index e5652a2c87889..10b5a277db575 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -20,10 +20,25 @@ ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll"); set_needs_system_composite(true); + + // An alpha "hole punch" is required if the frame behind us is not opaque. + if (!context->is_opaque) { + set_paint_bounds( + SkRect::MakeXYWH(offset_.fX, offset_.fY, size_.fWidth, size_.fHeight)); + } } void ChildSceneLayer::Paint(PaintContext& context) const { - FML_NOTREACHED() << "This layer never needs painting."; + TRACE_EVENT0("flutter", "ChildSceneLayer::Paint"); + FML_DCHECK(needs_painting()); + + // If we are being rendered into our own frame using the system compositor, + // then it is neccesary to "punch a hole" in the canvas/frame behind us so + // that group opacity looks correct. + SkPaint paint; + paint.setColor(SK_ColorTRANSPARENT); + paint.setBlendMode(SkBlendMode::kSrc); + context.leaf_nodes_canvas->drawRect(paint_bounds(), paint); } void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) { diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index fdb13411a9ac8..39372f6c84dd8 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -9,8 +9,7 @@ namespace flutter { ContainerLayer::ContainerLayer() {} void ContainerLayer::Add(std::shared_ptr layer) { - layer->set_parent(this); - layers_.push_back(std::move(layer)); + layers_.emplace_back(std::move(layer)); } void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index a0c054b1ff15c..df5be5e8b9466 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -14,7 +14,7 @@ class ContainerLayer : public Layer { public: ContainerLayer(); - void Add(std::shared_ptr layer); + virtual void Add(std::shared_ptr layer); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/elevated_container_layer.cc b/flow/layers/elevated_container_layer.cc new file mode 100644 index 0000000000000..cd68b06713992 --- /dev/null +++ b/flow/layers/elevated_container_layer.cc @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/elevated_container_layer.h" + +namespace flutter { +namespace { + +float ClampElevation(float elevation, + float parent_elevation, + float max_elevation) { + // TODO(mklim): Deal with bounds overflow more elegantly. We'd like to be + // able to have developers specify the behavior here to alternatives besides + // clamping, like normalization on some arbitrary curve. + float clamped_elevation = elevation; + if (max_elevation > -1 && (parent_elevation + elevation) > max_elevation) { + // Clamp the local z coordinate at our max bound. Take into account the + // parent z position here to fix clamping in cases where the child is + // overflowing because of its parents. + clamped_elevation = max_elevation - parent_elevation; + } + + return clamped_elevation; +} + +} // namespace + +ElevatedContainerLayer::ElevatedContainerLayer(float elevation) + : elevation_(elevation), clamped_elevation_(elevation) {} + +void ElevatedContainerLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "ElevatedContainerLayer::Preroll"); + + // Track total elevation as we walk the tree, in order to deal with bounds + // overflow in z. + parent_elevation_ = context->total_elevation; + clamped_elevation_ = ClampElevation(elevation_, parent_elevation_, + context->frame_physical_depth); + context->total_elevation += clamped_elevation_; + + ContainerLayer::Preroll(context, matrix); + + // Restore the elevation for our parent. + context->total_elevation = parent_elevation_; +} + +} // namespace flutter diff --git a/flow/layers/elevated_container_layer.h b/flow/layers/elevated_container_layer.h new file mode 100644 index 0000000000000..9c7a8b051f118 --- /dev/null +++ b/flow/layers/elevated_container_layer.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_LAYERS_ELEVATED_CONTAINER_LAYER_H_ +#define FLUTTER_FLOW_LAYERS_ELEVATED_CONTAINER_LAYER_H_ + +#include "flutter/flow/layers/container_layer.h" + +namespace flutter { + +class ElevatedContainerLayer : public ContainerLayer { + public: + ElevatedContainerLayer(float elevation); + ~ElevatedContainerLayer() override = default; + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + float elevation() const { return clamped_elevation_; } + float total_elevation() const { + return parent_elevation_ + clamped_elevation_; + } + + private: + float parent_elevation_ = 0.0f; + float elevation_ = 0.0f; + float clamped_elevation_ = 0.0f; + + FML_DISALLOW_COPY_AND_ASSIGN(ElevatedContainerLayer); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_ELEVATED_CONTAINER_LAYER_H_ diff --git a/flow/layers/fuchsia_system_composited_layer.cc b/flow/layers/fuchsia_system_composited_layer.cc new file mode 100644 index 0000000000000..8c4a1b2ae26e3 --- /dev/null +++ b/flow/layers/fuchsia_system_composited_layer.cc @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/fuchsia_system_composited_layer.h" + +namespace flutter { + +FuchsiaSystemCompositedLayer::FuchsiaSystemCompositedLayer(SkColor color, + SkAlpha opacity, + float elevation) + : ElevatedContainerLayer(elevation), color_(color), opacity_(opacity) {} + +void FuchsiaSystemCompositedLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "FuchsiaSystemCompositedLayer::Preroll"); + + const float parent_is_opaque = context->is_opaque; + context->mutators_stack.PushOpacity(opacity_); + context->is_opaque = parent_is_opaque && (opacity_ == SK_AlphaOPAQUE); + ElevatedContainerLayer::Preroll(context, matrix); + context->is_opaque = parent_is_opaque; + context->mutators_stack.Pop(); +} + +void FuchsiaSystemCompositedLayer::UpdateScene(SceneUpdateContext& context) { + FML_DCHECK(needs_system_composite()); + + // Retained rendering: speedup by reusing a retained entity node if + // possible. When an entity node is reused, no paint layer is added to the + // frame so we won't call Paint. + LayerRasterCacheKey key(unique_id(), context.Matrix()); + if (context.HasRetainedNode(key)) { + TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); + const scenic::EntityNode& retained_node = context.GetRetainedNode(key); + FML_DCHECK(context.top_entity()); + FML_DCHECK(retained_node.session() == context.session()); + context.top_entity()->embedder_node().AddChild(retained_node); + return; + } + + TRACE_EVENT_INSTANT0("flutter", "retained cache miss, creating"); + // If we can't find an existing retained surface, create one. + SceneUpdateContext::Frame frame(context, rrect_, color_, opacity_ / 255.0f, + elevation(), this); + for (auto& layer : layers()) { + if (layer->needs_painting()) { + frame.AddPaintLayer(layer.get()); + } + } + + ElevatedContainerLayer::UpdateScene(context); +} + +} // namespace flutter diff --git a/flow/layers/fuchsia_system_composited_layer.h b/flow/layers/fuchsia_system_composited_layer.h new file mode 100644 index 0000000000000..2fe00ee6d550f --- /dev/null +++ b/flow/layers/fuchsia_system_composited_layer.h @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_LAYERS_FUCHSIA_SYSTEM_COMPOSITED_LAYER_H_ +#define FLUTTER_FLOW_LAYERS_FUCHSIA_SYSTEM_COMPOSITED_LAYER_H_ + +#include "flutter/flow/layers/elevated_container_layer.h" +#include "flutter/flow/scene_update_context.h" + +namespace flutter { + +class FuchsiaSystemCompositedLayer : public ElevatedContainerLayer { + public: + static bool can_system_composite() { return true; } + + FuchsiaSystemCompositedLayer(SkColor color, SkAlpha opacity, float elevation); + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void UpdateScene(SceneUpdateContext& context) override; + + void set_dimensions(SkRRect rrect) { rrect_ = rrect; } + + SkColor color() const { return color_; } + SkAlpha opacity() const { return opacity_; } + + private: + SkRRect rrect_ = SkRRect::MakeEmpty(); + SkColor color_ = SK_ColorTRANSPARENT; + SkAlpha opacity_ = SK_AlphaOPAQUE; + + FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaSystemCompositedLayer); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_FUCHSIA_SYSTEM_COMPOSITED_LAYER_H_ diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index b729f582a0a9a..5ad6a6bee1499 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -10,10 +10,9 @@ namespace flutter { Layer::Layer() - : parent_(nullptr), - needs_system_composite_(false), - paint_bounds_(SkRect::MakeEmpty()), - unique_id_(NextUniqueID()) {} + : paint_bounds_(SkRect::MakeEmpty()), + unique_id_(NextUniqueID()), + needs_system_composite_(false) {} Layer::~Layer() = default; diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 66944376e8ce8..731509876b611 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -42,8 +42,6 @@ static constexpr SkRect kGiantRect = SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); // This should be an exact copy of the Clip enum in painting.dart. enum Clip { none, hardEdge, antiAlias, antiAliasWithSaveLayer }; -class ContainerLayer; - struct PrerollContext { RasterCache* raster_cache; GrContext* gr_context; @@ -52,13 +50,21 @@ struct PrerollContext { SkColorSpace* dst_color_space; SkRect cull_rect; - // The following allows us to paint in the end of subtree preroll + // These allow us to paint in the end of subtree Preroll. const Stopwatch& raster_time; const Stopwatch& ui_time; TextureRegistry& texture_registry; const bool checkerboard_offscreen_layers; + + // These allow us to make use of the scene metrics during Preroll. + float frame_physical_depth; + float frame_device_pixel_ratio; + + // These allow us to track properties like elevation, opacity, and the + // prescence of a platform view during Preroll. float total_elevation = 0.0f; bool has_platform_view = false; + bool is_opaque = true; }; // Represents a single composited layer. Created on the UI thread but then @@ -90,6 +96,10 @@ class Layer { TextureRegistry& texture_registry; const RasterCache* raster_cache; const bool checkerboard_offscreen_layers; + + // These allow us to make use of the scene metrics during Paint. + float frame_physical_depth; + float frame_device_pixel_ratio; }; // Calls SkCanvas::saveLayer and restores the layer upon destruction. Also @@ -126,10 +136,6 @@ class Layer { virtual void UpdateScene(SceneUpdateContext& context); #endif - ContainerLayer* parent() const { return parent_; } - - void set_parent(ContainerLayer* parent) { parent_ = parent; } - bool needs_system_composite() const { return needs_system_composite_; } void set_needs_system_composite(bool value) { needs_system_composite_ = value; @@ -148,10 +154,9 @@ class Layer { uint64_t unique_id() const { return unique_id_; } private: - ContainerLayer* parent_; - bool needs_system_composite_; SkRect paint_bounds_; uint64_t unique_id_; + bool needs_system_composite_; static uint64_t NextUniqueID(); diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index caa5531a79c06..f3fb164136913 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -11,8 +11,12 @@ namespace flutter { -LayerTree::LayerTree() - : frame_size_{}, +LayerTree::LayerTree(const SkISize& frame_size, + float frame_physical_depth, + float frame_device_pixel_ratio) + : frame_size_(frame_size), + frame_physical_depth_(frame_physical_depth), + frame_device_pixel_ratio_(frame_device_pixel_ratio), rasterizer_tracing_threshold_(0), checkerboard_raster_cache_images_(false), checkerboard_offscreen_layers_(false) {} @@ -46,7 +50,9 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, frame.context().raster_time(), frame.context().ui_time(), frame.context().texture_registry(), - checkerboard_offscreen_layers_}; + checkerboard_offscreen_layers_, + frame_physical_depth_, + frame_device_pixel_ratio_}; root_layer_->Preroll(&context, frame.root_surface_transformation()); } @@ -55,12 +61,22 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, void LayerTree::UpdateScene(SceneUpdateContext& context, scenic::ContainerNode& container) { TRACE_EVENT0("flutter", "LayerTree::UpdateScene"); + + // Ensure the context is aware of the view metrics. + context.set_dimensions(frame_size_, frame_physical_depth_, + frame_device_pixel_ratio_); + const auto& metrics = context.metrics(); + FML_DCHECK(metrics->scale_x > 0.0f); + FML_DCHECK(metrics->scale_y > 0.0f); + FML_DCHECK(metrics->scale_z > 0.0f); + SceneUpdateContext::Transform transform(context, // context 1.0f / metrics->scale_x, // X 1.0f / metrics->scale_y, // Y 1.0f / metrics->scale_z // Z ); + SceneUpdateContext::Frame frame( context, SkRRect::MakeRect( @@ -104,7 +120,9 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, frame.context().ui_time(), frame.context().texture_registry(), ignore_raster_cache ? nullptr : &frame.context().raster_cache(), - checkerboard_offscreen_layers_}; + checkerboard_offscreen_layers_, + frame_physical_depth_, + frame_device_pixel_ratio_}; if (root_layer_->needs_painting()) root_layer_->Paint(context); @@ -128,16 +146,18 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { root_surface_transformation.reset(); PrerollContext preroll_context{ - nullptr, // raster_cache (don't consult the cache) - nullptr, // gr_context (used for the raster cache) - nullptr, // external view embedder - unused_stack, // mutator stack - nullptr, // SkColorSpace* dst_color_space - kGiantRect, // SkRect cull_rect - unused_stopwatch, // frame time (dont care) - unused_stopwatch, // engine time (dont care) - unused_texture_registry, // texture registry (not supported) - false, // checkerboard_offscreen_layers + nullptr, // raster_cache (don't consult the cache) + nullptr, // gr_context (used for the raster cache) + nullptr, // external view embedder + unused_stack, // mutator stack + nullptr, // SkColorSpace* dst_color_space + kGiantRect, // SkRect cull_rect + unused_stopwatch, // frame time (dont care) + unused_stopwatch, // engine time (dont care) + unused_texture_registry, // texture registry (not supported) + false, // checkerboard_offscreen_layers + frame_physical_depth_, // maximum depth allowed for rendering + frame_device_pixel_ratio_ // ratio between logical and physical }; SkISize canvas_size = canvas->getBaseLayerSize(); @@ -149,11 +169,13 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { canvas, // canvas nullptr, nullptr, - unused_stopwatch, // frame time (dont care) - unused_stopwatch, // engine time (dont care) - unused_texture_registry, // texture registry (not supported) - nullptr, // raster cache - false // checkerboard offscreen layers + unused_stopwatch, // frame time (dont care) + unused_stopwatch, // engine time (dont care) + unused_texture_registry, // texture registry (not supported) + nullptr, // raster cache + false, // checkerboard offscreen layers + frame_physical_depth_, // maximum depth allowed for rendering + frame_device_pixel_ratio_ // ratio between logical and physical }; // Even if we don't have a root layer, we still need to create an empty diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index b9edeeceba2ed..10a10d6711ab1 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -14,13 +14,14 @@ #include "flutter/fml/macros.h" #include "flutter/fml/time/time_delta.h" #include "third_party/skia/include/core/SkPicture.h" -#include "third_party/skia/include/core/SkSize.h" namespace flutter { class LayerTree { public: - LayerTree(); + LayerTree(const SkISize& frame_size, + float frame_physical_depth, + float frame_device_pixel_ratio); void Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false); @@ -42,8 +43,8 @@ class LayerTree { } const SkISize& frame_size() const { return frame_size_; } - - void set_frame_size(const SkISize& frame_size) { frame_size_ = frame_size; } + float frame_physical_depth() const { return frame_physical_depth_; } + float frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; } void RecordBuildTime(fml::TimePoint begin_start); fml::TimePoint build_start() const { return build_start_; } @@ -69,18 +70,15 @@ class LayerTree { checkerboard_offscreen_layers_ = checkerboard; } - void set_device_pixel_ratio(double device_pixel_ratio) { - device_pixel_ratio_ = device_pixel_ratio; - } - - double device_pixel_ratio() const { return device_pixel_ratio_; } + double device_pixel_ratio() const { return frame_device_pixel_ratio_; } private: - SkISize frame_size_ = SkISize::MakeEmpty(); // Physical pixels. - double device_pixel_ratio_ = 1.0; std::shared_ptr root_layer_; fml::TimePoint build_start_; fml::TimePoint build_finish_; + SkISize frame_size_ = SkISize::MakeEmpty(); // Physical pixels. + float frame_physical_depth_; + float frame_device_pixel_ratio_ = 1.0f; // Logical / Physical pixels ratio. uint32_t rasterizer_tracing_threshold_; bool checkerboard_raster_cache_images_; bool checkerboard_offscreen_layers_; diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index e0961723e3c6e..7d4922ff3be48 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -17,13 +17,16 @@ namespace testing { class LayerTreeTest : public CanvasTest { public: - void SetUp() override { - root_transform_ = SkMatrix::MakeTrans(1.0f, 1.0f); - scoped_frame_ = compositor_context_.AcquireFrame( - nullptr, &mock_canvas(), nullptr, root_transform_, false, nullptr); - } - - void TearDown() override { scoped_frame_ = nullptr; } + LayerTreeTest() + : layer_tree_(SkISize::Make(64, 64), 100.0f, 1.0f), + compositor_context_(fml::kDefaultFrameBudget), + root_transform_(SkMatrix::MakeTrans(1.0f, 1.0f)), + scoped_frame_(compositor_context_.AcquireFrame(nullptr, + &mock_canvas(), + nullptr, + root_transform_, + false, + nullptr)) {} LayerTree& layer_tree() { return layer_tree_; } CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 1b407307fd068..5592f40f72f8c 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -4,64 +4,101 @@ #include "flutter/flow/layers/opacity_layer.h" -#include "flutter/flow/layers/transform_layer.h" +#include "flutter/fml/trace_event.h" +#include "third_party/skia/include/core/SkPaint.h" namespace flutter { -OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset) - : alpha_(alpha), offset_(offset) {} +// The OpacityLayer has no real "elevation", but we want to avoid Z-fighting +// when using the system compositor. Choose a small but non-zero value for +// this. +constexpr float kOpacityElevationWhenUsingSystemCompositor = 0.01f; -void OpacityLayer::EnsureSingleChild() { - FML_DCHECK(layers().size() > 0); // OpacityLayer should never be a leaf +#if !defined(OS_FUCHSIA) +void OpacityLayerBase::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + const float parent_is_opaque = context->is_opaque; - if (layers().size() == 1) { - return; - } + context->mutators_stack.PushOpacity(opacity_); + context->is_opaque = parent_is_opaque && (opacity_ == SK_AlphaOPAQUE); + ContainerLayer::Preroll(context, matrix); + context->is_opaque = parent_is_opaque; + context->mutators_stack.Pop(); +} +#endif - // Be careful: SkMatrix's default constructor doesn't initialize the matrix to - // identity. Hence we have to explicitly call SkMatrix::setIdentity. - SkMatrix identity; - identity.setIdentity(); - auto new_child = std::make_shared(identity); +OpacityLayer::OpacityLayer(SkAlpha opacity, const SkPoint& offset) + : OpacityLayerBase(SK_ColorTRANSPARENT, + opacity, + kOpacityElevationWhenUsingSystemCompositor), + offset_(offset) { + // Ensure OpacityLayer has only one direct child. + // + // This is needed to ensure that retained rendering can always be applied to + // save the costly saveLayer. + // + // Any children will be actually added as children of this empty + // ContainerLayer. + ContainerLayer::Add(std::make_shared()); +} - for (auto& child : layers()) { - new_child->Add(child); - } - ClearChildren(); - Add(new_child); +void OpacityLayer::Add(std::shared_ptr layer) { + GetChildContainer()->Add(std::move(layer)); } void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "OpacityLayer::Preroll"); - EnsureSingleChild(); + + ContainerLayer* container = GetChildContainer(); + FML_DCHECK(!container->layers().empty()); // OpacityLayer can't be a leaf. + + // Factor in the offset during Preroll. |OpacityLayerBase| will handle the + // opacity. SkMatrix child_matrix = matrix; child_matrix.postTranslate(offset_.fX, offset_.fY); context->mutators_stack.PushTransform( SkMatrix::MakeTrans(offset_.fX, offset_.fY)); - context->mutators_stack.PushOpacity(alpha_); - ContainerLayer::Preroll(context, child_matrix); + OpacityLayerBase::Preroll(context, child_matrix); context->mutators_stack.Pop(); - context->mutators_stack.Pop(); - set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); - // See |EnsureSingleChild|. - FML_DCHECK(layers().size() == 1); - if (!context->has_platform_view && context->raster_cache && - SkRect::Intersects(context->cull_rect, paint_bounds())) { - Layer* child = layers()[0].get(); - SkMatrix ctm = child_matrix; + + // When using the system compositor, do not include the offset since we are + // rendering as a separate piece of geometry and the offset will be baked into + // that geometry's transform. + if (OpacityLayerBase::can_system_composite()) { + set_dimensions(SkRRect::MakeRect(paint_bounds())); + set_needs_system_composite(true); + } else { + set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); + + if (!context->has_platform_view && context->raster_cache && + SkRect::Intersects(context->cull_rect, paint_bounds())) { + SkMatrix ctm = child_matrix; #ifndef SUPPORT_FRACTIONAL_TRANSLATION - ctm = RasterCache::GetIntegralTransCTM(ctm); + ctm = RasterCache::GetIntegralTransCTM(ctm); #endif - context->raster_cache->Prepare(context, child, ctm); + context->raster_cache->Prepare(context, container, ctm); + } } } +#if defined(OS_FUCHSIA) + +void OpacityLayer::UpdateScene(SceneUpdateContext& context) { + SceneUpdateContext::Transform transform( + context, SkMatrix::MakeTrans(offset_.fX, offset_.fY)); + + // OpacityLayerBase will handle applying the opacity itself. + OpacityLayerBase::UpdateScene(context); +} + +#endif + void OpacityLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "OpacityLayer::Paint"); FML_DCHECK(needs_painting()); SkPaint paint; - paint.setAlpha(alpha_); + paint.setAlpha(opacity()); SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); @@ -71,13 +108,10 @@ void OpacityLayer::Paint(PaintContext& context) const { context.leaf_nodes_canvas->getTotalMatrix())); #endif - // See |EnsureSingleChild|. - FML_DCHECK(layers().size() == 1); - if (context.raster_cache) { + ContainerLayer* container = GetChildContainer(); const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); - RasterCacheResult child_cache = - context.raster_cache->Get(layers()[0].get(), ctm); + RasterCacheResult child_cache = context.raster_cache->Get(container, ctm); if (child_cache.is_valid()) { child_cache.draw(*context.leaf_nodes_canvas, &paint); return; @@ -91,8 +125,7 @@ void OpacityLayer::Paint(PaintContext& context) const { // RasterCache::GetIntegralTransCTM optimization. // // Note that the following lines are only accessible when the raster cache is - // not available (e.g., when we're using the software backend in golden - // tests). + // not available, or when a cache miss occurs. SkRect saveLayerBounds; paint_bounds() .makeOffset(-offset_.fX, -offset_.fY) @@ -100,7 +133,13 @@ void OpacityLayer::Paint(PaintContext& context) const { Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create(context, saveLayerBounds, &paint); - PaintChildren(context); + OpacityLayerBase::Paint(context); +} + +ContainerLayer* OpacityLayer::GetChildContainer() const { + FML_DCHECK(layers().size() == 1); + + return static_cast(layers()[0].get()); } } // namespace flutter diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index f1c18c51918e7..f4951e0557c70 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -5,15 +5,42 @@ #ifndef FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_ #define FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/elevated_container_layer.h" +#if defined(OS_FUCHSIA) +#include "flutter/flow/layers/fuchsia_system_composited_layer.h" +#endif namespace flutter { +#if !defined(OS_FUCHSIA) +class OpacityLayerBase : public ContainerLayer { + public: + static bool can_system_composite() { return false; } + + OpacityLayerBase(SkColor color, SkAlpha opacity, float elevation) + : color_(color), opacity_(opacity) {} + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + void set_dimensions(SkRRect rrect) {} + + SkColor color() const { return color_; } + SkAlpha opacity() const { return opacity_; } + float elevation() const { return 0; } + + private: + SkColor color_; + SkAlpha opacity_; +}; +#else +using OpacityLayerBase = FuchsiaSystemCompositedLayer; +#endif + // Don't add an OpacityLayer with no children to the layer tree. Painting an // OpacityLayer is very costly due to the saveLayer call. If there's no child, -// having the OpacityLayer or not has the same effect. In debug_unopt build, the -// |EnsureSingleChild| will assert if there are no children. -class OpacityLayer : public ContainerLayer { +// having the OpacityLayer or not has the same effect. In debug_unopt build, +// |Preroll| will assert if there are no children. +class OpacityLayer : public OpacityLayerBase { public: // An offset is provided here because OpacityLayer.addToScene method in the // Flutter framework can take an optional offset argument. @@ -25,28 +52,20 @@ class OpacityLayer : public ContainerLayer { // the retained rendering inefficient as a small offset change could propagate // to many leaf layers. Therefore we try to capture that offset here to stop // the propagation as repainting the OpacityLayer is expensive. - OpacityLayer(int alpha, const SkPoint& offset); + OpacityLayer(SkAlpha alpha, const SkPoint& offset); - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Add(std::shared_ptr layer) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; +#if defined(OS_FUCHSIA) + void UpdateScene(SceneUpdateContext& context) override; +#endif void Paint(PaintContext& context) const override; - // TODO(chinmaygarde): Once SCN-139 is addressed, introduce a new node in the - // session scene hierarchy. - private: - int alpha_; - SkPoint offset_; + ContainerLayer* GetChildContainer() const; - // Restructure (if necessary) OpacityLayer to have only one child. - // - // This is needed to ensure that retained rendering can always be applied to - // save the costly saveLayer. - // - // If there are multiple children, this creates a new identity TransformLayer, - // sets all children to be the TransformLayer's children, and sets that - // TransformLayer as the single child of this OpacityLayer. - void EnsureSingleChild(); + SkPoint offset_; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); }; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 6ca78a8538fdf..8410d7767c890 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -20,7 +20,7 @@ TEST_F(OpacityLayerTest, LeafLayer) { std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), - "layers\\(\\)\\.size\\(\\) > 0"); + "\\!container->layers\\(\\)\\.empty\\(\\)"); } TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 91b6c73071d17..fed7a5fbe7163 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -14,18 +14,11 @@ const SkScalar kLightRadius = 800; PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, SkColor shadow_color, - SkScalar device_pixel_ratio, - float viewport_depth, float elevation, const SkPath& path, Clip clip_behavior) - : color_(color), + : PhysicalShapeLayerBase(color, SK_AlphaOPAQUE, elevation), shadow_color_(shadow_color), - device_pixel_ratio_(device_pixel_ratio), -#if defined(OS_FUCHSIA) - viewport_depth_(viewport_depth), -#endif - elevation_(elevation), path_(path), isRect_(false), clip_behavior_(clip_behavior) { @@ -48,79 +41,44 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, // an SkPath. frameRRect_ = SkRRect::MakeRect(path.getBounds()); } + + set_dimensions(frameRRect_); } void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); - context->total_elevation += elevation_; - total_elevation_ = context->total_elevation; - SkRect child_paint_bounds; - PrerollChildren(context, matrix, &child_paint_bounds); - context->total_elevation -= elevation_; - if (elevation_ == 0) { + PhysicalShapeLayerBase::Preroll(context, matrix); + + if (elevation() == 0) { set_paint_bounds(path_.getBounds()); } else { -#if defined(OS_FUCHSIA) - // Let the system compositor draw all shadows for us. - set_needs_system_composite(true); -#else + if (PhysicalShapeLayerBase::can_system_composite()) { + set_needs_system_composite(true); + return; + } + // We will draw the shadow in Paint(), so add some margin to the paint // bounds to leave space for the shadow. We fill this whole region and clip // children to it so we don't need to join the child paint bounds. - set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_, - device_pixel_ratio_)); -#endif // defined(OS_FUCHSIA) - } -} - -#if defined(OS_FUCHSIA) - -void PhysicalShapeLayer::UpdateScene(SceneUpdateContext& context) { - FML_DCHECK(needs_system_composite()); - TRACE_EVENT0("flutter", "PhysicalShapeLayer::UpdateScene"); - - // Retained rendering: speedup by reusing a retained entity node if possible. - // When an entity node is reused, no paint layer is added to the frame so we - // won't call PhysicalShapeLayer::Paint. - LayerRasterCacheKey key(unique_id(), context.Matrix()); - if (context.HasRetainedNode(key)) { - TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); - const scenic::EntityNode& retained_node = context.GetRetainedNode(key); - FML_DCHECK(context.top_entity()); - FML_DCHECK(retained_node.session() == context.session()); - context.top_entity()->entity_node().AddChild(retained_node); - return; - } - - TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); - // If we can't find an existing retained surface, create one. - SceneUpdateContext::Frame frame(context, frameRRect_, color_, elevation_, - total_elevation_, viewport_depth_, this); - for (auto& layer : layers()) { - if (layer->needs_painting()) { - frame.AddPaintLayer(layer.get()); - } + set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation(), + context->frame_device_pixel_ratio)); } - - UpdateSceneChildren(context); } -#endif // defined(OS_FUCHSIA) - void PhysicalShapeLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Paint"); FML_DCHECK(needs_painting()); - if (elevation_ != 0) { - DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation_, - SkColorGetA(color_) != 0xff, device_pixel_ratio_); + if (elevation() != 0) { + DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation(), + SkColorGetA(color()) != 0xff, context.frame_device_pixel_ratio); } // Call drawPath without clip if possible for better performance. SkPaint paint; - paint.setColor(color_); + paint.setColor(color()); paint.setAntiAlias(true); if (clip_behavior_ != Clip::antiAliasWithSaveLayer) { context.leaf_nodes_canvas->drawPath(path_, paint); diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index 1b5564c3662b3..ac54a7bbc6761 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -5,20 +5,28 @@ #ifndef FLUTTER_FLOW_LAYERS_PHYSICAL_SHAPE_LAYER_H_ #define FLUTTER_FLOW_LAYERS_PHYSICAL_SHAPE_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/elevated_container_layer.h" namespace flutter { -class PhysicalShapeLayer : public ContainerLayer { +class PhysicalShapeLayerBase : public ElevatedContainerLayer { public: - PhysicalShapeLayer(SkColor color, - SkColor shadow_color, - SkScalar device_pixel_ratio, - float viewport_depth, - float elevation, - const SkPath& path, - Clip clip_behavior); + static bool can_system_composite() { return false; } + + PhysicalShapeLayerBase(SkColor color, SkAlpha opacity, float elevation) + : ElevatedContainerLayer(elevation), color_(color) {} + + void set_dimensions(SkRRect rrect) {} + + SkColor color() const { return color_; } + SkAlpha opacity() const { return SK_AlphaOPAQUE; } + + private: + SkColor color_; +}; +class PhysicalShapeLayer : public PhysicalShapeLayerBase { + public: static SkRect ComputeShadowBounds(const SkRect& bounds, float elevation, float pixel_ratio); @@ -29,25 +37,17 @@ class PhysicalShapeLayer : public ContainerLayer { bool transparentOccluder, SkScalar dpr); - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + PhysicalShapeLayer(SkColor color, + SkColor shadow_color, + float elevation, + const SkPath& path, + Clip clip_behavior); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; -#if defined(OS_FUCHSIA) - void UpdateScene(SceneUpdateContext& context) override; -#endif // defined(OS_FUCHSIA) - - float total_elevation() const { return total_elevation_; } - private: - SkColor color_; SkColor shadow_color_; - SkScalar device_pixel_ratio_; -#if defined(OS_FUCHSIA) - float viewport_depth_ = 0.0f; -#endif - float elevation_ = 0.0f; - float total_elevation_ = 0.0f; SkPath path_; bool isRect_; SkRRect frameRRect_; diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index b9382318e8378..36107560fa4ed 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -18,8 +18,6 @@ using PhysicalShapeLayerTest = LayerTest; TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(SK_ColorBLACK, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth 0.0f, // elevation SkPath(), Clip::none); @@ -38,8 +36,6 @@ TEST_F(PhysicalShapeLayerTest, PaintBeforePreollDies) { auto mock_layer = std::make_shared(child_path, SkPaint()); auto layer = std::make_shared(SK_ColorBLACK, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth 0.0f, // elevation SkPath(), Clip::none); layer->Add(mock_layer); @@ -54,8 +50,6 @@ TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); auto layer = std::make_shared(SK_ColorGREEN, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth 0.0f, // elevation layer_path, Clip::none); layer->Preroll(preroll_context(), SkMatrix()); @@ -80,20 +74,14 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) { SkPath child2_path; child2_path.addRect(3, 2, 5, 15).close(); auto child1 = std::make_shared(SK_ColorRED, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth 0.0f, // elevation child1_path, Clip::none); auto child2 = std::make_shared(SK_ColorBLUE, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth 0.0f, // elevation child2_path, Clip::none); auto layer = std::make_shared(SK_ColorGREEN, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth 0.0f, // elevation layer_path, Clip::none); layer->Add(child1); @@ -133,30 +121,16 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { SkPath layer_path; layer_path.addRect(0, 0, 8, 8).close(); auto layer = std::make_shared( - SK_ColorGREEN, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - initial_elevation, layer_path, Clip::none); + SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, Clip::none); layer->Preroll(preroll_context(), SkMatrix()); - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not do any painting there. -#if defined(OS_FUCHSIA) - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_TRUE(layer->needs_system_composite()); -#else EXPECT_EQ(layer->paint_bounds(), PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), initial_elevation, 1.0f)); EXPECT_TRUE(layer->needs_painting()); EXPECT_FALSE(layer->needs_system_composite()); -#endif EXPECT_EQ(layer->total_elevation(), initial_elevation); - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(OS_FUCHSIA) SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); layer_paint.setAntiAlias(true); @@ -167,7 +141,6 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, MockCanvas::DrawCall{ 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -#endif } TEST_F(PhysicalShapeLayerTest, ElevationComplex) { @@ -190,10 +163,8 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { std::shared_ptr layers[4]; for (int i = 0; i < 4; i += 1) { layers[i] = std::make_shared( - SK_ColorBLACK, SK_ColorBLACK, - 1.0f, // pixel ratio - 1.0f, // depth - initial_elevations[i], layer_path, Clip::none); + SK_ColorBLACK, SK_ColorBLACK, initial_elevations[i], layer_path, + Clip::none); } layers[0]->Add(layers[1]); layers[0]->Add(layers[2]); @@ -201,27 +172,15 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { layers[0]->Preroll(preroll_context(), SkMatrix()); for (int i = 0; i < 4; i += 1) { - // On Fuchsia, the system compositor handles all elevated - // PhysicalShapeLayers and their shadows , so we do not do any painting - // there. -#if defined(OS_FUCHSIA) - EXPECT_EQ(layers[i]->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layers[i]->needs_painting()); - EXPECT_TRUE(layers[i]->needs_system_composite()); -#else EXPECT_EQ(layers[i]->paint_bounds(), (PhysicalShapeLayer::ComputeShadowBounds( layer_path.getBounds(), initial_elevations[i], 1.0f /* pixel_ratio */))); EXPECT_TRUE(layers[i]->needs_painting()); EXPECT_FALSE(layers[i]->needs_system_composite()); -#endif EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]); } - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(OS_FUCHSIA) SkPaint layer_paint; layer_paint.setColor(SK_ColorBLACK); layer_paint.setAntiAlias(true); @@ -241,7 +200,6 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, MockCanvas::DrawCall{ 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -#endif } } // namespace testing diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index f9388ad98d4b8..a3bbb20b9b05a 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -175,7 +175,9 @@ void RasterCache::Prepare(PrerollContext* context, context->ui_time, context->texture_registry, context->has_platform_view ? nullptr : context->raster_cache, - context->checkerboard_offscreen_layers}; + context->checkerboard_offscreen_layers, + context->frame_physical_depth, + context->frame_device_pixel_ratio}; if (layer->needs_painting()) { layer->Paint(paintContext); } diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 1c85bb658aacb..5ffcc4b272ce9 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -7,6 +7,7 @@ #include "flutter/flow/layers/layer.h" #include "flutter/flow/matrix_decomposition.h" #include "flutter/fml/trace_event.h" +#include "include/core/SkColor.h" namespace flutter { @@ -59,18 +60,16 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, scenic::ShapeNode shape_node, const SkRRect& rrect, SkColor color, + float opacity, const SkRect& paint_bounds, std::vector paint_layers, Layer* layer) { - // Frames always clip their children. - SetEntityNodeClipPlanes(entity_node, rrect.getBounds()); - // TODO(SCN-1274): SetClip() will be deleted. - entity_node.SetClip(0u, true /* clip to self */); - // We don't need a shape if the frame is zero size. if (rrect.isEmpty()) return; + SetEntityNodeClipPlanes(entity_node, rrect.getBounds()); + // isEmpty should account for this, but we are adding these experimental // checks to validate if this is the root cause for b/144933519. if (std::isnan(rrect.width()) || std::isnan(rrect.height())) { @@ -102,7 +101,9 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, // Check whether a solid color will suffice. if (paint_layers.empty()) { - SetShapeColor(shape_node, color); + scenic::Material material(session_); + SetMaterialColor(material, color, opacity); + shape_node.SetMaterial(material); return; } @@ -110,43 +111,38 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, const float scale_x = ScaleX(); const float scale_y = ScaleY(); - // Apply a texture to the whole shape. - SetShapeTextureAndColor(shape_node, color, scale_x, scale_y, shape_bounds, - std::move(paint_layers), layer, - std::move(entity_node)); -} - -void SceneUpdateContext::SetShapeTextureAndColor( - scenic::ShapeNode& shape_node, - SkColor color, - SkScalar scale_x, - SkScalar scale_y, - const SkRect& paint_bounds, - std::vector paint_layers, - Layer* layer, - scenic::EntityNode entity_node) { scenic::Image* image = GenerateImageIfNeeded( - color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer, + color, scale_x, scale_y, shape_bounds, std::move(paint_layers), layer, std::move(entity_node)); if (image != nullptr) { scenic::Material material(session_); + + // The final shape's color is material_color * texture_color. The passed in + // material color was already used as a background when generating the + // texture, so set the model color to |SK_ColorWHITE| in order to allow + // using the texture's color unmodified. + SetMaterialColor(material, SK_ColorWHITE, opacity); material.SetTexture(*image); shape_node.SetMaterial(material); return; } - SetShapeColor(shape_node, color); -} + // No texture was needed, so apply a solid color to the whole shape. + if (SkColorGetA(color) != 0 && opacity != 0.0f) { + scenic::Material material(session_); -void SceneUpdateContext::SetShapeColor(scenic::ShapeNode& shape_node, - SkColor color) { - if (SkColorGetA(color) == 0) + SetMaterialColor(material, color, opacity); + shape_node.SetMaterial(material); return; + } +} - scenic::Material material(session_); +void SceneUpdateContext::SetMaterialColor(scenic::Material& material, + SkColor color, + float opacity) { + const SkAlpha color_alpha = (SkAlpha)(SkColorGetA(color) * opacity); material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), - SkColorGetA(color)); - shape_node.SetMaterial(material); + color_alpha); } scenic::Image* SceneUpdateContext::GenerateImageIfNeeded( @@ -212,7 +208,9 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { frame.context().ui_time(), frame.context().texture_registry(), &frame.context().raster_cache(), - false}; + false, + frame_physical_depth_, + frame_device_pixel_ratio_}; canvas->restoreToCount(1); canvas->save(); canvas->clear(task.background_color); @@ -233,6 +231,7 @@ SceneUpdateContext::Entity::Entity(SceneUpdateContext& context) entity_node_(context.session()) { if (previous_entity_) previous_entity_->embedder_node().AddChild(entity_node_); + context.top_entity_ = this; } @@ -291,43 +290,37 @@ SceneUpdateContext::Transform::~Transform() { context().top_scale_y_ = previous_scale_y_; } -SceneUpdateContext::Shape::Shape(SceneUpdateContext& context) - : Entity(context), shape_node_(context.session()) { - entity_node().AddChild(shape_node_); +SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, + const SkRect& shape_bounds) + : Entity(context) { + SetEntityNodeClipPlanes(entity_node(), shape_bounds); } SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, - float local_elevation, - float world_elevation, - float depth, + float opacity, + float elevation, Layer* layer) - : Shape(context), + : Entity(context), + opacity_node_(context.session()), + shape_node_(context.session()), + layer_(layer), rrect_(rrect), - color_(color), paint_bounds_(SkRect::MakeEmpty()), - layer_(layer) { - if (depth > -1 && world_elevation > depth) { - // TODO(mklim): Deal with bounds overflow more elegantly. We'd like to be - // able to have developers specify the behavior here to alternatives besides - // clamping, like normalization on some arbitrary curve. - - // Clamp the local z coordinate at our max bound. Take into account the - // parent z position here to fix clamping in cases where the child is - // overflowing because of its parents. - const float parent_elevation = world_elevation - local_elevation; - local_elevation = depth - parent_elevation; - } - if (local_elevation != 0.0) { - entity_node().SetTranslation(0.f, 0.f, -local_elevation); - } + color_(color), + opacity_(opacity) { + entity_node().SetTranslation(0.f, 0.f, -elevation); + + entity_node().AddChild(shape_node_); + entity_node().AddChild(opacity_node_); + opacity_node_.SetOpacity(opacity_); } SceneUpdateContext::Frame::~Frame() { - context().CreateFrame(std::move(entity_node()), std::move(shape_node()), - rrect_, color_, paint_bounds_, std::move(paint_layers_), - layer_); + context().CreateFrame(std::move(entity_node()), std::move(shape_node_), + rrect_, color_, opacity_, paint_bounds_, + std::move(paint_layers_), layer_); } void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { @@ -336,10 +329,4 @@ void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { paint_bounds_.join(layer->paint_bounds()); } -SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, - const SkRect& shape_bounds) - : Entity(context) { - SetEntityNodeClipPlanes(entity_node(), shape_bounds); -} - } // namespace flutter diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index c61f5670ba7f2..c992fa20bba36 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -89,25 +89,20 @@ class SceneUpdateContext { float scale_x, float scale_y, float scale_z); - virtual ~Transform(); + ~Transform() override; private: float const previous_scale_x_; float const previous_scale_y_; }; - class Shape : public Entity { + class Clip : public Entity { public: - Shape(SceneUpdateContext& context); - virtual ~Shape() = default; - - scenic::ShapeNode& shape_node() { return shape_node_; } - - private: - scenic::ShapeNode shape_node_; + Clip(SceneUpdateContext& context, const SkRect& shape_bounds); + ~Clip() override = default; }; - class Frame : public Shape { + class Frame : public Entity { public: // When layer is not nullptr, the frame is associated with a layer subtree // rooted with that layer. The frame may then create a surface that will be @@ -115,27 +110,25 @@ class SceneUpdateContext { Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, - float local_elevation = 0.0f, - float parent_elevation = 0.0f, - float depth = 0.0f, + float opacity = 1.0f, + float elevation = 0.0f, Layer* layer = nullptr); - virtual ~Frame(); + ~Frame() override; + scenic::ContainerNode& embedder_node() override { return opacity_node_; } void AddPaintLayer(Layer* layer); private: - const SkRRect rrect_; - SkColor const color_; + scenic::OpacityNodeHACK opacity_node_; + scenic::ShapeNode shape_node_; std::vector paint_layers_; - SkRect paint_bounds_; Layer* layer_; - }; - class Clip : public Entity { - public: - Clip(SceneUpdateContext& context, const SkRect& shape_bounds); - ~Clip() = default; + SkRRect rrect_; + SkRect paint_bounds_; + SkColor color_; + float opacity_; }; SceneUpdateContext(scenic::Session* session, @@ -152,6 +145,17 @@ class SceneUpdateContext { } const fuchsia::ui::gfx::MetricsPtr& metrics() const { return metrics_; } + void set_dimensions(const SkISize& frame_physical_size, + float frame_physical_depth, + float frame_device_pixel_ratio) { + frame_physical_size_ = frame_physical_size; + frame_physical_depth_ = frame_physical_depth; + frame_device_pixel_ratio_ = frame_device_pixel_ratio; + } + const SkISize& frame_size() const { return frame_physical_size_; } + float frame_physical_depth() const { return frame_physical_depth_; } + float frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; } + // TODO(chinmaygarde): This method must submit the surfaces as soon as paint // tasks are done. However, given that there is no support currently for // Vulkan semaphores, we need to submit all the surfaces after an explicit @@ -197,6 +201,7 @@ class SceneUpdateContext { scenic::ShapeNode shape_node, const SkRRect& rrect, SkColor color, + float opacity, const SkRect& paint_bounds, std::vector paint_layers, Layer* layer); @@ -208,7 +213,9 @@ class SceneUpdateContext { std::vector paint_layers, Layer* layer, scenic::EntityNode entity_node); - void SetShapeColor(scenic::ShapeNode& shape_node, SkColor color); + void SetMaterialColor(scenic::Material& material, + SkColor color, + float opacity); scenic::Image* GenerateImageIfNeeded(SkColor color, SkScalar scale_x, SkScalar scale_y, @@ -225,6 +232,10 @@ class SceneUpdateContext { SurfaceProducer* const surface_producer_; fuchsia::ui::gfx::MetricsPtr metrics_; + SkISize frame_physical_size_; + float frame_physical_depth_ = 0.0f; + float frame_device_pixel_ratio_ = + 1.0f; // Ratio between logical and physical pixels. std::vector paint_tasks_; diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index e38d690a2eeb7..ab51af38af563 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -38,8 +38,11 @@ class LayerTestBase : public CanvasTestBase { mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), kGiantRect, /* cull_rect */ raster_time_, ui_time_, texture_registry_, - false, /* checkerboard_offscreen_layers */ - 0.0f /* total_elevation */ + false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ + 1.0f, /* frame_device_pixel_ratio */ + 0.0f, /* total_elevation */ + false, /* has_platform_view */ }), paint_context_({ TestT::mock_canvas().internal_canvas(), /* internal_nodes_canvas */ @@ -49,6 +52,8 @@ class LayerTestBase : public CanvasTestBase { raster_time_, ui_time_, texture_registry_, nullptr, /* raster_cache */ false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ + 1.0f, /* frame_device_pixel_ratio */ }) {} TextureRegistry& texture_regitry() { return texture_registry_; } diff --git a/flow/view_holder.cc b/flow/view_holder.cc index b49ca430da40d..7f8929d933705 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -104,14 +104,11 @@ void ViewHolder::UpdateScene(SceneUpdateContext& context, const SkSize& size, bool hit_testable) { if (pending_view_holder_token_.value) { - opacity_node_ = - std::make_unique(context.session()); entity_node_ = std::make_unique(context.session()); view_holder_ = std::make_unique( context.session(), std::move(pending_view_holder_token_), "Flutter SceneHost"); - opacity_node_->AddChild(*entity_node_); entity_node_->Attach(*view_holder_); ui_task_runner_->PostTask( [bind_callback = std::move(pending_bind_callback_), @@ -119,20 +116,18 @@ void ViewHolder::UpdateScene(SceneUpdateContext& context, bind_callback(view_holder_id); }); } - FML_DCHECK(opacity_node_); + FML_DCHECK(entity_node_); FML_DCHECK(view_holder_); - context.top_entity()->entity_node().AddChild(*opacity_node_); + context.top_entity()->embedder_node().AddChild(*entity_node_); entity_node_->SetTranslation(offset.x(), offset.y(), -0.1f); entity_node_->SetHitTestBehavior( hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault : fuchsia::ui::gfx::HitTestBehavior::kSuppress); - if (has_pending_opacity_) { - opacity_node_->SetOpacity(pending_opacity_); - - has_pending_opacity_ = false; - } if (has_pending_properties_) { + // TODO(dworsham): This should be derived from size and elevation. We + // should be able to Z-limit the view's box but otherwise it uses all of the + // available airspace. view_holder_->SetViewProperties(std::move(pending_properties_)); has_pending_properties_ = false; @@ -151,9 +146,4 @@ void ViewHolder::SetProperties(double width, has_pending_properties_ = true; } -void ViewHolder::SetOpacity(double opacity) { - pending_opacity_ = std::clamp(opacity, 0.0, 1.0); - has_pending_opacity_ = true; -} - } // namespace flutter diff --git a/flow/view_holder.h b/flow/view_holder.h index 8b49e216c2800..82d43eba826d4 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -51,7 +51,6 @@ class ViewHolder { double insetBottom, double insetLeft, bool focusable); - void SetOpacity(double opacity); // Creates or updates the contained ViewHolder resource using the specified // |SceneUpdateContext|. @@ -63,7 +62,6 @@ class ViewHolder { private: fml::RefPtr ui_task_runner_; - std::unique_ptr opacity_node_; std::unique_ptr entity_node_; std::unique_ptr view_holder_; @@ -71,9 +69,7 @@ class ViewHolder { BindCallback pending_bind_callback_; fuchsia::ui::gfx::ViewProperties pending_properties_; - double pending_opacity_; bool has_pending_properties_ = false; - bool has_pending_opacity_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ViewHolder); }; diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index 3eb13535e44cb..fc1b368bf4f82 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -694,13 +694,6 @@ class SceneHost extends NativeFieldWrapperClass2 { void Function(bool) viewStateChangedCallback) { _constructor(viewHolderToken, viewConnectedCallback, viewDisconnectedCallback, viewStateChangedCallback); } - SceneHost.fromViewHolderToken( - dynamic viewHolderToken, - void Function() viewConnectedCallback, - void Function() viewDisconnectedCallback, - void Function(bool) viewStateChangedCallback) { - _constructor(viewHolderToken, viewConnectedCallback, viewDisconnectedCallback, viewStateChangedCallback); - } void _constructor(dynamic viewHolderToken, void Function() viewConnectedCallback, void Function() viewDisconnectedCallback, void Function(bool) viewStateChangedCallback) native 'SceneHost_constructor'; @@ -720,8 +713,4 @@ class SceneHost extends NativeFieldWrapperClass2 { double insetBottom, double insetLeft, bool focusable) native 'SceneHost_setProperties'; - - /// Set the opacity of the linked scene. This opacity value is applied only - /// once, when the child scene is composited into our own. - void setOpacity(double opacity) native 'SceneHost_setOpacity'; } diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index fd3e86c7e5176..d02a232ac0433 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -7,6 +7,8 @@ #include "flutter/fml/trace_event.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/picture.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/window.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/tonic/converter/dart_converter.h" @@ -36,13 +38,19 @@ fml::RefPtr Scene::create(std::shared_ptr rootLayer, Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, - bool checkerboardOffscreenLayers) - : m_layerTree(new flutter::LayerTree()) { - m_layerTree->set_root_layer(std::move(rootLayer)); - m_layerTree->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); - m_layerTree->set_checkerboard_raster_cache_images( + bool checkerboardOffscreenLayers) { + auto viewport_metrics = UIDartState::Current()->window()->viewport_metrics(); + + layer_tree_ = std::make_unique( + SkISize::Make(viewport_metrics.physical_width, + viewport_metrics.physical_height), + static_cast(viewport_metrics.physical_depth), + static_cast(viewport_metrics.device_pixel_ratio)); + layer_tree_->set_root_layer(std::move(rootLayer)); + layer_tree_->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); + layer_tree_->set_checkerboard_raster_cache_images( checkerboardRasterCacheImages); - m_layerTree->set_checkerboard_offscreen_layers(checkerboardOffscreenLayers); + layer_tree_->set_checkerboard_offscreen_layers(checkerboardOffscreenLayers); } Scene::~Scene() {} @@ -56,11 +64,11 @@ Dart_Handle Scene::toImage(uint32_t width, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); - if (!m_layerTree) { + if (!layer_tree_) { return tonic::ToDart("Scene did not contain a layer tree."); } - auto picture = m_layerTree->Flatten(SkRect::MakeWH(width, height)); + auto picture = layer_tree_->Flatten(SkRect::MakeWH(width, height)); if (!picture) { return tonic::ToDart("Could not flatten scene into a layer tree."); } @@ -69,7 +77,7 @@ Dart_Handle Scene::toImage(uint32_t width, } std::unique_ptr Scene::takeLayerTree() { - return std::move(m_layerTree); + return std::move(layer_tree_); } } // namespace flutter diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index 2d280a534db63..42c972d8d4ee3 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -45,7 +45,7 @@ class Scene : public RefCountedDartWrappable { bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers); - std::unique_ptr m_layerTree; + std::unique_ptr layer_tree_; }; } // namespace flutter diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index 5b7148803c2c2..216e1c9284874 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -23,8 +23,6 @@ #include "flutter/fml/build_config.h" #include "flutter/lib/ui/painting/matrix.h" #include "flutter/lib/ui/painting/shader.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/window.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" @@ -80,7 +78,12 @@ void SceneBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) { }); } -SceneBuilder::SceneBuilder() = default; +SceneBuilder::SceneBuilder() { + // Add a ContainerLayer as the root layer, so that AddLayer operations are + // always valid. + PushLayer(std::make_shared()); +} + SceneBuilder::~SceneBuilder() = default; fml::RefPtr SceneBuilder::pushTransform( @@ -176,12 +179,6 @@ fml::RefPtr SceneBuilder::pushPhysicalShape(const CanvasPath* path, int clipBehavior) { auto layer = std::make_shared( static_cast(color), static_cast(shadow_color), - static_cast(UIDartState::Current() - ->window() - ->viewport_metrics() - .device_pixel_ratio), - static_cast( - UIDartState::Current()->window()->viewport_metrics().physical_depth), static_cast(elevation), path->path(), static_cast(clipBehavior)); PushLayer(layer); @@ -189,33 +186,24 @@ fml::RefPtr SceneBuilder::pushPhysicalShape(const CanvasPath* path, } void SceneBuilder::addRetained(fml::RefPtr retainedLayer) { - if (!current_layer_) { - return; - } - current_layer_->Add(retainedLayer->Layer()); + AddLayer(retainedLayer->Layer()); } void SceneBuilder::pop() { - if (!current_layer_) { - return; - } - current_layer_ = current_layer_->parent(); + PopLayer(); } void SceneBuilder::addPicture(double dx, double dy, Picture* picture, int hints) { - if (!current_layer_) { - return; - } SkPoint offset = SkPoint::Make(dx, dy); SkRect pictureRect = picture->picture()->cullRect(); pictureRect.offset(offset.x(), offset.y()); auto layer = std::make_unique( offset, UIDartState::CreateGPUObject(picture->picture()), !!(hints & 1), !!(hints & 2)); - current_layer_->Add(std::move(layer)); + AddLayer(std::move(layer)); } void SceneBuilder::addTexture(double dx, @@ -224,12 +212,9 @@ void SceneBuilder::addTexture(double dx, double height, int64_t textureId, bool freeze) { - if (!current_layer_) { - return; - } auto layer = std::make_unique( SkPoint::Make(dx, dy), SkSize::Make(width, height), textureId, freeze); - current_layer_->Add(std::move(layer)); + AddLayer(std::move(layer)); } void SceneBuilder::addPlatformView(double dx, @@ -237,12 +222,9 @@ void SceneBuilder::addPlatformView(double dx, double width, double height, int64_t viewId) { - if (!current_layer_) { - return; - } auto layer = std::make_unique( SkPoint::Make(dx, dy), SkSize::Make(width, height), viewId); - current_layer_->Add(std::move(layer)); + AddLayer(std::move(layer)); } #if defined(OS_FUCHSIA) @@ -252,13 +234,10 @@ void SceneBuilder::addChildScene(double dx, double height, SceneHost* sceneHost, bool hitTestable) { - if (!current_layer_) { - return; - } auto layer = std::make_unique( sceneHost->id(), SkPoint::Make(dx, dy), SkSize::Make(width, height), hitTestable); - current_layer_->Add(std::move(layer)); + AddLayer(std::move(layer)); } #endif // defined(OS_FUCHSIA) @@ -267,14 +246,11 @@ void SceneBuilder::addPerformanceOverlay(uint64_t enabledOptions, double right, double top, double bottom) { - if (!current_layer_) { - return; - } SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); auto layer = std::make_unique(enabledOptions); layer->set_paint_bounds(rect); - current_layer_->Add(std::move(layer)); + AddLayer(std::move(layer)); } void SceneBuilder::setRasterizerTracingThreshold(uint32_t frameInterval) { @@ -290,29 +266,33 @@ void SceneBuilder::setCheckerboardOffscreenLayers(bool checkerboard) { } fml::RefPtr SceneBuilder::build() { + FML_DCHECK(layer_stack_.size() >= 1); + fml::RefPtr scene = Scene::create( - std::move(root_layer_), rasterizer_tracing_threshold_, + layer_stack_[0], rasterizer_tracing_threshold_, checkerboard_raster_cache_images_, checkerboard_offscreen_layers_); - ClearDartWrapper(); + ClearDartWrapper(); // may delete this object. return scene; } -void SceneBuilder::PushLayer(std::shared_ptr layer) { +void SceneBuilder::AddLayer(std::shared_ptr layer) { FML_DCHECK(layer); - if (!root_layer_) { - root_layer_ = std::move(layer); - current_layer_ = root_layer_.get(); - return; + if (!layer_stack_.empty()) { + layer_stack_.back()->Add(std::move(layer)); } +} - if (!current_layer_) { - return; - } +void SceneBuilder::PushLayer(std::shared_ptr layer) { + AddLayer(layer); + layer_stack_.push_back(std::move(layer)); +} - flutter::ContainerLayer* newLayer = layer.get(); - current_layer_->Add(std::move(layer)); - current_layer_ = newLayer; +void SceneBuilder::PopLayer() { + // We never pop the root layer, so that AddLayer operations are always valid. + if (layer_stack_.size() > 1) { + layer_stack_.pop_back(); + } } } // namespace flutter diff --git a/lib/ui/compositing/scene_builder.h b/lib/ui/compositing/scene_builder.h index d378876d960a2..a634087174e2f 100644 --- a/lib/ui/compositing/scene_builder.h +++ b/lib/ui/compositing/scene_builder.h @@ -8,8 +8,9 @@ #include #include -#include +#include +#include "flutter/flow/layers/container_layer.h" #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/color_filter.h" @@ -35,7 +36,6 @@ class SceneBuilder : public RefCountedDartWrappable { static fml::RefPtr create() { return fml::MakeRefCounted(); } - ~SceneBuilder() override; fml::RefPtr pushTransform(tonic::Float64List& matrix4); @@ -98,7 +98,6 @@ class SceneBuilder : public RefCountedDartWrappable { #endif void setRasterizerTracingThreshold(uint32_t frameInterval); - void setCheckerboardRasterCacheImages(bool checkerboard); void setCheckerboardOffscreenLayers(bool checkerboard); @@ -109,15 +108,15 @@ class SceneBuilder : public RefCountedDartWrappable { private: SceneBuilder(); - std::shared_ptr root_layer_; - flutter::ContainerLayer* current_layer_ = nullptr; + void AddLayer(std::shared_ptr layer); + void PushLayer(std::shared_ptr layer); + void PopLayer(); + std::vector> layer_stack_; int rasterizer_tracing_threshold_ = 0; bool checkerboard_raster_cache_images_ = false; bool checkerboard_offscreen_layers_ = false; - void PushLayer(std::shared_ptr layer); - FML_DISALLOW_COPY_AND_ASSIGN(SceneBuilder); }; diff --git a/lib/ui/compositing/scene_host.cc b/lib/ui/compositing/scene_host.cc index 889f5cb174351..e4f83cc3ff0e8 100644 --- a/lib/ui/compositing/scene_host.cc +++ b/lib/ui/compositing/scene_host.cc @@ -85,10 +85,9 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, SceneHost); -#define FOR_EACH_BINDING(V) \ - V(SceneHost, dispose) \ - V(SceneHost, setProperties) \ - V(SceneHost, setOpacity) +#define FOR_EACH_BINDING(V) \ + V(SceneHost, dispose) \ + V(SceneHost, setProperties) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) @@ -205,13 +204,4 @@ void SceneHost::setProperties(double width, }); } -void SceneHost::setOpacity(double opacity) { - gpu_task_runner_->PostTask([id = koid_, opacity]() { - auto* view_holder = flutter::ViewHolder::FromId(id); - FML_DCHECK(view_holder); - - view_holder->SetOpacity(opacity); - }); -} - } // namespace flutter diff --git a/lib/ui/compositing/scene_host.h b/lib/ui/compositing/scene_host.h index 9e47cf49a3645..05c36e3c4f0cf 100644 --- a/lib/ui/compositing/scene_host.h +++ b/lib/ui/compositing/scene_host.h @@ -33,14 +33,11 @@ class SceneHost : public RefCountedDartWrappable { static void OnViewDisconnected(scenic::ResourceId id); static void OnViewStateChanged(scenic::ResourceId id, bool state); - SceneHost(fml::RefPtr viewHolderToken, - Dart_Handle viewConnectedCallback, - Dart_Handle viewDisconnectedCallback, - Dart_Handle viewStateChangedCallback); ~SceneHost() override; zx_koid_t id() const { return koid_; } + // These are visible to Dart. void dispose(); void setProperties(double width, double height, @@ -49,9 +46,13 @@ class SceneHost : public RefCountedDartWrappable { double insetBottom, double insetLeft, bool focusable); - void setOpacity(double opacity); private: + SceneHost(fml::RefPtr viewHolderToken, + Dart_Handle viewConnectedCallback, + Dart_Handle viewDisconnectedCallback, + Dart_Handle viewStateChangedCallback); + fml::RefPtr gpu_task_runner_; tonic::DartPersistentValue view_connected_callback_; tonic::DartPersistentValue view_disconnected_callback_; diff --git a/lib/ui/painting/engine_layer.cc b/lib/ui/painting/engine_layer.cc index 7ac9c98497f41..0868b42cc2928 100644 --- a/lib/ui/painting/engine_layer.cc +++ b/lib/ui/painting/engine_layer.cc @@ -4,8 +4,6 @@ #include "flutter/lib/ui/painting/engine_layer.h" -#include "flutter/flow/layers/container_layer.h" - #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" diff --git a/lib/ui/painting/engine_layer.h b/lib/ui/painting/engine_layer.h index 1dc565c5193c2..a679ef2fe50f3 100644 --- a/lib/ui/painting/engine_layer.h +++ b/lib/ui/painting/engine_layer.h @@ -7,7 +7,7 @@ #include "flutter/lib/ui/dart_wrapper.h" -#include "flutter/flow/layers/layer.h" +#include "flutter/flow/layers/container_layer.h" namespace tonic { class DartLibraryNatives; diff --git a/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc index f18972992189a..0b6dab6d4c1e0 100644 --- a/lib/ui/window/viewport_metrics.cc +++ b/lib/ui/window/viewport_metrics.cc @@ -4,8 +4,9 @@ #include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/fml/logging.h" + namespace flutter { -ViewportMetrics::ViewportMetrics() = default; ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, @@ -39,6 +40,10 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, physical_system_gesture_inset_bottom( p_physical_system_gesture_inset_bottom), physical_system_gesture_inset_left(p_physical_system_gesture_inset_left) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); + FML_DCHECK(device_pixel_ratio > 0); } ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, @@ -68,8 +73,11 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, physical_view_inset_bottom(p_physical_view_inset_bottom), physical_view_inset_left(p_physical_view_inset_left), physical_view_inset_front(p_physical_view_inset_front), - physical_view_inset_back(p_physical_view_inset_back) {} - -ViewportMetrics::ViewportMetrics(const ViewportMetrics& other) = default; + physical_view_inset_back(p_physical_view_inset_back) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); + FML_DCHECK(device_pixel_ratio > 0); +} } // namespace flutter diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index 5ca8b78eaa064..f60adbfcee110 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -16,7 +16,8 @@ namespace flutter { static const double kUnsetDepth = 1.7976931348623157e+308; struct ViewportMetrics { - ViewportMetrics(); + ViewportMetrics() = default; + ViewportMetrics(const ViewportMetrics& other) = default; // Create a 2D ViewportMetrics instance. ViewportMetrics(double p_device_pixel_ratio, @@ -51,8 +52,6 @@ struct ViewportMetrics { double p_physical_view_inset_bottom, double p_physical_view_inset_left); - ViewportMetrics(const ViewportMetrics& other); - double device_pixel_ratio = 1.0; double physical_width = 0; double physical_height = 0; diff --git a/lib/web_ui/lib/src/ui/compositing.dart b/lib/web_ui/lib/src/ui/compositing.dart index d33ccc6c216a9..96d83d52fd906 100644 --- a/lib/web_ui/lib/src/ui/compositing.dart +++ b/lib/web_ui/lib/src/ui/compositing.dart @@ -383,12 +383,6 @@ class SceneHost { void Function() viewDisconnectedCallback, void Function(bool) viewStateChangedCallback); - SceneHost.fromViewHolderToken( - dynamic viewHolderToken, - void Function() viewConnectedCallback, - void Function() viewDisconnectedCallback, - void Function(bool) viewStateChangedCallback); - /// Releases the resources associated with the SceneHost. /// /// After calling this function, the SceneHost cannot be used further. @@ -400,10 +394,4 @@ class SceneHost { double insetRight, double insetBottom, double insetLeft, bool focusable) { throw UnimplementedError(); } - - /// Set the opacity of the linked scene. This opacity value is applied only - /// once, when the child scene is composited into our own. - void setOpacity(double opacity) { - throw UnimplementedError(); - } } diff --git a/shell/common/engine.cc b/shell/common/engine.cc index c0d64b9bfc42d..543ed34767c5c 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -437,13 +437,12 @@ void Engine::Render(std::unique_ptr layer_tree) { if (!layer_tree) return; - SkISize frame_size = SkISize::Make(viewport_metrics_.physical_width, - viewport_metrics_.physical_height); - if (frame_size.isEmpty()) + // Ensure frame dimensions are sane. + if (layer_tree->frame_size().isEmpty() || + layer_tree->frame_physical_depth() <= 0.0f || + layer_tree->frame_device_pixel_ratio() <= 0.0f) return; - layer_tree->set_frame_size(frame_size); - layer_tree->set_device_pixel_ratio(viewport_metrics_.device_pixel_ratio); animator_->Render(std::move(layer_tree)); } diff --git a/shell/common/persistent_cache_unittests.cc b/shell/common/persistent_cache_unittests.cc index 62b36e786875b..4948a3359bc69 100644 --- a/shell/common/persistent_cache_unittests.cc +++ b/shell/common/persistent_cache_unittests.cc @@ -56,7 +56,7 @@ TEST_F(ShellTest, CacheSkSLWorks) { SkPath path; path.addCircle(50, 50, 20); auto physical_shape_layer = std::make_shared( - SK_ColorRED, SK_ColorBLUE, 1.0f, 1.0f, 1.0f, path, Clip::antiAlias); + SK_ColorRED, SK_ColorBLUE, 1.0f, path, Clip::antiAlias); root->Add(physical_shape_layer); }; PumpOneFrame(shell.get(), 100, 100, builder); diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 97a382dcacbbd..3a0d3e150a207 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -143,14 +143,22 @@ void ShellTest::PumpOneFrame(Shell* shell, double width, double height, LayerTreeBuilder builder) { + PumpOneFrame(shell, + flutter::ViewportMetrics{1, width, height, flutter::kUnsetDepth, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + std::move(builder)); +} + +void ShellTest::PumpOneFrame(Shell* shell, + flutter::ViewportMetrics viewport_metrics, + LayerTreeBuilder builder) { // Set viewport to nonempty, and call Animator::BeginFrame to make the layer // tree pipeline nonempty. Without either of this, the layer tree below // won't be rasterized. fml::AutoResetWaitableEvent latch; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, engine = shell->weak_engine_, width, height]() { - engine->SetViewportMetrics( - {1, width, height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + [&latch, engine = shell->weak_engine_, viewport_metrics]() { + engine->SetViewportMetrics(std::move(viewport_metrics)); engine->animator_->BeginFrame(fml::TimePoint::Now(), fml::TimePoint::Now()); latch.Signal(); @@ -161,8 +169,12 @@ void ShellTest::PumpOneFrame(Shell* shell, // Call |Render| to rasterize a layer tree and trigger |OnFrameRasterized| fml::WeakPtr runtime_delegate = shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, runtime_delegate, &builder]() { - auto layer_tree = std::make_unique(); + [&latch, runtime_delegate, &builder, viewport_metrics]() { + auto layer_tree = std::make_unique( + SkISize::Make(viewport_metrics.physical_width, + viewport_metrics.physical_height), + static_cast(viewport_metrics.physical_depth), + static_cast(viewport_metrics.device_pixel_ratio)); SkMatrix identity; identity.setIdentity(); auto root_layer = std::make_shared(identity); diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 0ae092a1c91bf..ebf9283378707 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -58,7 +58,9 @@ class ShellTest : public ThreadTest { double width = 1, double height = 1, LayerTreeBuilder = {}); - + static void PumpOneFrame(Shell* shell, + flutter::ViewportMetrics viewport_metrics, + LayerTreeBuilder = {}); static void DispatchFakePointerData(Shell* shell); static void DispatchPointerData(Shell* shell, std::unique_ptr packet); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 636b879178d88..3074e71aaf4df 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -602,6 +602,27 @@ TEST_F(ShellTest, WaitForFirstFrame) { fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); ASSERT_TRUE(result.ok()); + + DestroyShell(std::move(shell)); +} + +TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get(), {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + fml::Status result = + shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + ASSERT_FALSE(result.ok()); + ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); + DestroyShell(std::move(shell)); } @@ -618,7 +639,9 @@ TEST_F(ShellTest, WaitForFirstFrameTimeout) { RunEngine(shell.get(), std::move(configuration)); fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); + ASSERT_FALSE(result.ok()); ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); + DestroyShell(std::move(shell)); } @@ -641,6 +664,7 @@ TEST_F(ShellTest, WaitForFirstFrameMultiple) { result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); ASSERT_TRUE(result.ok()); } + DestroyShell(std::move(shell)); } @@ -666,10 +690,12 @@ TEST_F(ShellTest, WaitForFirstFrameInlined) { task_runner->PostTask([&shell, &event] { fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + ASSERT_FALSE(result.ok()); ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); event.Signal(); }); ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); + DestroyShell(std::move(shell), std::move(task_runners)); } diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index c8560a6d11e7a..3631dee2563ab 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1604,7 +1604,6 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); From ee4c2a53c7c6b0411f8f174b8992a4c5a549d01c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 4 Dec 2019 22:56:47 -0500 Subject: [PATCH 328/591] Roll src/third_party/skia 6344c2937997..0af32fdf5fea (12 commits) (#14139) https://skia.googlesource.com/skia.git/+log/6344c2937997..0af32fdf5fea git log 6344c2937997..0af32fdf5fea --date=short --first-parent --format='%ad %ae %s' 2019-12-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-04 michaelludwig@google.com Reduce GrTextureOp size 2019-12-04 michaelludwig@google.com Specialize inset/outset/reset based on known quad type 2019-12-04 mtklein@google.com flip on a bunch of GCC warnings 2019-12-04 herb@google.com Specialize vertex size for GrTextBlob 2019-12-04 mtklein@google.com work around GCC 8 mips release bug? 2019-12-04 herb@google.com Remove legacy CPU glyph drawing code 2019-12-04 herb@google.com Make fInitialViewMatrix const in GrTextBlob 2019-12-04 halcanary@google.com experimental/skottie_ios: fix some objc errors 2019-12-04 robertphillips@google.com Add helper functions to AAHairlineOp Created with: gclient setdep -r src/third_party/skia@0af32fdf5fea If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a10c5cd33e11a..c0266795fe41f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6344c2937997f12221a5c233803a3f2d19f2111f', + 'skia_revision': '0af32fdf5feaf1a5a46fb793b5d25849bd34a57b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 9ffd257809279..344040fa3d242 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c40417f6dd64a7cc0b09b7dfc92ebfe1 +Signature: 742071d5c0f38f8dd2ddb569911ed597 UNUSED LICENSES: From 253851e677892c6de144aab951d4f95a1941ffab Mon Sep 17 00:00:00 2001 From: George Wright Date: Thu, 5 Dec 2019 02:35:42 -0500 Subject: [PATCH 329/591] Move Fuchsia unit test runners into engine repo (#14092) - Copies fuchsia_test.sh into the testing/fuchsia directory - Fixes a small issue with fuchsia_archive to ensure the cmx file is correctly named according to the target - Add a list of fuchsia unittest fars to run on CI - Add a GN build target to build all currently-enabled unittests for Fuchsia --- BUILD.gn | 26 +++++++++++++------- testing/fuchsia/run_tests.sh | 41 +++++++++++++++++++++++++++++++ testing/fuchsia/test_fars | 1 + tools/fuchsia/fuchsia_archive.gni | 2 +- 4 files changed, 60 insertions(+), 10 deletions(-) create mode 100755 testing/fuchsia/run_tests.sh create mode 100644 testing/fuchsia/test_fars diff --git a/BUILD.gn b/BUILD.gn index 6711b06124153..0c299cf4d1377 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -92,15 +92,6 @@ group("flutter") { ] } } - - # Fuchsia currently only supports a subset of our unit tests - if (is_fuchsia) { - public_deps += [ - "$flutter_root/flow:flow_tests", - # TODO(gw280): Re-enable fml_tests on Fuchsia (https://github.com/flutter/flutter/issues/46122) - # "$flutter_root/fml:fml_tests", - ] - } } config("config") { @@ -128,3 +119,20 @@ group("dist") { "$flutter_root/sky/dist", ] } + +# Fuchsia currently only supports a subset of our unit tests +# When adding a new dep here, please also ensure the dep is added to +# testing/fuchsia/run_tests.sh and testing/fuchsia/test_fars +if (is_fuchsia) { + group("fuchsia_tests") { + testonly = true + + deps = [ + "$flutter_root/flow:flow_tests", + + # TODO(gw280): Re-enable fml_tests on Fuchsia (https://github.com/flutter/flutter/issues/46122) + # "$flutter_root/fml:fml_tests", + "$flutter_root/shell/platform/fuchsia/flutter:flutter_runner_tests", + ] + } +} diff --git a/testing/fuchsia/run_tests.sh b/testing/fuchsia/run_tests.sh new file mode 100755 index 0000000000000..186a00907adf5 --- /dev/null +++ b/testing/fuchsia/run_tests.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# This expects the device to be in zedboot mode, with a zedboot that is +# is compatible with the Fuchsia system image provided. +# +# The first and only parameter should be the path to the Fuchsia system image +# tarball, e.g. `./fuchsia-test.sh generic-x64.tgz`. +# +# This script expects `pm`, `dev_finder`, and `fuchsia_ctl` to all be in the +# same directory as the script, as well as the `flutter_aot_runner-0.far` and +# the `flutter_runner_tests-0.far`. It is written to be run from its own +# directory, and will fail if run from other directories or via sym-links. + +set -Ee + +# The nodes are named blah-blah--four-word-fuchsia-id +device_name=${SWARMING_BOT_ID#*--} + +if [ -z "$device_name" ] +then + echo "No device found. Aborting." + exit 1 +else + echo "Connecting to device $device_name" +fi + +reboot() { + # note: this will set an exit code of 255, which we can ignore. + ./fuchsia_ctl -d $device_name ssh -c "dm reboot-recovery" || true +} + +trap reboot EXIT + +./fuchsia_ctl -d $device_name pave -i $1 + +# TODO(gw280): Enable tests using JIT runner +./fuchsia_ctl -d $device_name test \ + -f flutter_aot_runner-0.far \ + -f flutter_runner_tests-0.far \ + -t flutter_runner_tests + diff --git a/testing/fuchsia/test_fars b/testing/fuchsia/test_fars new file mode 100644 index 0000000000000..b163073a8370a --- /dev/null +++ b/testing/fuchsia/test_fars @@ -0,0 +1 @@ +flutter_runner_tests-0.far diff --git a/tools/fuchsia/fuchsia_archive.gni b/tools/fuchsia/fuchsia_archive.gni index 3b1da4397894c..6c5b024953dc2 100644 --- a/tools/fuchsia/fuchsia_archive.gni +++ b/tools/fuchsia/fuchsia_archive.gni @@ -68,7 +68,7 @@ template("fuchsia_archive") { "$cmx_file", ] outputs = [ - "$far_base_dir/meta/{{source_file_part}}", + "$far_base_dir/meta/${pkg_target_name}.cmx", ] } From 8f095cd1f3d35b95be22f8ed3093cba49d4e7f1d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 5 Dec 2019 03:36:09 -0500 Subject: [PATCH 330/591] Roll src/third_party/skia 0af32fdf5fea..f5542b6aa307 (4 commits) (#14141) https://skia.googlesource.com/skia.git/+log/0af32fdf5fea..f5542b6aa307 git log 0af32fdf5fea..f5542b6aa307 --date=short --first-parent --format='%ad %ae %s' 2019-12-05 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader bbd0694f9ab2..dee5b5f3cf3f (4 commits) 2019-12-05 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src d02b0cb4d389..a25cc4cdcd47 (492 commits) 2019-12-05 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 249cb200173f..5f857839b9ec (14 commits) 2019-12-05 michaelludwig@google.com Revert "Specialize inset/outset/reset based on known quad type" Created with: gclient setdep -r src/third_party/skia@f5542b6aa307 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c0266795fe41f..942b0f3c097b6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '0af32fdf5feaf1a5a46fb793b5d25849bd34a57b', + 'skia_revision': 'f5542b6aa3078a9f44b5666eff7a207d872d9316', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 344040fa3d242..dfa8bebdbd8a9 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 742071d5c0f38f8dd2ddb569911ed597 +Signature: 919c13061ac98fe77cd0d6e469e4c142 UNUSED LICENSES: From 90c42f4ca6fa43605be305581b9eade065906a5d Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 5 Dec 2019 10:48:55 -0800 Subject: [PATCH 331/591] Fix firefox detection to fix app startup for other browsers (#14145) --- lib/web_ui/lib/src/engine/browser_detection.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index 5d0f55d4a2db3..054b16e6be83b 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -19,6 +19,9 @@ enum BrowserEngine { /// The engine that powers Edge. edge, + /// The engine that powers Internet Explorer 11. + ie11, + /// We were unable to detect the current browser engine. unknown, } @@ -33,14 +36,16 @@ BrowserEngine get browserEngine => _browserEngine ??= _detectBrowserEngine(); BrowserEngine _detectBrowserEngine() { final String vendor = html.window.navigator.vendor; - final String agent = html.window.navigator.userAgent; + final String agent = html.window.navigator.userAgent.toLowerCase(); if (vendor == 'Google Inc.') { return BrowserEngine.blink; } else if (vendor == 'Apple Computer, Inc.') { return BrowserEngine.webkit; - } else if (agent.contains('Edge/')) { + } else if (agent.contains('edge/')) { return BrowserEngine.edge; - } else if (vendor == '') { + } else if (agent.contains('trident/7.0')) { + return BrowserEngine.ie11; + } else if (vendor == '' && agent.contains('firefox')) { // An empty string means firefox: // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor return BrowserEngine.firefox; @@ -48,7 +53,6 @@ BrowserEngine _detectBrowserEngine() { // Assume blink otherwise, but issue a warning. print('WARNING: failed to detect current browser engine.'); - return BrowserEngine.unknown; } From 73da385c11a4ca581ba53d999e16610f4794bea5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 5 Dec 2019 11:16:38 -0800 Subject: [PATCH 332/591] Disable a11y on detach (#14142) --- .../io/flutter/embedding/android/FlutterView.java | 1 + .../flutter/embedding/android/FlutterViewTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index f26109d7dcf93..1a0e22d3683d3 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -701,6 +701,7 @@ public void detachFromFlutterEngine() { isFlutterUiDisplayed = false; flutterRenderer.removeIsDisplayingFlutterUiListener(flutterUiDisplayListener); flutterRenderer.stopRenderingToSurface(); + flutterRenderer.setSemanticsEnabled(false); renderSurface.detachFromRenderer(); flutterEngine = null; } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index 6d10e8e9de6f3..c0c46acddbf38 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -78,6 +78,19 @@ public void detachFromFlutterEngine_alertsPlatformViews() { verify(platformViewsController, times(1)).detachFromView(); } + @Test + public void detachFromFlutterEngine_turnsOffA11y() { + FlutterView flutterView = new FlutterView(RuntimeEnvironment.application); + FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni)); + FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni)); + when(flutterEngine.getRenderer()).thenReturn(flutterRenderer); + + flutterView.attachToFlutterEngine(flutterEngine); + flutterView.detachFromFlutterEngine(); + + verify(flutterRenderer, times(1)).setSemanticsEnabled(false); + } + @Test public void onConfigurationChanged_fizzlesWhenNullEngine() { FlutterView flutterView = new FlutterView(RuntimeEnvironment.application); From d323f84afe97ff9f80310a876ece14d1f66ff86c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 5 Dec 2019 14:28:42 -0500 Subject: [PATCH 333/591] Roll src/third_party/skia f5542b6aa307..0af13b3caf03 (1 commits) (#14143) https://skia.googlesource.com/skia.git/+log/f5542b6aa307..0af13b3caf03 git log f5542b6aa307..0af13b3caf03 --date=short --first-parent --format='%ad %ae %s' 2019-12-05 kjlubick@google.com [canvaskit] update shaping example to have emoji Created with: gclient setdep -r src/third_party/skia@0af13b3caf03 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 942b0f3c097b6..5a4e6119b8199 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f5542b6aa3078a9f44b5666eff7a207d872d9316', + 'skia_revision': '0af13b3caf0384981f50e073ebc7bd0f3f751711', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index dfa8bebdbd8a9..8608b77df677c 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 919c13061ac98fe77cd0d6e469e4c142 +Signature: 1d620f9e9f640326a25368428f153cc1 UNUSED LICENSES: From 133556faa0a8de58c0e4e150278a2be331a89892 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 5 Dec 2019 14:31:19 -0500 Subject: [PATCH 334/591] Roll fuchsia/sdk/core/mac-amd64 from XCAOU... to qQlb5... (#14144) Roll fuchsia/sdk/core/mac-amd64 from XCAOU... to qQlb5... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 5a4e6119b8199..ed68657272af9 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'XCAOUqFKaG7LugHzLoN1fxiJVhguZDnPrdYo7HCwsoMC' + 'version': 'qQlb5qhKaBOvJuoCe6UIzRcz4OR8N6CWAFvw8WHZ4QwC' } ], 'condition': 'host_os == "mac"', From 0506c65f5915f9a5ce89cb53ce8b071a23f7c31d Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 5 Dec 2019 12:15:03 -0800 Subject: [PATCH 335/591] [web] DOM text measurement return a LineMetrics list if single-line (#14138) --- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 15 +- .../lib/src/engine/text/measurement.dart | 35 +- lib/web_ui/lib/src/engine/text/paragraph.dart | 16 +- lib/web_ui/test/text/measurement_test.dart | 299 ++++++++++++++---- 4 files changed, 280 insertions(+), 85 deletions(-) diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index 35d59eed67767..84cfdb45faf9b 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -661,10 +661,14 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { } void _drawTextLine( - ParagraphGeometricStyle style, String line, double x, double y) { + ParagraphGeometricStyle style, + EngineLineMetrics line, + double x, + double y, + ) { final double letterSpacing = style.letterSpacing; if (letterSpacing == null || letterSpacing == 0.0) { - ctx.fillText(line, x, y); + ctx.fillText(line.text, x, y); } else { // When letter-spacing is set, we go through a more expensive code path // that renders each character separately with the correct spacing @@ -676,9 +680,9 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { // would put 5px before each letter and 5px after it, but on the web, we // put no spacing before the letter and 10px after it. This is how the DOM // does it. - final int len = line.length; + final int len = line.text.length; for (int i = 0; i < len; i++) { - final String char = line[i]; + final String char = line.text[i]; ctx.fillText(char, x, y); x += letterSpacing + ctx.measureText(char).width; } @@ -692,8 +696,7 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { final ParagraphGeometricStyle style = paragraph._geometricStyle; if (paragraph._drawOnCanvas && _childOverdraw == false) { - final List lines = - paragraph._lines ?? [paragraph._plainText]; + final List lines = paragraph._measurementResult.lines; final ui.PaintData backgroundPaint = paragraph._background?.webOnlyPaintData; diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index 20f8328e5d126..e22fd5ea8169e 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -159,6 +159,12 @@ class RulerManager { /// in [DomTextMeasurementService], or a canvas-based approach in /// [CanvasTextMeasurementService]. abstract class TextMeasurementService { + /// Whether this service uses a canvas to make the text measurements. + /// + /// If [isCanvas] is false, it indicates that this service uses DOM elements + /// to make the text measurements. + bool get isCanvas; + /// Initializes the text measurement service with a specific /// [rulerCacheCapacity] that gets passed to the [RulerManager]. static void initialize({@required int rulerCacheCapacity}) { @@ -312,6 +318,9 @@ abstract class TextMeasurementService { /// needed for some cases that aren't yet supported in the canvas-based /// implementation such as letter-spacing, word-spacing, etc. class DomTextMeasurementService extends TextMeasurementService { + @override + final bool isCanvas = false; + /// The text measurement service singleton. static DomTextMeasurementService get instance => _instance ??= DomTextMeasurementService(); @@ -388,8 +397,11 @@ class DomTextMeasurementService extends TextMeasurementService { /// value. /// /// This method still needs to measure `minIntrinsicWidth`. - MeasurementResult _measureSingleLineParagraph(ParagraphRuler ruler, - ui.Paragraph paragraph, ui.ParagraphConstraints constraints) { + MeasurementResult _measureSingleLineParagraph( + ParagraphRuler ruler, + EngineParagraph paragraph, + ui.ParagraphConstraints constraints, + ) { final double width = constraints.width; final double minIntrinsicWidth = ruler.minIntrinsicDimensions.width; double maxIntrinsicWidth = ruler.singleLineDimensions.width; @@ -399,6 +411,20 @@ class DomTextMeasurementService extends TextMeasurementService { maxIntrinsicWidth = _applySubPixelRoundingHack(minIntrinsicWidth, maxIntrinsicWidth); final double ideographicBaseline = alphabeticBaseline * _baselineRatioHack; + + List lines; + if (paragraph._plainText != null) { + final double lineWidth = maxIntrinsicWidth; + lines = [ + EngineLineMetrics.withText( + paragraph._plainText, + hardBreak: true, + width: lineWidth, + lineNumber: 0, + ), + ]; + } + return MeasurementResult( constraints.width, isSingleLine: true, @@ -410,7 +436,7 @@ class DomTextMeasurementService extends TextMeasurementService { maxIntrinsicWidth: maxIntrinsicWidth, alphabeticBaseline: alphabeticBaseline, ideographicBaseline: ideographicBaseline, - lines: null, + lines: lines, ); } @@ -492,6 +518,9 @@ class DomTextMeasurementService extends TextMeasurementService { /// This is a faster implementation than [DomTextMeasurementService] and /// provides line breaks information that can be useful for multi-line text. class CanvasTextMeasurementService extends TextMeasurementService { + @override + final bool isCanvas = true; + /// The text measurement service singleton. static CanvasTextMeasurementService get instance => _instance ??= CanvasTextMeasurementService(); diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index e093088a6961a..c4d7ab135e9fb 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -292,19 +292,21 @@ class EngineParagraph implements ui.Paragraph { /// - Paragraphs that have a non-null word-spacing. /// - Paragraphs with a background. bool get _drawOnCanvas { + if (_measurementResult.lines == null) { + return false; + } + bool canDrawTextOnCanvas; - if (TextMeasurementService.enableExperimentalCanvasImplementation) { - canDrawTextOnCanvas = _measurementResult.lines != null; + if (_measurementService.isCanvas) { + canDrawTextOnCanvas = true; } else { - canDrawTextOnCanvas = _measurementResult.isSingleLine && - _plainText != null && - _geometricStyle.ellipsis == null && - _geometricStyle.shadows == null; + canDrawTextOnCanvas = _geometricStyle.ellipsis == null; } return canDrawTextOnCanvas && _geometricStyle.decoration == null && - _geometricStyle.wordSpacing == null; + _geometricStyle.wordSpacing == null && + _geometricStyle.shadows == null; } /// Whether this paragraph has been laid out. diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index 3478124486376..ee7b923c467d2 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -155,11 +155,9 @@ void main() async { expect(result.maxIntrinsicWidth, 60); expect(result.minIntrinsicWidth, 30); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { - expect(result.lines, [ - line(' abc', hardBreak: true, width: 60.0, lineNumber: 0), - ]); - } + expect(result.lines, [ + line(' abc', hardBreak: true, width: 60.0, lineNumber: 0), + ]); // trailing whitespaces text = build(ahemStyle, 'abc '); @@ -167,10 +165,16 @@ void main() async { expect(result.maxIntrinsicWidth, 60); expect(result.minIntrinsicWidth, 30); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), ]); + } else { + // DOM-based measurement always includes trailing whitespace in the + // width, while Flutter and Canvas-based measurement don't. + expect(result.lines, [ + line('abc ', hardBreak: true, width: 60.0, lineNumber: 0), + ]); } // mixed whitespaces @@ -179,10 +183,16 @@ void main() async { expect(result.maxIntrinsicWidth, 100); expect(result.minIntrinsicWidth, 20); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line(' ab c ', hardBreak: true, width: 80.0, lineNumber: 0), ]); + } else { + // DOM-based measurement always includes trailing whitespace in the + // width, while Flutter and Canvas-based measurement don't. + expect(result.lines, [ + line(' ab c ', hardBreak: true, width: 100.0, lineNumber: 0), + ]); } // single whitespace @@ -191,10 +201,16 @@ void main() async { expect(result.maxIntrinsicWidth, 10); expect(result.minIntrinsicWidth, 0); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line(' ', hardBreak: true, width: 0.0, lineNumber: 0), ]); + } else { + // DOM-based measurement always includes trailing whitespace in the + // width, while Flutter and Canvas-based measurement don't. + expect(result.lines, [ + line(' ', hardBreak: true, width: 10.0, lineNumber: 0), + ]); } // whitespace only @@ -203,10 +219,16 @@ void main() async { expect(result.maxIntrinsicWidth, 50); expect(result.minIntrinsicWidth, 0); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line(' ', hardBreak: true, width: 0.0, lineNumber: 0), ]); + } else { + // DOM-based measurement always includes trailing whitespace in the + // width, while Flutter and Canvas-based measurement don't. + expect(result.lines, [ + line(' ', hardBreak: true, width: 50.0, lineNumber: 0), + ]); } }, ); @@ -223,11 +245,9 @@ void main() async { expect(result.minIntrinsicWidth, 50); expect(result.width, 50); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { - expect(result.lines, [ - line('12345', hardBreak: true, width: 50.0, lineNumber: 0), - ]); - } + expect(result.lines, [ + line('12345', hardBreak: true, width: 50.0, lineNumber: 0), + ]); }, ); @@ -238,18 +258,22 @@ void main() async { ui.ParagraphConstraints(width: 70); MeasurementResult result; - // The long text doesn't fit in 50px of width, so it needs to wrap. + // The long text doesn't fit in 70px of width, so it needs to wrap. result = instance.measure(build(ahemStyle, 'foo bar baz'), constraints); expect(result.isSingleLine, false); expect(result.maxIntrinsicWidth, 110); expect(result.minIntrinsicWidth, 30); expect(result.width, 70); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('foo bar ', hardBreak: false, width: 70.0, lineNumber: 0), line('baz', hardBreak: true, width: 30.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }, ); @@ -266,11 +290,15 @@ void main() async { expect(result.minIntrinsicWidth, 100); expect(result.width, 50); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('12345', hardBreak: false, width: 50.0, lineNumber: 0), line('67890', hardBreak: true, width: 50.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // The first word is force-broken twice. @@ -281,12 +309,16 @@ void main() async { expect(result.minIntrinsicWidth, 110); expect(result.width, 50); expect(result.height, 30); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abcde', hardBreak: false, width: 50.0, lineNumber: 0), line('fghij', hardBreak: false, width: 50.0, lineNumber: 1), line('k lm', hardBreak: true, width: 40.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Constraints aren't enough even for a single character. In this case, @@ -299,11 +331,15 @@ void main() async { expect(result.minIntrinsicWidth, 20); expect(result.width, 8); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('A', hardBreak: false, width: 10.0, lineNumber: 0), line('A', hardBreak: true, width: 10.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Extremely narrow constraints with new line in the middle. @@ -312,15 +348,17 @@ void main() async { expect(result.maxIntrinsicWidth, 20); expect(result.minIntrinsicWidth, 20); expect(result.width, 8); - if (instance is CanvasTextMeasurementService) { - // This can only be done correctly by the canvas-based implementation. - expect(result.height, 30); - + expect(result.height, 30); + if (instance.isCanvas) { expect(result.lines, [ line('A', hardBreak: false, width: 10.0, lineNumber: 0), line('A', hardBreak: true, width: 10.0, lineNumber: 1), line('A', hardBreak: true, width: 10.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Extremely narrow constraints with new line in the end. @@ -330,13 +368,17 @@ void main() async { expect(result.minIntrinsicWidth, 30); expect(result.width, 8); expect(result.height, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('A', hardBreak: false, width: 10.0, lineNumber: 0), line('A', hardBreak: false, width: 10.0, lineNumber: 1), line('A', hardBreak: true, width: 10.0, lineNumber: 2), line('', hardBreak: true, width: 0.0, lineNumber: 3), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }, ); @@ -353,11 +395,15 @@ void main() async { expect(result.minIntrinsicWidth, 20); expect(result.width, 50); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('12', hardBreak: true, width: 20.0, lineNumber: 0), line('34', hardBreak: true, width: 20.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }, ); @@ -370,12 +416,16 @@ void main() async { expect(result.maxIntrinsicWidth, 40); expect(result.minIntrinsicWidth, 40); expect(result.height, 30); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('', hardBreak: true, width: 0.0, lineNumber: 0), line('', hardBreak: true, width: 0.0, lineNumber: 1), line('1234', hardBreak: true, width: 40.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Empty lines in the middle. @@ -383,19 +433,23 @@ void main() async { expect(result.maxIntrinsicWidth, 30); expect(result.minIntrinsicWidth, 30); expect(result.height, 30); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('12', hardBreak: true, width: 20.0, lineNumber: 0), line('', hardBreak: true, width: 0.0, lineNumber: 1), line('345', hardBreak: true, width: 30.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Empty lines in the end. result = instance.measure(build(ahemStyle, '1234\n\n'), constraints); expect(result.maxIntrinsicWidth, 40); expect(result.minIntrinsicWidth, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { // This can only be done correctly in the canvas-based implementation. expect(result.height, 30); expect(result.lines, [ @@ -403,6 +457,10 @@ void main() async { line('', hardBreak: true, width: 0.0, lineNumber: 1), line('', hardBreak: true, width: 0.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }); @@ -419,11 +477,15 @@ void main() async { expect(result.width, double.infinity); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('123', hardBreak: true, width: 30.0, lineNumber: 0), line('456 789', hardBreak: true, width: 70.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }, ); @@ -480,54 +542,74 @@ void main() async { // Simple case. result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); expect(result.minIntrinsicWidth, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), line('de ', hardBreak: false, width: 20.0, lineNumber: 1), line('fghi', hardBreak: true, width: 40.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With new lines. result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); expect(result.minIntrinsicWidth, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abcd', hardBreak: true, width: 40.0, lineNumber: 0), line('ef', hardBreak: true, width: 20.0, lineNumber: 1), line('ghi', hardBreak: true, width: 30.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With trailing whitespace. result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); expect(result.minIntrinsicWidth, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), line('efg', hardBreak: true, width: 30.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With trailing whitespace and new lines. result = instance.measure(build(ahemStyle, 'abc \ndefg'), constraints); expect(result.minIntrinsicWidth, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), line('defg', hardBreak: true, width: 40.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Very long text. result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); expect(result.minIntrinsicWidth, 120); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 0), line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), line('AA', hardBreak: true, width: 20.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }); @@ -537,64 +619,88 @@ void main() async { // Simple case. result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); expect(result.maxIntrinsicWidth, 110); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), line('de ', hardBreak: false, width: 20.0, lineNumber: 1), line('fghi', hardBreak: true, width: 40.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With new lines. result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); expect(result.maxIntrinsicWidth, 40); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abcd', hardBreak: true, width: 40.0, lineNumber: 0), line('ef', hardBreak: true, width: 20.0, lineNumber: 1), line('ghi', hardBreak: true, width: 30.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With long whitespace. result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); expect(result.maxIntrinsicWidth, 100); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), line('efg', hardBreak: true, width: 30.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With trailing whitespace. result = instance.measure(build(ahemStyle, 'abc def '), constraints); expect(result.maxIntrinsicWidth, 100); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), line('def ', hardBreak: true, width: 30.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // With trailing whitespace and new lines. result = instance.measure(build(ahemStyle, 'abc \ndef '), constraints); expect(result.maxIntrinsicWidth, 60); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), line('def ', hardBreak: true, width: 30.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Very long text. result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); expect(result.maxIntrinsicWidth, 120); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 0), line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), line('AA', hardBreak: true, width: 20.0, lineNumber: 2), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }); @@ -619,10 +725,14 @@ void main() async { expect(result.minIntrinsicWidth, 480); expect(result.maxIntrinsicWidth, 480); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('AA...', hardBreak: false, width: 50.0, lineNumber: 0), ]); + } else { + // DOM-based measurement can't handle the ellipsis case very well. The + // text wraps into multiple lines instead. + expect(result.lines, isNull); } // The short prefix should make the text break into two lines, but the @@ -635,11 +745,15 @@ void main() async { expect(result.minIntrinsicWidth, 450); expect(result.maxIntrinsicWidth, 450); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('AAA', hardBreak: true, width: 30.0, lineNumber: 0), line('AA...', hardBreak: false, width: 50.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't handle the ellipsis case very well. The + // text wraps into multiple lines instead. + expect(result.lines, isNull); } // Tiny constraints. @@ -650,10 +764,14 @@ void main() async { expect(result.minIntrinsicWidth, 40); expect(result.maxIntrinsicWidth, 40); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('...', hardBreak: false, width: 30.0, lineNumber: 0), ]); + } else { + // DOM-based measurement can't handle the ellipsis case very well. The + // text wraps into multiple lines instead. + expect(result.lines, isNull); } // Tinier constraints (not enough for the ellipsis). @@ -663,12 +781,19 @@ void main() async { expect(result.minIntrinsicWidth, 40); expect(result.maxIntrinsicWidth, 40); expect(result.height, 10); - // TODO(flutter_web): https://github.com/flutter/flutter/issues/34346 - // if (instance is CanvasTextMeasurementService) { - // expect(result.lines, [ - // line('.', hardBreak: false, width: 10.0, lineNumber: 0), - // ]); - // } + if (instance.isCanvas) { + // TODO(flutter_web): https://github.com/flutter/flutter/issues/34346 + // expect(result.lines, [ + // line('.', hardBreak: false, width: 10.0, lineNumber: 0), + // ]); + expect(result.lines, [ + line('...', hardBreak: false, width: 30.0, lineNumber: 0), + ]); + } else { + // DOM-based measurement can't handle the ellipsis case very well. The + // text wraps into multiple lines instead. + expect(result.lines, isNull); + } }, ); @@ -685,22 +810,24 @@ void main() async { final ui.Paragraph oneline = build(maxlinesStyle, 'One line'); result = instance.measure(oneline, infiniteConstraints); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { - expect(result.lines, [ - line('One line', hardBreak: true, width: 80.0, lineNumber: 0), - ]); - } + expect(result.lines, [ + line('One line', hardBreak: true, width: 80.0, lineNumber: 0), + ]); // The height should respect max lines and be limited to two lines here. final ui.Paragraph threelines = build(maxlinesStyle, 'First\nSecond\nThird'); result = instance.measure(threelines, infiniteConstraints); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('First', hardBreak: true, width: 50.0, lineNumber: 0), line('Second', hardBreak: true, width: 60.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // The height should respect max lines and be limited to two lines here. @@ -710,11 +837,15 @@ void main() async { ); result = instance.measure(veryLong, constraints); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('Lorem ', hardBreak: false, width: 50.0, lineNumber: 0), line('ipsum ', hardBreak: false, width: 50.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Case when last line is a long unbreakable word. @@ -724,11 +855,15 @@ void main() async { ); result = instance.measure(veryLongLastLine, constraints); expect(result.height, 20); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('AAA ', hardBreak: false, width: 30.0, lineNumber: 0), line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }); @@ -757,30 +892,36 @@ void main() async { p = build(onelineStyle, 'abcdef'); result = instance.measure(p, constraints); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { - expect(result.lines, [ - line('abcdef', hardBreak: true, width: 60.0, lineNumber: 0), - ]); - } + expect(result.lines, [ + line('abcdef', hardBreak: true, width: 60.0, lineNumber: 0), + ]); // Simple overflow case. p = build(onelineStyle, 'abcd efg'); result = instance.measure(p, constraints); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('abc...', hardBreak: false, width: 60.0, lineNumber: 0), ]); + } else { + // DOM-based measurement can't handle the ellipsis case very well. The + // text wraps into multiple lines instead. + expect(result.lines, isNull); } // Another simple overflow case. p = build(onelineStyle, 'a bcde fgh'); result = instance.measure(p, constraints); expect(result.height, 10); - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.lines, [ line('a b...', hardBreak: false, width: 60.0, lineNumber: 0), ]); + } else { + // DOM-based measurement can't handle the ellipsis case very well. The + // text wraps into multiple lines instead. + expect(result.lines, isNull); } // The ellipsis is supposed to go on the second line, but because the @@ -788,26 +929,34 @@ void main() async { p = build(multilineStyle, 'abcdef ghijkl'); result = instance.measure(p, constraints); // This can only be done correctly in the canvas-based implementation. - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.height, 20); expect(result.lines, [ line('abcdef ', hardBreak: false, width: 60.0, lineNumber: 0), line('ghijkl', hardBreak: true, width: 60.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // But when the 2nd line is long enough, the ellipsis is shown. p = build(multilineStyle, 'abcd efghijkl'); result = instance.measure(p, constraints); // This can only be done correctly in the canvas-based implementation. - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.height, 20); expect(result.lines, [ line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), line('efg...', hardBreak: false, width: 60.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Even if the second line can be broken, we don't break it, we just @@ -815,39 +964,51 @@ void main() async { p = build(multilineStyle, 'abcde f gh ijk'); result = instance.measure(p, constraints); // This can only be done correctly in the canvas-based implementation. - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.height, 20); expect(result.lines, [ line('abcde ', hardBreak: false, width: 50.0, lineNumber: 0), line('f g...', hardBreak: false, width: 60.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // First line overflows but second line doesn't. p = build(multilineStyle, 'abcdefg hijk'); result = instance.measure(p, constraints); // This can only be done correctly in the canvas-based implementation. - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.height, 20); expect(result.lines, [ line('abcdef', hardBreak: false, width: 60.0, lineNumber: 0), line('g hijk', hardBreak: true, width: 60.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } // Both first and second lines overflow. p = build(multilineStyle, 'abcdefg hijklmnop'); result = instance.measure(p, constraints); // This can only be done correctly in the canvas-based implementation. - if (instance is CanvasTextMeasurementService) { + if (instance.isCanvas) { expect(result.height, 20); expect(result.lines, [ line('abcdef', hardBreak: false, width: 60.0, lineNumber: 0), line('g h...', hardBreak: false, width: 60.0, lineNumber: 1), ]); + } else { + // DOM-based measurement can't produce line metrics for multi-line + // paragraphs. + expect(result.lines, isNull); } }, ); From 84497ba495913ec93aba866f468deeb47c11b2b0 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 5 Dec 2019 13:53:07 -0800 Subject: [PATCH 336/591] Started setting the FlutterOverlayView's bounds when it gets added to its superview. (#14018) --- .../framework/Source/FlutterPlatformViews.mm | 12 ++++++---- .../Scenarios.xcodeproj/project.pbxproj | 4 ++++ .../ios/Scenarios/Scenarios/AppDelegate.m | 1 + .../Scenarios/ScenariosUITests/GoldenImage.m | 7 ++---- .../PlatformViewGoldenTestManager.m | 2 ++ .../ScenariosUITests/PlatformViewUITests.m | 21 ++++++++++++++++++ ...atform_view_rotate_iPhone SE_simulator.png | Bin 0 -> 28759 bytes testing/scenario_app/lib/main.dart | 1 + 8 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone SE_simulator.png diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33ca14d9fabea..3371191fa3c9e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -370,10 +370,13 @@ for (int64_t view_id : composition_order_) { EnsureOverlayInitialized(view_id, gl_context, gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); - SkCanvas* canvas = frame->SkiaCanvas(); - canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); - canvas->flush(); - did_submit &= frame->Submit(); + // If frame is null, AcquireFrame already printed out an error message. + if (frame) { + SkCanvas* canvas = frame->SkiaCanvas(); + canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); + canvas->flush(); + did_submit &= frame->Submit(); + } } picture_recorders_.clear(); if (composition_order_ == active_composition_order_) { @@ -398,6 +401,7 @@ } else { [flutter_view addSubview:platform_view_root]; [flutter_view addSubview:overlay]; + overlay.frame = flutter_view.bounds; } active_composition_order_.push_back(view_id); diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index e667c4c88678d..389b44830da24 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */; }; 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */; }; 0A57B3C22323D2D700DD9521 /* AppLifecycleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A57B3C12323D2D700DD9521 /* AppLifecycleTests.m */; }; + 0D14A3FE239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */; }; 0DB781EF22E931BE00E9B371 /* Flutter.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0DB781F122E933E800E9B371 /* Flutter.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0DB781FE22EA2C6D00E9B371 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* Flutter.framework */; }; @@ -102,6 +103,7 @@ 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FlutterEngine+ScenariosTest.m"; sourceTree = ""; }; 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FlutterEngine+ScenariosTest.h"; sourceTree = ""; }; 0A57B3C12323D2D700DD9521 /* AppLifecycleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppLifecycleTests.m; sourceTree = ""; }; + 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_rotate_iPhone SE_simulator.png"; sourceTree = ""; }; 0DB781FC22EA2C0300E9B371 /* FlutterViewControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlutterViewControllerTest.m; sourceTree = ""; }; 244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = golden_platform_view_D21AP.png; sourceTree = ""; }; 246B4E4122E3B5F700073EBF /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = App.framework; sourceTree = ""; }; @@ -220,6 +222,7 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( + 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */, 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */, 244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */, @@ -377,6 +380,7 @@ buildActionMask = 2147483647; files = ( 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */, + 0D14A3FE239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png in Resources */, 6816DBAB2318696600A51400 /* golden_platform_view_transform_iPhone SE_simulator.png in Resources */, 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */, 6816DBAA2318696600A51400 /* golden_platform_view_clippath_iPhone SE_simulator.png in Resources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 278f7916cf7bb..86a601cc19495 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -36,6 +36,7 @@ - (BOOL)application:(UIApplication*)application @"--platform-view-clippath" : @"platform_view_clippath", @"--platform-view-transform" : @"platform_view_transform", @"--platform-view-opacity" : @"platform_view_opacity", + @"--platform-view-rotate" : @"platform_view_rotate", }; __block NSString* goldenTestName = nil; [launchArgsMap diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m index 6dbcd8e73e72a..e1b27c9bb845a 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenImage.m @@ -67,11 +67,8 @@ - (BOOL)compareGoldenToImage:(UIImage*)image { CGContextDrawImage(contextB, CGRectMake(0, 0, widthA, heightA), imageRefB); CGContextRelease(contextB); - if (memcmp(rawA.mutableBytes, rawB.mutableBytes, size)) { - return NO; - } - - return YES; + BOOL isSame = memcmp(rawA.mutableBytes, rawB.mutableBytes, size) == 0; + return isSame; } NS_INLINE NSString* _platformName() { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGoldenTestManager.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGoldenTestManager.m index cb3ac5f924acc..df7015afd9eb5 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGoldenTestManager.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGoldenTestManager.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "PlatformViewGoldenTestManager.h" +#import @interface PlatformViewGoldenTestManager () @@ -31,6 +32,7 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { @"--platform-view-clippath" : @"platform_view_clippath", @"--platform-view-transform" : @"platform_view_transform", @"--platform-view-opacity" : @"platform_view_opacity", + @"--platform-view-rotate" : @"platform_view_rotate", }; }); _identifier = launchArgsMap[launchArg]; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index 72c3c460de96f..1d23e3a064378 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -150,3 +150,24 @@ - (void)testPlatformView { } @end + +@interface PlatformViewRotation : GoldenPlatformViewTests +@end + +@implementation PlatformViewRotation +- (instancetype)initWithInvocation:(NSInvocation*)invocation { + PlatformViewGoldenTestManager* manager = + [[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view-rotate"]; + return [super initWithManager:manager invocation:invocation]; +} + +- (void)tearDown { + XCUIDevice.sharedDevice.orientation = UIDeviceOrientationPortrait; + [super tearDown]; +} + +- (void)testPlatformView { + XCUIDevice.sharedDevice.orientation = UIDeviceOrientationLandscapeLeft; + [self checkGolden]; +} +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone SE_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone SE_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..5281191b085796432d1ea26439aa3eb3cdf59240 GIT binary patch literal 28759 zcmeEuXH-*L+pRQd0!lrCG!;P*P^xqgl_ouaf`EebCM9$TC`Cj-q)G3DVn8}11W-Yg z-a-$(mxLZ^;ck52?;F6sJI4KW$8n59j%>5{de(Z@Q|6p&hiPdjQBg2cT)1$7N=5mh z&V>uauPNrR zY#wTYfX|aJ0e=YOfge2o`8@f5osv1ii{2M5$X`%-r~vXLSVfbLU3+xY;(X=$#T@cz zX7hLd5L~$zbUo-F@5TU;D`r96*SQ{lxN@8G`eW4qt)KuhPWl@hj~*y-C}RoVh(rrK zdatZ2e{4E*xZvMqUt8-pj~Pbz`pyNr`jnTKV-cf?6T;=?><9lGD_$TVzAXRmkALUj zUqAT2+XftO8odr_PG&7*hWNS&4SnDRLNbnk3xu46tb^f`L4Q4}ld={cL;ByTy-wDR z{rikX=|Hj$qDLqH82E<#JNX6hjr%uF_F>@16*;f}`Xa}h3ux-UzwD%Y*Db9@{GZ#C z1(CU#h`$kjaI$I1&fb3Q-`oGcoSgXwfr-KpafgvF*^%(&Zp9L*&0j^Dl1sf4hw~n; znMBaBZ-iHpNZs=T2D6CSt-RT?KqF}OEM=? zjj#FxtB((M{MZ>fNvm|tS)H$_N2R6rZhtFZ!{SX!+WqgB*beWM+M#5R%Dd`c)Xw3T zWNQWvZSri%s8HMww&bt;-4A3Om#=>ud_bo)_glmVf#1h?N0__`C?v+$E~GVdh|zRP zVIeZ@(+NnQhU!5RkJ$Uy5F#p*q;fSV|5xdUvp)N;lHI;^@`%muYqFhe2f*#$aeN#E zb7Zu}*etuFf0r+2VTU5Kd&Bv6-5dN%1nd8d>gGL1_h);UOf9>XG~nsdpgt1VI(B97 zHpUtdyel^VPb8-mLuDmwdRJ}w1di667A`rre|DZ~3M*&u&c(u* z#W7W#w`>#2rRHMQ7@vO?qj6uWOm|F3!hvKIi;rg&{*!tG$q2n7Wd8Fcr_$yh0vEdP zRL(@+NaDr92xOe{(&Kr1ru|8{*dT{bE{u_NBUjDU0bopd0!3Tv!cr-sqJLjHS)i9 z(xv~$wG=o?P~89V1j53nb);;6lvu8;)gPd#;-np)Cp?1d87d-Tt_HF(osFThmHRR* zTB*)IA9=5S&^I;$qnP%0eAdd-{ruweO#bW-9*eoJIzZF?olR2FyYQ|xSn@Z9Ia=oI zAseUT$%rZrBb6u}9afG+ECU%DPL+ zou{evEt3)akH}&z{z&f7V#%u|*>Qj9B#yaSnn!_kv}0}=3qED2{7zA_c#xM~DC_9A z?Su70)U#nS62sb=TVF4v${ofn6&0B#-d(Hr7Wyo8n_uhYM5~%z+!coWZzg(Q4&;oC zeYZQtUcBYjf4;%hQy}lxWVrOI8jZnpW_NAzPIRns4nj8nCnFLE)`=mYgoR6ef+N0L zN(;$YqEVJA8ySe@pLM11ABFG@9rUGmi_KX(q#lL6Uu+r#kE?Y2_TefQQ2-E1Tlnr` zbJucgCBVt*G|zj@z51OiSmMSZ#~(7|c&uWJ1G@V=P=?GVte+D6_Um?L*e71SWH586 zM42)^&8T9mLTg8b>KF`>ymxQ;xGm_vL25K&(6I0O%Vcho=T`sz>Qf`nA01IR%k!{C_kP z3fP0ci>0cW1THb#&h0;9^crN`+3RXB^EmE@;@8HH2CDJ)6RJ>dP5h7deD2SlH?1Y# zWZoZMen^iVmRhOnbu;e0&I3&|%p~DF8?2ThmCJIK2n!Wykew^qUPwlq%T(}$UX(=1 zCU_Vg};rwbRv4_r}t zB^NPLl=;9J@lILCzBWMB)CIQq6!$7danZ_WF%#@x1%z^jKUY>Zb2Y#I^VI?y^8EPs z+y7QtSBN(aaRa0vQdT#``H4TMd#$T11qWbYNvsAM9P2>hZ{MSYoQq2E&z&JXJ_eA+ zft-?dUIzOQv|1`%MNc++FM^!Aeo^L&+!olH*|Oc)h&sj%nU`oKcmy-Rr7^$TEKM;r zGe;!eBu;*Fe3e{udKvpog{}&CTBk7N{g4XoPdN>H9SyUrJky$ulm_)%sRzBHzsIsB zM(_s zo;gN(qe%^H7kaC_Kr^o))o#OX)bE*FkB9%!$_ELtzboIAbyarvsp`tlFd1B(F3l)v zwO&^f`EW)%X=wpm)i2Q}qwQ6gI11S*q6t^UFAZGr&|>X$(4^fJU=mF0>9G7Ane;AK z0I>){Mc=?m&41=YE6k)t8xWxH$AaWnY!BB%s~6wWMPD}o&mlDyM(iM-M%tgAnd`%K zHO!UZBo-qTYG9uz<+Wd*%ECa8YRkzq54WL%+)B>Jdh6Lf{_hvULlR1gt8lq`dlP?t z-jF*O2Jb%s(^F+(%DdKxF4j%+e!N+6&o8z{j)0|jG27YIx?ww)3GC(^HzMdYrh%YM8E$NSXAg4pJzAw)pHfk@K&wo>8 zlo{?+a;tM`B{P`;%oOjKFajPFPzwusve&mXq{z2+WG^}aUV-hDuL)IaP2A(BOf1f+ zT4WP8%9xL|70T==m^fgzbGurm`^5rMDCWx7cP@lotdApz`^o3#RJ~T2Zvcx}g4@8O zJ(54dO_%KduFB7-hT#Dv>MQxdnFPrcK}?_;O;g-Y-T(>%`=@F2>dAX5m7Z zclG-WAv8#1Te!8b5h6MgUtIn@Gmyw`M!V38R_HckM~Xu($GUL3SKmJ5V3Z~&oN73$ z6Uoo5!UykG%Ikzv=hVLDoXgp={_^v21Mjmon-sU++@%!BplPGv$l_ZRm+F!ovW|)E zYd&2w(C({ocAYo!j_ZH-UL57#C4*hxT+B7Q%i?|8yB)#zB>eTzq!NSH$0df(#hLh! zPZIHS?iCi6~Y7#wpXIJyYyp@#-MpkUtemzG9d>20P=XLL`zgdvmOFUd#)(? zIZm0sEp7m2Z#(Mim?$pF3^Q4}VaqR?pg_(K`q(*BmG5H{%0>WCS%#iCx!^w@CA_qrbCnG_vg0pHC(ldrOv0&@9kR;7w6Ps zPYF@l2SU>A`&~q>?2+6(n9Wk8NpR?|;IqMOpVUNt zPLcxszF#Hu?JT5e`j zuxdzZ7Z%dH&mltEOMSW43>h?^Y$Ktxlet5}CmR%Ud(|vt*>>gU*J3Smx#!gT_-LNuv{;dcGx8!g1X3rcaW*}#jgH+c9ZKok7%IKJ>X>a`quM_DKf9*o z^+T6aZnRJ10LhxvNBeHP%e}mV4SZ|gRR^x>9{%Q4Lak}T;F^)lr^qu6?rh~YKM61K}u+nteSSe~_@94ih`(iGQ`C~kif z2eazEk&bzqbi>MHaDNg5dlQrwIE)-Chb3*$t$&ZJtM#g?GQ8TQFDS#DEmh)X z@N`oX_iNZsxbN*n87u|paA|JCHh<{ne+(a<%6sfzg+r+FP952Ul~RE0Q_d5~8H1Ro zO|Pq@>f=V$@t+G9^K}*kagH(`#*FF*zY#3sMbI?s*4RFGM3rWc0{2&|BD_Q0#fn zD>YWQKy>_@uY_RkNfdu~F-;~5CGxN;1fJaKBfgb;^wJxrb-TZ`?T5%_Ir3ownaA{~ z)5M%367A8@hRkhR>;VtNc71Wzbog*~bwzhgXo^gjw?VL*vu%!cNxQ?Z&Q0U2(N zN)_XD!ktH>o}_0aeDhg?-NGVAI}+ZHidttgkPSZHy4q1+&al_LA`wD zJ*mL5e&yAZe3J7vv7OiE^-yXoZNs5ODh*HhWrQ}7M{%PYt34HRuE&8>%=ogFm#GWM z1NWBcZK1aQG_5aE82kAc&9pg2<^I}5vn-w-Bdw7M=KfJA>wrbrtaR}!gmIvjM-IJJr*E~Jyx!MF z#h6Z@M4{mQm{MR6kw%p-?b}h`mwxFYmAAl_e$n>QzXMwNt8(_J{$M<7=TgG~TFxdqszV$CQi$dIdOxEVX2&Udhb+IY zReZa=QnSg#eOSCMX)*8@=FX>}5P=;jRPV8*s^rkFJN8?`xyZd+4>+vx=eCuapoZ;R z%YYrdn~S?OhMpM2Ji#KhE5@4T`MT@M?Vi;D)t%B%yG1KIgEZx1 zdj=sJ6&j-&b-OiBSaoItfz61OI4N(NpBKe0#Th_swz36?BX!*NBb}(>VRpHW5==xy#Tt5mvYCCKVJeQc$UyTp#S-l)#PAUgh# ziwI_n_+X`0TRkz9rYY9$@)nTILKQ_hqX=kis}h4V5@dvf|eE+3B zxZ#3&>*Y+Z46^@X7i3p{*|ixlYAtDgFILKmCP3$EO`A3av?8dGnFm}|W1BBM1or#D zX%63ZEr~(ulG)VM`P&2qR;#kATy=V1D<_kE3~m^buv`B!IvLW9j9#)2#@;>Tp5z_x zj9!0(tn{?I$Gx)z($HX!es*R$OdsWOz<6k1NuBvPtHPwNEhVwrY$GwMhgrQNiqEBK=TRSXvd$L8CMBVyQ3bp&f0|V8D+}wm0e+&n18>v zfqJrg0b9ttWqq)q>XrMqd#EpY7ae+aU2$BrD*8C&m&7S&lsZ4|Gf@n6nd?BE;9fsy z8uFUNIWYp~-0&6OLZCNUTh0!0ti(|WdfKFqjWjeI zPo}XC3;)+B0@}I*%r^ZCrl*o0 z8Mz;v#daKg+X#833S-ikVhxbfYe0pf0krC);+ydZp8Li7n!AFFKSJW36#>(AGNr8q zacl~s01${VMTe4E`G29n8p|1V!OFZ=>EgY2_Bqz4Y^sQpf%)a|2AojWtS1$;OE1(1 zAW=&f`sCJVMJpNvoq~7=5)M~P1-HskOLiYRHHcARnu#uYx76Ci9-qZv3~})!po3Li zBR$RN_7Z=vib>q}>d{WqY+bxtY`vcy9eg`szHz2dX$~Hxx>~!y8Ynel56mLSU-<7} z883D(U}PgJ2mOCuC(TQFnI!s$#y|WA0S90+`MV zxC(*Ze}-J)a6bp!WxP%MN5R_jn?EUi`VgyVkD8ib%_TnAw$87^VzfJAbS*2WeorM4 z*k})F^0z+<7-wdgp#u#%a>u7xJf_&SeddM(kJ54>zzOSMJ2bUn)Ef4m2hzC)$5S22b|KMsD`AAU&z zLmQX)UYSetmpV{>z|(zoelYooruhbF1%}HZBEOnh#?T!J3|`~@lncvT$dSF(=w<5& zQTU;~rmR=Ilm%DCt}r1gPG!gYA4+Q=_`N3Ho-8JHql#-8w?-c=q?;{5M7?Cmngz2jN0b`CJ7x{&5F6 z`eqPkmk-KA^M;1I66``6^-qxtW?PH5`1tYBxGCF1+g~5K9q_5{@IlK+z>s4Y z6aI|@4fhS#h3r}wi9wtFV;V-kr_*8aT-$8n$Q4YKl5W4m$~=gT6ln#!_Cf$>Ha>Z@bB5=p-6?4R><%PKn&fOMKzI9tlT} zI^Y&gvi8n+tkMsTB>^xz#LFm&N$ZhZ_A)6*(jr*%H?HZRYyNSGH**q-j-Ib`t z?0)ZIII6WRFJau)W%q3#DJ*Jq^pUGRgSPct7^_E}X&Vk!w;ArfpD(l`l_C*v6~}#9 zCZjj3q~6=X*3o4EfC0*YbZuK<3jl1W&&fx@wvQ-ezWko5xRs&9wUa`Gb=?MHa*N=h zRgtqzYPJ+5mk51puSUe??*w+(+^Ly<81|UJm@DtiU;Lv@f~jj;Is5fhSJ7qNScJX1CEivDwM9 zm226uq@nzX6@v!i@9{Agq>B~qXf_vT%KK;77JM~=XMn%SqC3GMN(R$<@Xn}K=3I5z zSS_2OquzOc=6VD}kQ=1(;bvy&zPNOaYZ!x8K_iebE%)Y*w8;j&T*CI|59Enh(N~jI zc_&r_c&D#cssV!@IMQGaaADV1en6>w&HZWja^!xuD3zXR0K9oQJT^@<$rpoZ0HC~+ zSg#^8*(Lys_G9bv=ttqwmt zhg|)}MhEZ~v_~tMz@+|Ex|_lqvYy&aHYSMP)vH@IX#!BlUfMUy%)I@-S4DsZbu?+1 zp-ku{TP1q@O#tt})k^J?5u+WGeiT$b?k#`3HuwZ<%bVfiz4CaZi$@s7H7_={Ua~JV z)cdL8F&x;d7BIS}UyH*GEO8J*b&7mYmuqM3i~jrrdWoDakfKb|8@}aPH}AU{IwWq8 zK_$6SCbJ{^9auN=bz*MVgX|UfYK~LHmh7;8{iQQiWkp$_)8f!m4gb3fcjpt-vFY7+}&wP6PXvxXx_7bk|B14h{29b!J)Kz^4; zqywy6RZ!jbV-^_<#Wrb!TGr{y9iOqVpFPbc@ z`$iM{`_L3VLfqf?@Q=~h&ranhi7u5= zD?~JPnZg?H%Xv7@d2xPfn#7g|ka0FkqmDF>_qV-Ve|`{{`skhi+i2=I)nokFf2XL6 z-F-;38TIFcwP^*VBksG>)E}&v9{Z#8OkeAP!%zm0Vp2QQi2G7 zZfH#dQUY~Vse@I@cq2?Y2F44?R!upf!*OvXhcXg~U`B6TtgdAoPmr;s{8^9a)@qna zo;XzN)lLS8dRCWTJLB~xPD9|QinJ>w$4wFo&nFocO{#!|G!+Z5`yJBH2zhbZjNEzZ z2)8sZ{K}<4lY^z{LlP{0w|e0u#un%2Cg2mUN6bQIexjGF_%?oP)_Y)T7ntZ#?oO#L zty~!H@>X8K8agKgUFLW6m9yR+BtE3X3XZ!N`zdV^K&$gSRyE{v%?zGS#r0_*?i+up zsu|BL({q=8xxMH^dkr^Xf*i+=;?mUwk3#ZZSfnwr^bt~AxHfsX)gWj7Gk8gw(}-Hu zvx-kCY^}P4%HALA<1tb_pNe}sahPZ1N~yTqH!Ur-x*j0a;ky|(3`daDx$^b6_YLz7_^39yb@muCMZHNno5#C!VP z`vJ?J&5NJE42IM|yc1Yn}H$^Of6%0g2!6MWDn&4IoVLEO#x^H(KtybS~(WoYlX+;)HN4dnm^(KkVeKTef6hWwctv@ZR4Qs2d> zv+>E0&Br~fOmKWtd{-EvuUB+;Cyei07_|8=Ds6$KZZ)zh=?PE`)&Fhebo6mWhA;D( zNy9#h%LG=)u4O~kmi9>8KzCgO;^M_YIR#nRk;0o+3f~bET_fXle z7qe$Wm|?x2^+;3bp_c{D?l3Z&w8}qaD`~r!qx3q|4OL>$@1DVD3pNJzwEJyvgCd}g zlsn$5+@+6{-n7NidtMcoMB^()9X&#^$+L$egSHd-QI1VXYUW`8v+ZuGrvA+#Hu}(k z|C~un_r8?=EH5_7gDTIh=}2ZsF6F45u*n|p{Etj<)Y?Y@s$FR=I|gRU{rkA*UJJj} zxHCj?jHZYD3wzCi^{e9)D%QOjZ+NN?K5}XXucD&8+(@%spPGeLOWNQ25qbFQdK&1A z8X?2+07hha*j-ribl)ZUu z#{~d5p=uPPFiO75wliuipX>QV&D<`vI?lkq0D#v|WC*Z9anE(vq#Y5j%@imiuWR}P zAQtk&Az~m#juH!DMk=uFUI4Kr=YF2HS`UbDZjLH_SlqnyEH$zNvY0uTOS4e*&h7Zf z>6+|eDN5hR1XpNt^n<~`*9#D{Toqle{}^2iIDJ)%3k~b1DIjbYE9*f^%=niSNYRt- z602WFyjf1fDkoL`)|~9|o{ii6|{j;mky9%829}ZJ|H}5FH9->jZx73r?Hjn?b?4;XC12vmKLwE0vssG+c zdCyZmuC=~zMrUgu@Bv+wPoa?z#ij>$h7PnF{3cE-;3W+`(0oVdCB+UE88|3+Fec|P z@OsX1w@3o&l)gma>cdIW>RX7Dt~c>&UBr)2Ia|$^+!IBO410kGb|lMvJknd@MI+ym z6%T?ocHN8yjM)Kk`Os@vvvUIGqpo7mDE92OO3yG-qtNH3)SdNb~2&dVR|;p z%H#xfEzA9JPS)7$N5(JbAT?~{Fek|L(gUj@FKYQD!3ySm)Y*tBk*pMhu6(wQ2S|49V$tcECnjYlr8GQ?u+gwI>eKr0)>&QvLc;-l@ApOF>vz-U)xvow3l17~-c$ z9}{vmZyC?CVjT78TMA~}uV$qypNMI!U}e_Z~x4r#+I`FoU|EF>L zaseVG*rj}s(LqJ78@ktMd+esuXC6*~bfJRg;82VQ!(KN)@A-v2HpHl}ZccgMWFFh9 zT0nwoo|-kg;%d{~N1ELWJ~>qV_LNU-Tcu6nUs~>KI4*KTB#`Yrwh?VpcxziuYFw&1 zWEDBQmUQ`ZWPylw_gv4ITFI^5T+5H}6qV~&z=wSr}a$|`kyuoXhINafdwOp?#6Pw-KgFCUVV>J?e zS`HyM(NN7byYxKT^O{dK^m#RgsL@iJs6nr52!E1DO~wlBtFh!%|vk3^v|K zcGcbB!BpZcKJVbgTt|UT@83mVHq9iH0sR1{QHF`7r7H{4q7VAyck0vJXRnwgN`s52 z-)BG97P|*j1>^Dq0P_7(m}iXy#QRJk_b9OUErAL8PGqU=t|E996}`l!RZ zY0G_^>nV4Uhs-IZMOy{4dkYZ3t+(s$?LJE~=?2=)CY`Skog?3DZCc;_X~2l95_uQ% zaLH~ZiaR{CEy}dGNgobxmn_CDt-Spj+ExeAB;ie-SAn~H3d#~al4xY>%&io9-}c2G z{;Ul-$E9z)gB5^cdN#-BETHGQoA*+5?k@wT<>HG{0CtXX!j)B)OKfPe!~|v!5%FsY z5219cM)E0yf1N`+=9n>D+{PX!wS6JEB+@_0sv(%nq-C?-%4$pwoA`*!n9b3rK&`e{ zYeM!N1V5>ef||vb^PdW47y_b}ih6fu&f)3cjKVEIhWeQawp1koyKJrbx;JNKOSKqb zYy}4dHGxV_?=8JOf`c*LC-NW3x1%~ZmfT84V{PWc6fyoABW}4iL#@R@oF+K^PhTeu z4}bWGdlkIyVRbHTww+k0%jD()5yB6;tb+(TcvYloU@Vw+GEg$ z&ojqKM;VYWb^R>us_peu_AV`(#$YRK65y)58$Z~`b$oTIxC;-Hk`p>7aspIK)7gwx zZHp593w`$&#~FR-B6szaErmS^;es95}ttb&vfin?|&F6N55%<-n&jFcAgI8;CI z7Jmk48;wl@sgPrCeIrN4l9g{>$lp+|N=qtx`Z{CUDRzEyI7>w1sTOE6-uQ{;gq2fG zm;YQvosy+Z65k^A{tlakRk;1?s{X8u+7e>W2Jk=%{k99xn!OyW!j{hcWfmyHFwzlU?vGQnfZo zOi&O6&}y3qQJ=-SZxki6hQUsOB(^cFBKa04R$jk+;AlB=5yWg*z!ETAU=1_`CGA4! z$5it}u44d@HfF>{J07aHZfyuNoQf>En$xUH72cq|8z~#%jY4{;E$s9V`c*0|+8)XH zl)3N81{>}F9Px;$NK^UPZyGn~F+?)%)rK3E%6`v8H0IPGu<~w*J1V#6TYTiX28Mo{ zO@yj_M=S0h(Pd4N#GQC1t%4Y6GeaGuh1Db2j3I(;QE-3x_Q+=3K-LvUB&pArX-rAS z)lUQFkTqu_u6ssh@=tzJoJ%!0&mSzKL^U+>lel zhXxMMohb`ava|RD#Xc2x%In0nP%ns^CU%hozxaVWW?7yDJ>e$0?AT*S zQY8x{xYh1ZOiY21Uc$?`-STiskcWkBUull?oZ@o$`B@x(70*yy-BtE3on*650Q?c^4({3srRRqrMQ zu*=o&R^-1@h5DHVGLEiC7BxS3ZgavQ;EHj*EKK02+}*N`vLEMADH1<2Y##m25~a`z z3_}dsKR{Mj)yU{8;07(rZ6btIyqCG)qYF?uq)8OHJ}HRiJ<9s6<*?0mBlvNgwS~Os z3weicAwUs3u3vM1?U8zw<|>kb_QFuWa=58)?VoP%!HXaYmgV^<7A+g6br^E>pyY66RR_#qq%S)a68 zQ;e~nqwGC#w6K0-;<3~uTIQ0k&=*dti7bS?GNMDNcy8H^`s()~7w5(h`-GmF!Brmw z9OTFZ9}cQp7gu0z_Uh7+QWeVZJku<3i7QRd(06kiU3+HtlDyj36l`#hAB(A4;Hs-d z7`ogQn1Sm{j)D1j9$nRL3ZCuwguEj(fzoPJC_roTe{amD4 zmhgE;HzvgrJd|4NAL4}ooS_J-M)j8=f$VrQQ=c_x<>x?5TJQB9!zY@!+?s`{-N`I^ zzk>nBF90qKrC3K+*F7_G|M=38EI;QZJ7OCEme70F9LjBDwVD&HHPTBD736cHpBg05 zg1P&1DZ56^uoh*3>6kpV$b3?VrQg5YJ!DG7z1oWPof3R6J$w7GE2PQ_ zIE%T^yD`xR4qbI~7t)zGkr90$>g%~ST(W%M)=bZtyM~mg5NH&CgB?%?ov+%*@Mp{ao8lV9&>>Fdq4+`-ZGeK+C zBrl3M-}BOmCwx%d>bqYBU#o^R>ivX`WW`}(B9Pw5lFT-EJV&0v>rG6I_*r|2X@wUxetQ^Zhfw(<8x1PP7Rq)a);Fyn2 z0z`m3U&O7viPlx$@ngV%{S)F<)osO)e$&9a!$lk!n-__M8`!8DdV=P?;Ml^8krfCw zz11oJa^yD&RJx|^Lk2FR4I3zSgUojuvYO!A6AXjDvpyg{nETz{^|SLFbVcvMx{rgP zz|G6WCFmOjaem(m3Yp8kj|P(BdjM%tqKqZB_ObV~OLHBCttn-2a zvN9M9wY#dE&?7U+*SEIB9pF4Nt6aauHz<#yxlU#l3qDwM-(<*PXrTmahjZK#`aq`n zL9)G0@?;W@!@lGi0-k|nYHR!1Mt$xm9X*cogqGd86ZcsbeM|{QBb5kwoo;`J-vBMY zwdH9{16j(?$-G?&Ai`UBiKJ3r%j}KxzFRD_|7YWBR6*sOU12hEOZ7k_(Iu|qE{+%^ zX;-cM_FBc_JALTu&qj`WUoCEHFp+}Bp(SBS&ACg)5h^1Y9@~o&!nA!-%eRp#sXI@3 zBTC@NZ6h_S0Y;yg&uKa^71(bK1qV{e*aA}=;oV!RYiP%ReA~p!rxb}MIg3&>r(VQf zJW`1%7W_4F;xZ;5*@y0dh*u@GycuP7Np{QG`cP%rhetR77i~9x;v`6& z9}Ag_vug{@Uk60zs!Y>Wi}iT`j^--bgLZ~rX+y4Xt+L-<_4ZR@Pc??9+qu0pWPT1N z82b7WvZdPTTVUnxcQ_-O3x_!Wx)T(!c8-PN!SLh@TeY~>DP?#c>?+IWN`9%GFE17> zYRXXPDtNnsQirq++Bd!egZa%$ff65lM~H|Q_IlB~lvh{Eb`4zeh>Cynjt)ae#V|Q@5p<1L&|o@Q7A-5j`Vt@J_Wa zx^ENc4$1(?ZL$6s0HtLv0Gn~h>R=xHq;fS-%9%@K_3Lt>;9>|PKUV^qt}RbSl0;v6 zGjtQx%q}!r;85+iE;Ui>)buX2lK7p64s+74Iz@ZH$n6~?;JDA~!lxXisQT*3 zNBLai-J$HZX-~gH{c(DoqNkwfofxJ+9S#I>Y2W6i ztS{>o`PE=wDqp{up(BAZEZu7hqefh5BP4_z{*q-8_(X07HC>hmH z3emMv@jqRNaMz7U$0=y{e%d6m;bdy8$X=LW$)eL9rp4fA%J>CG16}On&2KzzG{JM4r_E8uF#EK z{gikkB~jLX^};!zM!De|Of9c7Mc<(dg@1VA0zbFGMYQOjR{6hAYMu9l?LF&RT>Ys_;F)fk<+Ng zg{lBmN5iDC+qbnna+`ui@Zu+XWg*2i@D|z+zg6-oQh&)`8m07Bx$1Db;&G|H_EPZU zUW5-++RG)CG@FZS6^Ub|w#^L2!=#A%xXN>N&qX56RPqDT9pEq%v3>XFcBKe$-B)RPGFyy&#K{JOwzN}nP$H~Tp+wcA`*~tsJs(fo*gw$!+ynPv>^ZN z(e!NApx-eitam3-v+4RcM>mhn8v*jVBzaG2fj3bYd@^0c)q7a!Mm?I+V>}!6K(&hU z=)X*S>$!Pr6-BKEpYZKk7Uz~APKvnyKT?F0bCy5X?J)T(IQaK5|4!P!p7!s)`8U{{ zeYeBEq4VE-^KbV2x8(S@fcm%U{Qs?n4y3;F2OYf70iQqQbCkp;AvzoO!X%C{zI%rV z)1srkwy1GQY*du*XZ1uYW7jnISqNDy?OBXE*0x}noPco-JJ}TtfrlH?#-{ z)YlN0`Yj|&RxhWPaMjqJ5RpE^pNpUhinGsnze|$ONQh$6;>krMCtbNMYB;}2nn?Qe z2&)!u_s^N*eAdn^T~n7Ai32B$|4ikBvR`V(Y(efDHWvn@uJ62er9OM(`|9Gk9CruF zd~4iIqn@l&hP71u6EZ*qOlFFQ$r+JbKqM(4SrCPFWI7uI1MSeZeOhw*i;FK7tww71 z&Z@CfM^h);FTcC}8gEfvBy~3~azZ4;eR%crg66ZebFUS$uGS#jd6Yp)%Xsh{ z<904EhJFb-*9bDJ%T>x}6cQRjvMe+cRB1%Oknpsp&8iG~M=qk3CTI2Ac~y(_k@7y2 zhxqrqk$ImypWH)XORTXpZ$`ug&MF11>k=(#i07#Z9_hvzca-!*^T`l?{%ni+GXgI^ ztNyPUNC4i%)=w;XJ4rQ5(5!F-DrJy!)@s*BU2kF@W^e{}hUdL2VBRNmdpLi#T2tD- zcAuPhn%vD+WX`NG+_L#cR?hM{MMdE#YlbCRg-Hf_aGSEW`aSsA@EExqcH`_DP`tA; z7e14LKpAuc+ms&)c=yS*VAkh|MQfNi31{~_;oAQ8_(w9{)KDvY)!tyGRX>5A*3b%0^j5SNCy$QhEPe_ZUM|zm`1QVyEa?91q#E_L`$0sZ zhrtel56CO+`phk*^_=9$ZtqPyYodf%kvj!e-uMj~2s7pCA*cL{>d}U}amXK6BZ#Sq z&)i!Q=9b7_vdbLu0rK(k>(ZH`gmxrp$f1VgG8nqQ%*XfG0=L?1cs(ll*2g#yoKrsF z0s&zJNgfsXGS_S0z@r8^M@R9U9hb2NIX2V2D?Vf*k*v8_fV*7?yc5XQQ$j(wKvx4F zUUXbuNo!>}?(|qr-A*k3q$*B)^306VMin~rH*ze@Pv%oHB9zpUfM8iN#J1`Sld5#ZH4+USpR7zvT2%ojm8V^_{z~#I$Y& znp4(D?;J5mFcn1lg{1CzMwlzv{2elf+GKkoc8NSir4b5UMoFaSNcI(YWm7A$O}wC|9>(W5-c_gEY9h*06qIa1C#mUKz?Tc;7wt@yQ$#ml0EkTSwjc$hlrp zy|dqb?0B~dXXif_Eh55F|xcS4U{o#*i~KdabQ2k7N|{K0!i zKTESkPAup6$kokth*Fp=Al`L49vWa<%0Nz!%B3xX*QKEhzfPG4jc`-q?pAL+IP$M( z*eq^vTv(l1wz(#ydgHHmJR^(>w{3XzqOcJa(WXm7Su;4%?|N_mKfUU>EB22#j3@VZ zr`CefF~v@Ons3y%PtT3my=T^+8pR3NGxj$kds_1AQ~9%x6Hj^IW6ewNxnF;7Y@Um% zrjy%$*TWe36;UI7X(0Q6{In1Hn+LonOL;Q6SHy20>R;c2-ZJ3!p}O`b?2O;gSs|tmFQ}Fl$-90yo9KF2PLs z@CkJi$5++Hkk$}eR2c0>#;Mi0d{4sqO(SpM(Se|y2R#d^aN?HUd?FYPl}VC6_2v79-cpwX zcL}RhR!Bw~K2sb9%FhgGGymNfzkt)Zw+`+Fas=*-l1E+g9u^-_J@wV0#V?s_>J&A% zn@1w15najB3Ry{xr(8mtF36HhnrE_V$xa+pAG-XF&Zddxv`4)AEgwxBdHsc=g(5f> zxTtU{E;at|#Y%U@ofWJSfyYN-iqjr0JdcFlT5lpe7-S#7@QC4?>r3RMH`Hd9A_Y#{ zADK2yt(;K;I3^W}IS`FfpJ39qSUKYfaO+(j!YhPbrm$fR&vwjS(YryPKK0WcrLK7` zX(FkyIk%%1xJUEcfJQ`}|0z@C|ArVEvegMclk)7LZF6686cKlK3cKj3Ju5L2s7iiK zadO|iFFBV%e00cof{ke8Ugw?DQMNN>FD%mc^KRk6M;B-WM{u1j&o}vo` zr_kcwv4)9Xco;fabHP_z8mCQi>Q;@~bYtJhE?xOEI10S|X9n&HY2bun3x!Sv=*GfV zyBvfs37HZOaHee4EANhAKv-l-cv-R)Kl=Mr6f$DFzYVr^YaYsk=ES=VhgGRNpwt59@#aH*I-v{Zn6{hotl4R+X~SaX}8GJ zL-{PsmLJ;)1ZQ#fnCd4q_LNStX|4UvUHoS}hIKzJU7jd#4nxm!m^Lw&?3<3X&L|0r zIg^)TZq~k~{e0^y#0yy)4jNw-U%MldKYrROtp`4L178F>pf|7y6K_maVf=)r*1K_5 zA)wKe&`H)|N)mJSE%xJWoZG&r$;UDS*8?I8dA8a=HQZf$Ny%=}}}1 zU&^>fC_}ut3v$<$>HNZ7bp6xSAfpD&z)dHtpvPe+MVa4}%gxZ6AQy`A~pFx}Rj2BvR&tOEBU#mNJg$afo9 zCb8=2eP3_C<#zDie+A{-y!9M^!WEE0Wri(>K*f>+Mf#s!-{!l$YAMhAkJV4QXZ+4a zGD)F)&IQRvb-@U`h^;Yeqw<(OK0Lqt-)@_iMWLt4eQZy@wt44?oTqnaIv!-P;?jA) zb@#5yP5Enf{VrSi^yQv|>mRSYuyh$xl!KcW3eFE+v#u7tyEVyjjdzgb^OG-kqqHsh zOV2wbOlCT5{c&2i$LaPpbw$Nr-+jL8zGRwnW%hlw^NZch?;=Hl+}vw*6B!z}GM%pe zJa2!LnQQs{ZNI*L->ttn>ff~9Yn%T3x<8#=XZpfvvR6K7-@CGX+KtDnkgL2EiaEZZ zy7x13JNUq#jgIV~CQ$%#;pM=am<>wbGxU+GlxO|CR^Voz7IKtjaYPFPMGw>cVhuF$PX)!bL)b(4W=Mxoe@V)o1c%f$%4BDC~ZH}mbIKf(S&^{ zMU%bR3vdHwG-yYIb~M$F76>y&3k2`cvKBZ3G+K>qVi|2AoDdo<5EMoW1kjN1Xn{a+ zfdJ}lfQRHp8KBP4XlRTE!f19F%>@K=LE~YTf6ShOMVk_M7XAa?`N!bt>gTe~DWM4f DH{-WH literal 0 HcmV?d00001 diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 7a264b70008f0..f387bf4668baf 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,6 +25,7 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), + 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), }; Scenario _currentScenario = _scenarios['animated_color_square']; From 2a1ade7908af4c929b3ecedf61b9941d3601d08f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 5 Dec 2019 14:51:55 -0800 Subject: [PATCH 337/591] Raise API level for reportFullyDrawn (#14146) --- .../android/io/flutter/embedding/android/FlutterActivity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 671103f8d3f03..ebba0750ff58d 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -938,7 +938,9 @@ public boolean shouldAttachEngineToActivity() { public void onFlutterUiDisplayed() { // Notifies Android that we're fully drawn so that performance metrics can be collected by // Flutter performance tests. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // This was supported in KitKat (API 19), but has a bug around requiring + // permissions. See https://github.com/flutter/flutter/issues/46172 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { reportFullyDrawn(); } } From 91ef612ee0676b1e513ba8b21b2c3480ddfea4a5 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Thu, 5 Dec 2019 15:17:34 -0800 Subject: [PATCH 338/591] [web] [tests] Changing configurations for firefox (#14148) * Changing configurations for firefox * remove headless * change comments on configurations files * remove unused boolean --- lib/web_ui/{dart_test.yaml => dart_test_chrome.yaml} | 6 +++--- lib/web_ui/dart_test_firefox.yaml | 7 +++++++ lib/web_ui/dev/firefox.dart | 10 ++++++---- lib/web_ui/dev/supported_browsers.dart | 6 ++++++ lib/web_ui/dev/test_runner.dart | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) rename lib/web_ui/{dart_test.yaml => dart_test_chrome.yaml} (64%) create mode 100644 lib/web_ui/dart_test_firefox.yaml diff --git a/lib/web_ui/dart_test.yaml b/lib/web_ui/dart_test_chrome.yaml similarity index 64% rename from lib/web_ui/dart_test.yaml rename to lib/web_ui/dart_test_chrome.yaml index 5715dbe897f2e..a43e4e114d980 100644 --- a/lib/web_ui/dart_test.yaml +++ b/lib/web_ui/dart_test_chrome.yaml @@ -1,11 +1,11 @@ -# Sources of inspiration for this file: +# For more information on test and runner configurations: # -# * https://github.com/dart-lang/angular/blob/master/dev/tool/test/dart_test_repo.yaml -# * https://github.com/dart-lang/angular/blob/master/_tests/dart_test.yaml +# * https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#platforms platforms: - chrome - vm + presets: cirrus: override_platforms: diff --git a/lib/web_ui/dart_test_firefox.yaml b/lib/web_ui/dart_test_firefox.yaml new file mode 100644 index 0000000000000..6bfa5925562b0 --- /dev/null +++ b/lib/web_ui/dart_test_firefox.yaml @@ -0,0 +1,7 @@ +# For more information on test and runner configurations: +# +# * https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#platforms + +platforms: + - firefox + - vm diff --git a/lib/web_ui/dev/firefox.dart b/lib/web_ui/dev/firefox.dart index 98e4b242cfe76..eb05a07a5553b 100644 --- a/lib/web_ui/dev/firefox.dart +++ b/lib/web_ui/dev/firefox.dart @@ -49,17 +49,19 @@ class Firefox extends Browser { var args = [ url.toString(), if (!debug) '--headless', - '-width $kMaxScreenshotWidth' + '-width $kMaxScreenshotWidth', '-height $kMaxScreenshotHeight', '-new-window', '-new-instance', + '-prefs { "dom.disable_beforeunload" = true, "toolkit.startup.max_resumed_crashes"=999999 } ', '--start-debugger-server $kDevtoolsPort', ]; - final Process process = await Process.start(installation.executable, args); + final Process process = + await Process.start(installation.executable, args); - remoteDebuggerCompleter.complete(getRemoteDebuggerUrl( - Uri.parse('http://localhost:$kDevtoolsPort'))); + remoteDebuggerCompleter.complete( + getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort'))); unawaited(process.exitCode .then((_) => Directory(dir).deleteSync(recursive: true))); diff --git a/lib/web_ui/dev/supported_browsers.dart b/lib/web_ui/dev/supported_browsers.dart index 8cd0251ad54d5..3d16f1a62013a 100644 --- a/lib/web_ui/dev/supported_browsers.dart +++ b/lib/web_ui/dev/supported_browsers.dart @@ -8,6 +8,7 @@ import 'browser.dart'; import 'chrome.dart'; import 'chrome_installer.dart'; import 'common.dart'; +import 'environment.dart'; import 'firefox.dart'; import 'firefox_installer.dart'; // ignore: implementation_imports @@ -30,6 +31,11 @@ class SupportedBrowsers { 'firefox': Runtime.firefox }; + final Map browserToConfiguration = { + 'chrome': '--configuration=${environment.webUiRootDir.path}/dart_test_chrome.yaml', + 'firefox': '--configuration=${environment.webUiRootDir.path}/dart_test_firefox.yaml', + }; + static final SupportedBrowsers _singletonInstance = SupportedBrowsers._(); /// The [SupportedBrowsers] singleton. diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 83025795080b7..48891327206f0 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -243,6 +243,7 @@ class TestCommand extends Command { if (isDebug) '--pause-after-load', '--platform=$browser', '--precompiled=${environment.webUiRootDir.path}/build', + SupportedBrowsers.instance.browserToConfiguration[browser], '--', ...testFiles.map((f) => f.relativeToWebUi).toList(), ]; From 3ad666248c05982954fabbed14a51279b1a7723e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 5 Dec 2019 18:30:32 -0500 Subject: [PATCH 339/591] Roll src/third_party/skia 0af13b3caf03..cc92b27c78a0 (26 commits) (#14151) https://skia.googlesource.com/skia.git/+log/0af13b3caf03..cc92b27c78a0 git log 0af13b3caf03..cc92b27c78a0 --date=short --first-parent --format='%ad %ae %s' 2019-12-05 fmalita@chromium.org [skottie] Optionally-deferred single-frame image loading 2019-12-05 mtklein@google.com stifle GCC warning 2019-12-05 hcm@google.com Update release notes files 2019-12-05 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-05 egdaniel@google.com Have GrLatticeOp use GrSurfaceProxyView. 2019-12-05 mtklein@google.com upgrade SkMiniRecorder to c++11 2019-12-05 brianosman@google.com Particles: Remove play-position thing, simplify mouse tracking 2019-12-05 bsalomon@google.com Revert "Remove most of GrConfig.h" 2019-12-05 reed@google.com clip paths in perspective (as needed) 2019-12-05 herb@google.com Have direct mask glyphs happen after SDFT 2019-12-05 mtklein@google.com more "SkScalerContextRec is bytes" 2019-12-05 bsalomon@google.com Remove most of GrConfig.h 2019-12-05 mtklein@google.com tell GCC copying into SkScalerContextRect is safe 2019-12-05 mtklein@google.com stifle GCC warnings in Sk4px 2019-12-05 robertphillips@google.com Add mutex to guard GrSkSLFPFactoryCache accesses 2019-12-05 michaelludwig@google.com Help MSVC pack GrTextureOps bitfields 2019-12-05 mtklein@google.com Stifle GCC false positive 2019-12-05 mtklein@google.com clearing fFlags is redundant here 2019-12-05 reed@google.com show perspective bug (need for clipping) 2019-12-05 mtklein@google.com GCC 8 reminds us, don't memcpy() sk_sp 2019-12-05 bsalomon@google.com Some changes to GrYUVToRGBEffect. 2019-12-05 reed@google.com move perspective-clipper into SkPathPriv 2019-12-05 benjaminwagner@google.com Add additional Mac Metal jobs 2019-12-05 hcm@google.com Update Skia milestone to 81 2019-12-05 egdaniel@google.com Always destroy resources instead of simply "abandoning" them in Vulkan backend. 2019-12-05 fmalita@chromium.org [skottie] Fix precomposed camera sizing Created with: gclient setdep -r src/third_party/skia@cc92b27c78a0 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ed68657272af9..eaf635c69ebac 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '0af13b3caf0384981f50e073ebc7bd0f3f751711', + 'skia_revision': 'cc92b27c78a0eb76e3bf3a68da7342fad57ae9af', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 8608b77df677c..c946caa8b8171 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1d620f9e9f640326a25368428f153cc1 +Signature: be418c8df8becf9ec26322125192d207 UNUSED LICENSES: From af511babc76354cd283efa3d3f80fdd4169f9ecb Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 5 Dec 2019 16:25:55 -0800 Subject: [PATCH 340/591] Do not paint a layer's children if the children were not prerolled (#14149) Prerolling a layer can have side effects. In particular, PlatformViewLayer::Preroll will call view_embedder->PrerollCompositeEmbeddedView. Clip layers will check whether the layer's children are all clipped and if so will skip calling Preroll on the children. However, the Paint implementation in these layers was always calling Paint on their children. This could result in a call to PlatformViewLayer::Paint without a corresponding call to PlatformViewLayer::Preroll. This translates to a CompositeEmbeddedView call without a PrerollCompositeEmbeddedView call on the affected view_id. The EmbedderExternalViewEmbedder implementation does not allow that. With this change, clip layers will only call PaintChildren if the preroll called PrerollChildren. See https://github.com/flutter/flutter/issues/46111 --- flow/layers/clip_path_layer.cc | 6 +++++- flow/layers/clip_path_layer.h | 1 + flow/layers/clip_rect_layer.cc | 6 +++++- flow/layers/clip_rect_layer.h | 1 + flow/layers/clip_rrect_layer.cc | 6 +++++- flow/layers/clip_rrect_layer.h | 1 + 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 3957837d0d7f5..4353debfc095e 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -20,7 +20,8 @@ ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_path_bounds = clip_path_.getBounds(); - if (context->cull_rect.intersect(clip_path_bounds)) { + children_inside_clip_ = context->cull_rect.intersect(clip_path_bounds); + if (children_inside_clip_) { context->mutators_stack.PushClipPath(clip_path_); SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_paint_bounds); @@ -49,6 +50,9 @@ void ClipPathLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipPathLayer::Paint"); FML_DCHECK(needs_painting()); + if (!children_inside_clip_) + return; + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->clipPath(clip_path_, clip_behavior_ != Clip::hardEdge); diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index c21e53c34e76e..aac23d6935b76 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -24,6 +24,7 @@ class ClipPathLayer : public ContainerLayer { private: SkPath clip_path_; Clip clip_behavior_; + bool children_inside_clip_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ClipPathLayer); }; diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index 191132a055788..9e12839aedcdc 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -13,7 +13,8 @@ ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; - if (context->cull_rect.intersect(clip_rect_)) { + children_inside_clip_ = context->cull_rect.intersect(clip_rect_); + if (children_inside_clip_) { context->mutators_stack.PushClipRect(clip_rect_); SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_paint_bounds); @@ -42,6 +43,9 @@ void ClipRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRectLayer::Paint"); FML_DCHECK(needs_painting()); + if (!children_inside_clip_) + return; + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->clipRect(clip_rect_, clip_behavior_ != Clip::hardEdge); diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index 50eef22a46e2f..f503a59336558 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -23,6 +23,7 @@ class ClipRectLayer : public ContainerLayer { private: SkRect clip_rect_; Clip clip_behavior_; + bool children_inside_clip_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ClipRectLayer); }; diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index e02f8d2411a2b..4a22151d660d3 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -14,7 +14,8 @@ ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; SkRect clip_rrect_bounds = clip_rrect_.getBounds(); - if (context->cull_rect.intersect(clip_rrect_bounds)) { + children_inside_clip_ = context->cull_rect.intersect(clip_rrect_bounds); + if (children_inside_clip_) { context->mutators_stack.PushClipRRect(clip_rrect_); SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_paint_bounds); @@ -43,6 +44,9 @@ void ClipRRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRRectLayer::Paint"); FML_DCHECK(needs_painting()); + if (!children_inside_clip_) + return; + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->clipRRect(clip_rrect_, clip_behavior_ != Clip::hardEdge); diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index ce1cca2b568de..45daf48ba6e36 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -24,6 +24,7 @@ class ClipRRectLayer : public ContainerLayer { private: SkRRect clip_rrect_; Clip clip_behavior_; + bool children_inside_clip_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer); }; From 29998f07faf9368d399cdc353dca3eaa480ac95f Mon Sep 17 00:00:00 2001 From: Filip Filmar Date: Thu, 5 Dec 2019 17:32:55 -0800 Subject: [PATCH 341/591] Configures ICU to load the timezone data (#13952) The timezone data in Fuchsia is at a fixed path. This will have the flutter runner attempt to load it and log, but not fail if loading does not work out. Added two tests (1) Shows that the specific TZ data version loaded takes effect after initialization (2) Shows that when TZ data files are absent the initialization continues. --- ci/licenses_golden/licenses_flutter | 3 + .../meta/dart_aot_product_runner.cmx | 1 + .../dart_runner/meta/dart_aot_runner.cmx | 1 + .../meta/dart_jit_product_runner.cmx | 5 +- .../dart_runner/meta/dart_jit_runner.cmx | 5 +- shell/platform/fuchsia/flutter/BUILD.gn | 81 ++++++++++++++++++ .../meta/flutter_aot_product_runner.cmx | 3 +- .../flutter/meta/flutter_aot_runner.cmx | 3 +- .../meta/flutter_jit_product_runner.cmx | 5 +- .../flutter/meta/flutter_jit_runner.cmx | 5 +- .../flutter/meta/flutter_runner_tests.cmx | 9 +- .../meta/flutter_runner_tzdata_tests.cmx | 16 ++++ shell/platform/fuchsia/flutter/runner.cc | 60 ++++++++++++- shell/platform/fuchsia/flutter/runner.h | 9 ++ .../fuchsia/flutter/runner_tzdata_unittest.cc | 20 +++++ .../fuchsia/flutter/runner_unittest.cc | 28 ++++++ .../tests/tzdata/2019a/44/le/metaZones.res | Bin 0 -> 41488 bytes .../tzdata/2019a/44/le/timezoneTypes.res | Bin 0 -> 20064 bytes .../tests/tzdata/2019a/44/le/zoneinfo64.res | Bin 0 -> 154224 bytes .../fuchsia/flutter/tests/tzdata/README.md | 6 ++ 20 files changed, 245 insertions(+), 15 deletions(-) create mode 100644 shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx create mode 100644 shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc create mode 100644 shell/platform/fuchsia/flutter/runner_unittest.cc create mode 100644 shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/metaZones.res create mode 100644 shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/timezoneTypes.res create mode 100644 shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/zoneinfo64.res create mode 100644 shell/platform/fuchsia/flutter/tests/tzdata/README.md diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7eec11ad58215..2a5be4e4d7acb 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1014,6 +1014,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cm FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_product_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view.cc @@ -1021,6 +1022,8 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/session_connection.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/session_connection.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.cc diff --git a/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx b/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx index cb0587353f24c..5539713b9a19f 100644 --- a/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx +++ b/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx @@ -4,6 +4,7 @@ }, "sandbox": { "features": [ + "config-data", "deprecated-ambient-replace-as-executable", "root-ssl-certificates" ], diff --git a/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx b/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx index cb0587353f24c..5539713b9a19f 100644 --- a/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx +++ b/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx @@ -4,6 +4,7 @@ }, "sandbox": { "features": [ + "config-data", "deprecated-ambient-replace-as-executable", "root-ssl-certificates" ], diff --git a/shell/platform/fuchsia/dart_runner/meta/dart_jit_product_runner.cmx b/shell/platform/fuchsia/dart_runner/meta/dart_jit_product_runner.cmx index bbf0d394c5853..f8a61ac927144 100644 --- a/shell/platform/fuchsia/dart_runner/meta/dart_jit_product_runner.cmx +++ b/shell/platform/fuchsia/dart_runner/meta/dart_jit_product_runner.cmx @@ -4,8 +4,9 @@ }, "sandbox": { "features": [ - "root-ssl-certificates", - "deprecated-ambient-replace-as-executable" + "config-data", + "deprecated-ambient-replace-as-executable", + "root-ssl-certificates" ], "services": [ "fuchsia.feedback.CrashReporter", diff --git a/shell/platform/fuchsia/dart_runner/meta/dart_jit_runner.cmx b/shell/platform/fuchsia/dart_runner/meta/dart_jit_runner.cmx index bbf0d394c5853..f8a61ac927144 100644 --- a/shell/platform/fuchsia/dart_runner/meta/dart_jit_runner.cmx +++ b/shell/platform/fuchsia/dart_runner/meta/dart_jit_runner.cmx @@ -4,8 +4,9 @@ }, "sandbox": { "features": [ - "root-ssl-certificates", - "deprecated-ambient-replace-as-executable" + "config-data", + "deprecated-ambient-replace-as-executable", + "root-ssl-certificates" ], "services": [ "fuchsia.feedback.CrashReporter", diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 0549a1f75352c..2db9b474ee67c 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -308,6 +308,9 @@ executable("flutter_runner_unittests") { "platform_view.cc", "platform_view.h", "platform_view_unittest.cc", + "runner.cc", + "runner.h", + "runner_unittest.cc", "surface.cc", "surface.h", "task_observers.cc", @@ -335,6 +338,7 @@ executable("flutter_runner_unittests") { "//build/fuchsia/pkg:scenic_cpp", "//build/fuchsia/pkg:sys_cpp_testing", "//flutter/lib/ui", + "//flutter/runtime", "//flutter/shell/common", "//flutter/shell/platform/fuchsia/runtime/dart/utils", "//flutter/testing", @@ -343,6 +347,40 @@ executable("flutter_runner_unittests") { ] } +executable("flutter_runner_tzdata_unittests") { + testonly = true + + output_name = "flutter_runner_tzdata_tests" + + sources = [ + "runner.cc", + "runner.h", + "runner_tzdata_unittest.cc", + ] + + # This is needed for //third_party/googletest for linking zircon symbols. + libs = [ "//fuchsia/sdk/$host_os/arch/$target_cpu/sysroot/lib/libzircon.so" ] + + deps = [ + ":aot", + ":flutter_runner_fixtures", + "//build/fuchsia/fidl:fuchsia.accessibility.semantics", + "//build/fuchsia/pkg:async-loop-cpp", + "//build/fuchsia/pkg:async-loop-default", + "//build/fuchsia/pkg:scenic_cpp", + "//build/fuchsia/pkg:sys_cpp_testing", + "//flutter/lib/ui", + "//flutter/runtime", + "//flutter/shell/common", + "//flutter/shell/platform/fuchsia/runtime/dart/utils", + "//flutter/testing", + "//third_party/dart/runtime:libdart_jit", + "//third_party/dart/runtime/platform:libdart_platform_jit", + "//third_party/icu", + "//third_party/skia", + ] +} + fuchsia_archive("flutter_runner_tests") { testonly = true @@ -352,6 +390,49 @@ fuchsia_archive("flutter_runner_tests") { binary = "$target_name" + resources = [ + { + path = rebase_path("//third_party/icu/common/icudtl.dat") + dest = "icudtl.dat" + }, + { + path = rebase_path( + "$flutter_root/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/metaZones.res") + dest = "tzdata/metaZones.res" + }, + { + path = rebase_path( + "$flutter_root/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/timezoneTypes.res") + dest = "tzdata/timezoneTypes.res" + }, + { + path = rebase_path( + "$flutter_root/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/zoneinfo64.res") + dest = "tzdata/zoneinfo64.res" + }, + ] + + meta_dir = "$flutter_root/shell/platform/fuchsia/flutter/meta" + + libraries = common_libs + + meta = [ + { + path = rebase_path("meta/$target_name.cmx") + dest = "$target_name.cmx" + }, + ] +} + +fuchsia_archive("flutter_runner_tzdata_tests") { + testonly = true + + deps = [ + ":flutter_runner_tzdata_unittests", + ] + + binary = "$target_name" + resources = [ { path = rebase_path("//third_party/icu/common/icudtl.dat") diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx index b127787027c81..d3c76ec76d16d 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx @@ -4,8 +4,9 @@ }, "sandbox": { "features": [ - "root-ssl-certificates", + "config-data", "deprecated-ambient-replace-as-executable", + "root-ssl-certificates", "vulkan" ], "services": [ diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index 244761694a8dc..af0c724590652 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -4,8 +4,9 @@ }, "sandbox": { "features": [ - "root-ssl-certificates", + "config-data", "deprecated-ambient-replace-as-executable", + "root-ssl-certificates", "vulkan" ], "services": [ diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx index 912b534df93a2..ee97b1e63c731 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx @@ -4,9 +4,10 @@ }, "sandbox": { "features": [ + "config-data", + "deprecated-ambient-replace-as-executable", "root-ssl-certificates", - "vulkan", - "deprecated-ambient-replace-as-executable" + "vulkan" ], "services": [ "fuchsia.accessibility.SettingsManager", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx index 1119b279ddd6c..af0c724590652 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -4,9 +4,10 @@ }, "sandbox": { "features": [ + "config-data", + "deprecated-ambient-replace-as-executable", "root-ssl-certificates", - "vulkan", - "deprecated-ambient-replace-as-executable" + "vulkan" ], "services": [ "fuchsia.accessibility.SettingsManager", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx index ea275dd650b1b..1893721bfff73 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx @@ -1,11 +1,14 @@ { "program": { - "binary": "bin/app" + "binary": "bin/app", + "env_vars": [ + "ICU_TIMEZONE_FILES_DIR=/pkg/data/tzdata" + ] }, "sandbox": { "features": [ - "vulkan", - "deprecated-ambient-replace-as-executable" + "deprecated-ambient-replace-as-executable", + "vulkan" ], "services": [ "fuchsia.accessibility.semantics.SemanticsManager", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx b/shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx new file mode 100644 index 0000000000000..53c38f29489cf --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx @@ -0,0 +1,16 @@ +{ + "program": { + "binary": "bin/app" + }, + "sandbox": { + "features": [ + "deprecated-ambient-replace-as-executable", + "vulkan" + ], + "services": [ + "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.intl.PropertyProvider", + "fuchsia.process.Launcher" + ] + } +} diff --git a/shell/platform/fuchsia/flutter/runner.cc b/shell/platform/fuchsia/flutter/runner.cc index 876645b1b1d82..2fe0b5626ada6 100644 --- a/shell/platform/fuchsia/flutter/runner.cc +++ b/shell/platform/fuchsia/flutter/runner.cc @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include @@ -29,6 +31,13 @@ namespace { static constexpr char kIcuDataPath[] = "/pkg/data/icudtl.dat"; +// Environment variable containing the path to the directory containing the +// timezone files. +static constexpr char kICUTZEnv[] = "ICU_TIMEZONE_FILES_DIR"; + +// The data directory containing ICU timezone data files. +static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le"; + // Map the memory into the process and return a pointer to the memory. uintptr_t GetICUData(const fuchsia::mem::Buffer& icu_data) { uint64_t data_size = icu_data.size; @@ -46,6 +55,33 @@ uintptr_t GetICUData(const fuchsia::mem::Buffer& icu_data) { return 0u; } +// Initializes the timezone data if available. Timezone data file in Fuchsia +// is at a fixed directory path. Returns true on success. As a side effect +// sets the value of the environment variable "ICU_TIMEZONE_FILES_DIR" to a +// fixed value which is fuchsia-specific. +bool InitializeTZData() { + // We need the ability to change the env variable for testing, so not + // overwriting if set. + setenv(kICUTZEnv, kICUTZDataDir, 0 /* No overwrite */); + + const std::string tzdata_dir = getenv(kICUTZEnv); + // Try opening the path to check if present. No need to verify that it is a + // directory since ICU loading will return an error if the TZ data path is + // wrong. + int fd = openat(AT_FDCWD, tzdata_dir.c_str(), O_RDONLY); + if (fd < 0) { + FML_LOG(INFO) << "Could not open: '" << tzdata_dir + << "', proceeding without loading the timezone database: " + << strerror(errno); + return false; + } + if (!close(fd)) { + FML_LOG(WARNING) << "Could not close: " << tzdata_dir << ": " + << strerror(errno); + } + return true; +} + // Return value indicates if initialization was successful. bool InitializeICU() { const char* data_path = kIcuDataPath; @@ -60,10 +96,18 @@ bool InitializeICU() { return false; } + // If the loading fails, soldier on. The loading is optional as we don't + // want to crash the engine in transition. + InitializeTZData(); + // Pass the data to ICU. UErrorCode err = U_ZERO_ERROR; udata_setCommonData(reinterpret_cast(data), &err); - return err == U_ZERO_ERROR; + if (err != U_ZERO_ERROR) { + FML_LOG(ERROR) << "error loading ICU data: " << err; + return false; + } + return true; } } // namespace @@ -223,11 +267,23 @@ void Runner::OnApplicationTerminate(const Application* application) { } void Runner::SetupICU() { - if (!InitializeICU()) { + // Exposes the TZ data setup for testing. Failing here is not fatal. + Runner::SetupTZDataInternal(); + if (!Runner::SetupICUInternal()) { FML_LOG(ERROR) << "Could not initialize ICU data."; } } +// static +bool Runner::SetupICUInternal() { + return InitializeICU(); +} + +// static +bool Runner::SetupTZDataInternal() { + return InitializeTZData(); +} + #if !defined(DART_PRODUCT) void Runner::SetupTraceObserver() { trace_observer_ = std::make_unique(); diff --git a/shell/platform/fuchsia/flutter/runner.h b/shell/platform/fuchsia/flutter/runner.h index ca4cb70d3e530..4abba33a6060f 100644 --- a/shell/platform/fuchsia/flutter/runner.h +++ b/shell/platform/fuchsia/flutter/runner.h @@ -64,6 +64,15 @@ class Runner final : public fuchsia::sys::Runner { void SetupTraceObserver(); #endif // !defined(DART_PRODUCT) + // Called from SetupICU, for testing only. Returns false on error. + static bool SetupICUInternal(); + // Called from SetupICU, for testing only. Returns false on error. + static bool SetupTZDataInternal(); +#if defined(FRIEND_TEST) + FRIEND_TEST(RunnerTest, TZData); + FRIEND_TEST(RunnerTZDataTest, LoadsWithoutTZDataPresent); +#endif // defined(FRIEND_TEST) + FML_DISALLOW_COPY_AND_ASSIGN(Runner); }; diff --git a/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc b/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc new file mode 100644 index 0000000000000..c90239c9170af --- /dev/null +++ b/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "runner.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" + +namespace flutter_runner { + +// This test has not been configured with tzdata files. This test shows that +// even without the data files, the runner continues initialization. It will +// use whatever the base data exists in icudtl.dat. +TEST(RunnerTZDataTest, LoadsWithoutTZDataPresent) { + bool success = Runner::SetupICUInternal(); + ASSERT_TRUE(success) << "failed to load set up ICU data"; +} + +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/runner_unittest.cc b/shell/platform/fuchsia/flutter/runner_unittest.cc new file mode 100644 index 0000000000000..f0ec0b7889871 --- /dev/null +++ b/shell/platform/fuchsia/flutter/runner_unittest.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "runner.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" + +namespace flutter_runner { + +TEST(RunnerTest, TZData) { + UErrorCode err = U_ZERO_ERROR; + const auto version_before = std::string(icu::TimeZone::getTZDataVersion(err)); + ASSERT_EQ(U_ZERO_ERROR, err) << "unicode error: " << u_errorName(err); + + // This loads the tzdata. In Fuchsia, we force the data from this package + // to be version 2019a, so that we can test the resource load. + bool success = Runner::SetupICUInternal(); + ASSERT_TRUE(success) << "failed to load timezone data"; + + const auto version_after = std::string(icu::TimeZone::getTZDataVersion(err)); + ASSERT_EQ(U_ZERO_ERROR, err) << "unicode error: " << u_errorName(err); + + EXPECT_EQ("2019a", version_after); +} + +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/metaZones.res b/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/metaZones.res new file mode 100644 index 0000000000000000000000000000000000000000..3b3ee01349732bfdb4e2fb54e674bc7e3de8da14 GIT binary patch literal 41488 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 WT2Q;GT2y~|n5cebmgwA$|NTFbBCA3G literal 0 HcmV?d00001 diff --git a/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/timezoneTypes.res b/shell/platform/fuchsia/flutter/tests/tzdata/2019a/44/le/timezoneTypes.res new file mode 100644 index 0000000000000000000000000000000000000000..895ed2174c0d381a3d660a3b353f7c07a1001582 GIT binary patch literal 20064 zcmZ{M33wdUm2UM`y=k*-OR^pVwNif+&~dM<&;{<=_|e!HLa)@m5N7ND|)lrDpj?D z-QC&kx=~B-RqC{ZE$+@rPE(+#{d-X_X`Y@FFoKya*0)aTrdD0jN+oaML`6TC-Z||u zngMS;J(M*(l3Bf~7@F>q%oVgnL(djn#2(GaYY@Itg9&T&%baf33h5c08&x&>sZcB^ z86_u%BL%&ZKcH3e&d{vni|%k#$MaFa<*VSJyOupQ(C1U5Id_irIImAVl<&t z3wgzG2DL(2spJp}Tsonbitw-)Oet9f$`GGYOX;B{ZOI-L7(W8uy#SHYGhm@OQtmD( zmGrPu)9pR>VMd2+HQ=H2*6oU+LtSS$4(chTu7bj>)(i#FzIA9MqYf?C}V~5O#2eTsAnTRN(TX-YkanXSXhf&vrQR6kBe%vZ!kAcupzniaXk;EK0lQwhonOcLAwp4XML|4GqcFLOZ?!t>q^>uaL@d z55O^HSP)?i#_*gp@KNd0pB&IJeuXI8!i{Q)V(+ZUL;FVK(a(ouOP#Lt)7v zySekFyoNwL%u$*=@+Iie|bt+m0hs)0f@x4kg1Z;;Ms8uXxIh>jkA!QL9h}H9`n6 z&cLwY38lQ~+iaqsplX?}XcGv$k}KGRR;d=Cyt8;x$?L9j5G{t`FChvI4jZ z%?#{|I^(jQG^?&x;RIq_==5==mtIiKj>r1YLbBdl$`E*Yi z8rukmN6M_Qr4S7FYL#3;SG^WsD5v-a73^*(d6?1ZrWoZVMu&^2X?fE(2DynO@5ea3 z+oY17)%1ncrhrfHP;dk+k2<{Ft`7mr0AB@PNsq&||HafKx};U6QfAgfF*d5U?YZ=f{)l5p|PmskR`l*htc}LXFh~lQW4<}54EJNcm^Kk!A3RB zl>GbXN_vlmF3DS^=ks>iUP!OZy2!yw8^X*P*E%A>on-q??i=r937$k*ME z;2Kc#S!%@8to-eWjvQ(=iS~F@so-`{L>5`{^cz){t3DQDm5`xT5fx6?M@wGV&^-8kZ5ap*9=qJ}|A8{NfO&lvLj(V@FBJ zHhOV~o~xlNclqql_39xM^l+M$(IYY%FC#llC8w5j(dFD60FRAX zN0;ah$B?h*m4m+VQp2=rl+iMkN?tVB9miH)VBBR6atgHs-T7ho85NzST~%z+oF%vm zl+q(w%?om8W{RkDm;?|?cpNRFuS*+VyHGW8RiOPXiZod=YX>5GhUaPS94 z<4tlLF~QEjR>8gBO|h>h4Ayn|^9TLpJhF8)(Y41Ev@3Q@oa=P4F*TQFz1h zRvmJc#?_jKO7$_pTY&HKJf&hi?i{{iB>L_~=TA`(e=>z_ac}Z%3|r&&#f!0SW zkyE|6Bg#66!z?MdGJ3S$922ay|YqMJ!j0SxJY`jGppiZ!_yP5U3$sH#mo_k{ReKoxGuY> zIkf3YdMA2q+(;Zc3u*WaN`&SwZbazmQO2$>Z?PwvQB2=?p3CZ}U70$13f%i`ThD3! z_oBHPYBj=u{el;rz@n?Jcs2oILaWsp5R+!s*O`DKRnW^mI|8%E%6p#0=9abjT6)C9 zQK)#>l$r-Cbm5Dh^-=}X_wa-fJD!HNBBd>$EAy>jbhC%H^-M7adt>c1=yPdIhRVZjG#tTr~NXP}h@ z^T91cLs}zTP93UYC11!nkA3KG4eKD2Y8bW`^t}1v(bE|is14A-qEXTG*j$HkD=M;~ zvL@II;X*=qL@8y>8hcr51h>1Ou4R-`l{Lbe(o1@oNr;0{q@-$=MaUWztjn7PYP{)K z5L$N^M8}6WIx(>wYx8(Tv-jDdu(#rhF*SoQwP9E~J;4VGF{LlrPo6ss1YOluWSp04 zhtW7%qebh|PG;k{C00f5<#0mrJ#dz@1;sk(Vg@}bZ=zyQgXi4mPDq`rGhJJAxK&j2 zx?(uHPw<|eo;1tY)h02)$&q=&#G{UFe#Eik(5n&4HlXs%RL*9yc`wT$KpwyW%jxQp*w*0Pxb>Unxbbsl|ZaN_vVZCi4t&d^3E z3F`CEFxWz!$F9G^#*Jc~S6``@a4)c>MDEY)j?ZSbx{|Zsl;;#tYq@4l!JBo-d49(0 z6s`a;tYA2&>Q21Q!QAG#b;+u(+JEk%S z!>Ti-;@sF_mBQ2XBA*8=D!o{@;~DpL$7Fb(Myx8H0d^Z^`GC!9H=bIjtt3JQdo=6_ zBibVFDdF(}uCNjY=dA?k@ZG1PSwahimv+9zSo12LOemW3);+YsqU4Q-(XN}cwL`#X zTD~oL<3$y<+~Doe!EIO6h6nU1+$-=h>JiMU_2L3@FkJ7f5#Jehs5(AC_$X1P#!&rO zNnA<!A>!=`pm{KC&sb#LF#x<+ydHn5!ei>ds@e^ZU3h z9^}4XAGgJaJ=rDjpi^AjsV;Py3!UylXSmRrF7#ms8gw@q+~RIM=xsf?#ocgli@W3C z7I(`*Z{C~sW?g{>U4aH&fd*ZH28BSoQzMpOyDP;CUm)#Q_@OdCGvx4~=^*e?j-O+k zA-~!n19%HihtWUT(|FU*8wgQvY%o_iv*_?Od`(z#*$pGFS8+#oDUfq_E4SPl*T=@l^sx1PqIs zGsf2sm3=qTxreepvDr&doqP-?m>nS_%-}4T(9|Ul#v7(Hn8e9K!nOHKE_wxYxF+7Of)K|8fK4${fSb_kcnBTe(%e~ms>cTp zn|4Aq;53v02Qj4-R1Yqp=%HxY#8V0+sZlW(QO66qOFygP{TmVLd~_RHgfH9yzOLg@ z0w0yccgU^7cxzX*intdzNN9rRwz5kkKBRfWdDK~N#LE#%Zr#OfN6)3t>)u#f@V@eG zH!I3KKgKwl;ZBmc@4!Pc-zF$iUt_-yx>Nb=yVG;wwK) zfCS*MLCeU`j?OTJ$9%HnrkQY8HQ8bDdhRHcFo zo04HsRIK@;RI*l8rJSUKQUYBLKP73&f>n$TN*N2D#|{~bUa|OBuq){`{2as&;S!|b zm~a^!mu#LD?8%*p*FnWy2p<4mfqVky&SKvKn9te#4}g;*E!#9@tSH;?A~-p)0lu^Z zc~+&mOJBpT)RFil(1^VZXktP>;%N`vmW{BmAn_W07HpaY&^vGm@BtfNhMX%lY``vJ zF9Z8fOToVedU}OCO-m97re_FOrA7S7TW}3>YPM{o&e=Ro$Xu}ZC0uc7a^NfML!F$x zOi*XAp8QStj&UI15_k%EOz_UxGMJXfg#3gLKng*>2(H3+CV14bg7{^Nui(F8?ZgP= zj#XHSw#rz4@)n;AR?)xQHv@hd%NB&^VSVaJIFGeDFocElv`+>cGT=yG^Y^fx9J(Hj1AH(!mC0T10!Mto?L}Z7&G45sx=Zc_{Va#?p5qB&PxWk8aUEN)SD7B zo-^=I8EYAz8SKs<;(5-pkd%1BJlV7-;d%UFYzSDy6=6m*Z75*f(u**&3FCyY296F~ z1_kXyJo6HDBwVp$oA4q=Jg0=IyThL_<>;Mt&ajw4s3M4Z5O_=AA`HCC~5f(lXXP-E#yjaUxEY1$Q1V$ClDfeV-2^!`|k&EaD<_*D~W9k_^{9a!WO($J@3-xaK39TGI06SA#=Co9P$kHvSM#TIO4UXC)B5nYg`Ft@tF&oWpZRI0wG817V&h z2d`OH7kK77#u90qo?O7D%d-dzI%^r7v;%4AOU5%{T9Y~oSoH0LX-C?DFf+HawuTiv zQ^fNOGKK}r`;35jH)P!*ehGhgE(kmQp@5nBh-WTk{7D##TODmBY9^}&VP-VpNA^C{ zm+`=S%z8oote{o+i$3K|kUs*}z?(Nt;#s$aZL8qIzMnEMs_A#eyvUNGM`C6cyAe-I zv4Rt3Pa^tK)^l=azNIWY6@upyev96|3NJ858I$79C-OrTUf_+Ka`L83k5aE1esgE$ zNk$~k1MR^4N=fMpR!Qbep5GcM88g(8Jrw&b+Jq6D!>+U~XhXt`4TnD^a^z&y zcjR0K#4}6!W&AJ>2_MAY4h^%Bun+I8>XNP6B<*i}0n$3gIAEu?(RBchWOn(_>((n-=5%$cAPqmvs(r!1I5OF{?h$vQv2&OXTXDIG%Jy zx5!W;x3dQzKgJQ)GOl@=m}i-5m@CA)Yoo@;Nwo8!INEmzYxweTqBM%NJdfPp!%kt$ zUYcjb!_Gl2_GsMO!=`}a?UK3t(7l9@JlZ+%msxF_{KQJS=Pu{#qS{qk{Vp`-AVe{LAu zn5#rJJ=7NC*n^dy)yb2ePyv$g?MY-;Z;rO$y^Xm^#H}|sVNr88#$ls=j2Xs}c*isj z({B{?yrYQj=TP05D_Iq(d*j>`u&k1zGi;pOi~YnaDl_<@x{t!H;xtj3LuY3(%QqF# zQF%I!Td~BmMmwmmE!X-j&M0E9z&B?RuZ{6HhLz&AQh2LzmUiNv+`;dYF-!Zrl$IuJ z$Ui%0NlK4#Ur}`qjqYujWd`C|kfcLrXDqH<&x&=ZHAXD1j6O!h;qxi%&o~mYAL^r> z*1nu$k0$a%qt+wVx#0QasX3fKeI?#lL}ziRe6%U8J&)h)LJplD2cB^$a=_v9Q{c~- zWyiOge$$rzj6a^v!`IIM&(4T9&(-#478LbgboxWkh8-C*x5z!)F`{S4h5YG%YAp0( z-kP!I#miD-oD1tQ4~fV*+`FU@FLkuT>ip-B1z0`A+svWyL5cWID5Peua;S~BVKqBQ z>f~gpMlUnAX~uVpFv ziqXzm>z(i2mWCo$6-!oeH(1RhqnPL2PvorCD0pglV#HnVP@9S~%{;+VA@XITq|CO= zvW#7Rhhpp<%7tT|nzS{u`uQ1xCD>~)3Q4`%8K-3(E|h1rb0}=fD#qTQ`Q&gP?g5^= zu@_*kvRby8fYsPHiyZ3MuTl3|*p>1OTQUodhwsL^$lDBif8G+AlMkob3yGOI*e}oq zymgbxiA3iVGVAj_ijt9^h`__oB6|qlYnV|8k=Nl=lw4FUT4Xi3D18+=(m(VHrC-ga zQ^526msU-qJEbn7W{WS>B8P|`A&(i}RrpnrkmwfC{uyAZFv$G-;$f5!H+@IjiN&VeCig9SgAoTirUMIx&{0i_lz1yShg7 z3%gQs=L!)wk`%$6;dJS2MBJFvE4AZKmvp3b88kES_vfU4mR^+JmD&Qw2F?nM1@;9> zfhz)E2z)*8c;LCfZv*cJT7sK`#|O^}ZV%1|v%v$wOM-tN{B-aO!LJ1$3O*BjIrv8K zFTu7@Z)i*CzlWwme;qPHmxVqRx-<06(37F(Lca~Y9SVfkg!{uE4v&S;50}Cp311t& zIebs}9C<|EA)}6ygOOxpW8|+QQ}T@b(eCete;EFG_~r2V@`bW0 zmt{j9ZLP~q1Ai-jLjG`MJhCrxg?zPqgZvq}5Lt>`9=RcMt9*z2W%+CJ2VLKk-wfXt zxhwLG$hRX;MqY@#8u@)BB)7`l^3n2X@^wA$h98vW$lJ+KQ@ZU}Ex&3xs(DS*rq9M@+y1TjO7QqXb7$-GO=k1eEw?m%xA}~g zkF=cCdP~zMS{GWP%}+G7CBKl|+w>0!@#oeA|Gb*~YVz&oSCg+M9sEBeW|F^cx~a8_ zJwMUBEjiWtK+{G@I@a=UkS@}4E#$i?+0pzTEjJ}U)}%J&lUtMTHf?LZH4$mOD*5&1 z-Oy=Y%a2;d+f-=ubnDZt|Ixyos!g{heh_#ko$;`qcliG7KKiR%(~Cmu_@ zlz2Z8OKwOWlRPK6EBV*SQu1TT|CPKwd2jN&$zLSjO#WxGwP|zHmZo!?W|}fh2b(_G z^qHo+o9=J=e$y|T-e?Lnw>J+opVB-E9ku4aX}-4kbIo6Aex&)?=2x2EYfiRoYB|2; z?3Rg^i(0BJSGC;H@^H(uEpN8OS~s;G-+FfIWb6J`t@RVFH?{s_>wT?{xBjB__pMS} zTU%e-iEYDesW!FEZ2P;m8{7V=?VsD8YJ0Km&9=~*b!(1UbNZScYxb_mt*NcKZp|0h zJhA2%YhGLP!J0Mgo7>N9pKi~zFSTFQerx-E?T@rS)BdmR@3be^_N*OTJF<57+J&_T z*Iu*s*0uMp{r1`)uYG0hpVzjn>s@#9y5V)R>oV(V>;7)tP3!Jl_rSUztb1|YyX%_Q z_pLv9{pk9=>kI4a>#tgW>-v9M|Mm3`uYYp=&({Cz`rohr%lbq|XUFD_6FSc7*wK;d zxVWRzQSZ2-{36+|fDPsdQF4KiYX^ z=Vv;<)OmmBW1UZTzS#MC=ewQZt~Fgpbe-CDPS;r1`Ca+0rLK>4UE6g_*WF#;?0Tf@ z>8_W${=MtHuGog|4aaOaZNu<}=?xcbnBQ<LlQyEiii*zns8?`=qI z+_>?CjpuCKwK21?zVYgfpWS%x#z!{(eB)~y|FkjL-P3(y_el5N?tJ&IBlaF~u=~>P zE4x3{eOve4-Cys1sQY`}KkojQ?pM2i-~FfVNKac&chAv1TYAp!+1@kLv%e?TQ|UR_ zb6L;TJva2+*7KE~hkKsx`DM@Vdj8VWx~Xr|Nt@2yG_&dAO~$56H~r5|pWF16O^W+tS<9JJ@?x?^N#vy@lR-@8!KW^xockPw)TjeX{rY-dB3x>XrK1`v&^9 z^o{hL*LP8$-gimg)qS7s`%>Tiec$W*S>G#tf9#9&ukAmo|CIh6{k!|;`>XvQ>%YGL zmi~MCAL@UqKYc|0h=1vSt^ZH`ZJYZypSbzIZQivxy}7jcZ#G}M`PR)}+x+n6r#HX6 z`R&c&fsTQr2F@7RK9Cwv1`Z5dI&l5KZ3AB!czEEa1FsIeGY~mq-4RD0amo?r9`T#t zJE0px`$Lz69*jI2To;}RH_0yqYXKv4P2}UDjp0P>)3M=@96A{KAQ}!n7Qa8WGOZ;|0woC zY#24-rocVYW73n-bJG3NGpGya1U5!r4~)q(fm;G?!A}N1A7~1_9J^V%CU|e4Oa6HL zPU*E!PvkeTcKN~J?NWQFBY0OJB}J1rph6rey%C!Z^#s2W_)zRa!S4i42nNHa2G5PX z80rs?hUY_j!drt~pzKO0NM$Kt@m*?Y?!9PUa4ICSv4W1rAFNuFOmPRu9DiCW?#iC@M06UQV@NSu~9JMo+N z@8a*o{}K-;l8N@jhD01a$CdK2(wWgS!zV}24!s+_FmQad7XEVL(~+^z_2EB<-VEIw zx+#2F>^tE-(c5B$Xe|1A^tsSSBiDuAkK7(g1aF8`qSwh!hHi_T9=a^@KsXZ($HoI! zN52^RR-_PkK3WQmNjF8m8oMZRF#ejf6#0DgHMtPJBldFej>zZ3M}{5^eh{9FJr$K@ zNxmX*)w9+5A@|4-b<@pCzT?(+Tb{{tUn#^d?L1uu?=+WK~dX@zMm8vI;1c#6_{7C@WxZYcw$?CaxthmguU{ z*j-B$V_SRIRid$y)g)?+nzc97-#If}*=EX@@Be@Aeedl$JNHgGbLRA!xfjLcVx%X* zkC1fkTb3wb@UHX=m6_pGj-Ajh_EhFWn z1w-I>l)P_tzkX$X%h`2ift(Z<4H;BZFD>octSRYNG^C)o^RVK+HCF{A*hRO#1*Hs* zw4|&Y8fFy?2FTd5j3i`{gdCEPOA@+}#L|M%Ma3j3I%QZXNh%mpP*^~c1{9PJC>RWR z5dcma*1LeDHSFCyuHC+Sd9S_oN28p-I}yI=@RBD3$vqCO=g zv!uANWOxC|>Q1BuFl>3zkiKwLT0n9}l>(rgQDxD8Z6Z|HTEcneL-Ql+* z{cR8R*b<>7kqr1vC5iBxPLk<0mSoaiQj!bTd33BqTAM_=K}ibqz+Z%rP9b!leC-H# z*f$M+rKCOl=2Cp}=!jh?%&xRo0xe0T@RMpFW8Wk?mt=sA@RR5-&Zs@froVWd2J^si zu}wOKpH5+RpfEeq@{Y7TgJPRW;b+p?ObREnrW|RKMN6_M{!%(GDTObk^O90HQVKtZ zQYnYF$)(V8Nmqc7N2!`XM@gWgC(yYk(3T05CJ7W;0;NC#9WjA)fxZb8LP8C{5TC>v zY9mJyDYQfiEs;V?q_j<>FcV1!+6MWRSVK?bbs~kINRa!96!SznN+O+gBE>d|(l&`g zPNI;LDC8tM_aq7}iNZ;u6iA}jCQ&#^6x$>UCz;}qOnW8MUdgmqGObOa_@_|F?J3Uf zY5VrHeJY)4Dy3U0ZJA2(Nu~It(q5^wS1N^^N~xDhIh9Ihmr8LZ_!$)c44^Rq z?xL(_&|Vo7=L|Z>3_5xS#V~`8oKuND9kJhCyPSJrgN0izEavtN@pRhDMyKt zQmRVntUJ+`ooUO?lwzGJKAmYBu&B^Jht4a9ayEyy%%L!IC=NMP26E^eb0|JJbgUdo z=Nt+%hhmsRA?MIJ=1{52p*Vnbgi&%S{9HQ6Tsp^GI!Z2upG)EAQuw(PaxR6OOQGdb z2zj(u9);P3V%~*f-i6}ah1PbVICr76>PpAzMr*rKiRw;!b*K1urxcJ7326e@B|$w} zPqYGQid!(RyMz z>LANmeOe71w4YdxwiELXH91Nn-lLRZj*`T3b}y|N4=p1~8s=D!HWG7`2`tAP?KJWb z%ah13(wh{(RXQn!dg*ltXzMloY{L5nOkGYdItwB?C%&Ln|C72l7(5$Ll0oUqA}!T_(UD2{nCS zbfh-oH5%@V;SPOn0x7HMmkw80+n3gLfcB*n29A{l<-;Kjq)?>rTMTzYpdRHF=Q0TT zje~OqvxI>J2Y%<^u`7s1qAm*7A*FyM3{A=N{@g)W&-C-WZkdWfow?E8@<;YPS zuN-Ro0~DkkMlCUr3Sgck6f$xhIf=6`g8qisv(Xv?hfOhvre!=AVt`78&=WDQElUK* zgD7UTWyzF>C=<11snBjT#ih0k=Q5n~skST|a4rLghJCYPer#qu#d(}j>rKm=S}1E~ zp)A%yS)7Hk<`&9YSSX9PP}b6{ESg#>W1OQwzf9*5P0VqQCgwOt6LXxSi8;>E#2n{n zVvch(L7bTcB~aN#ZlH9sGSv7Hkc!}I$fE?{+h8a+mW~866eO;|L_U~h!E!?g$#CBX zzNV!EpoC#iD<#PQ5jDvehmKIv8){GoYTIT)DMCdXV_u3nRa@E#+96(O|7uHfKj2p9$^DD4mSkcZRm;O$)&%8@5Lq zUj%la`IB1ujzWqF@&E-=`a|y*q9F8C! z`Wu(?vSM1^722W<)mp$jpv@piB|tl*GmA({AiC%a^3xG;=?i|tv^*Q26hj}=a-HRh!r?UnWwS4p9M+RB&jd()C_Qmh#Gh#e zdV7>9v@cA4OyReO@_~?$1BRM`6h)dcSuw1^cnam0*&yQ@hMRdGw6kbWndTVw$$(Pi zX_*-Y7)PPJvKpjXtv+K^fDjRD)0%QRl6eirhp5-)HCZq|<4UbGV~o)sD2n4?RA*en zUqfgp$p{JQW^AvrV5S&# z^YL{pMLW~BCM_F&C7IcD^by?v4vTP2 zwHCD><=tHWakj-Z)G@XgsF6cz_+wmy8io zqpG>>NC3;hpqXlYD!{@>-MkNSX&BJSye1d0MhcnNbOl%_zox4lw9B|EjI~gcL~Af+ zG+rUa08OwDMwq5GY^{nhrD+XYm11ORTGNd}GmeSTQ{b9n1k4rn0r|kMy3pbbhuTu$ z5Aqge+t|XOKFA0-&pu}B2xz~*jPhdP3Kk5n)nFM~ov ze`h{FtV2xD%9!?J+J*78`P{Ki4t;UuYpGuu<)QifP`;6(Y;|tR+XR>!zkgv`ht$U1 z35KV!Okhlh5`h|J-me6DAwK4H7?&cCaOG~=F9&+n`ZC@tpsX-k-5W45j@Zzi6@x@0 zRZJ~*GSs0QVWfu?$MJZWxUxa0XbnxH*mS5tSXg7~yU^QUgou3LWtWGEUZtFlhd^W!dMz*#t;|eE6M_sPh)@733$z8gCiUE!>Du+g^Lns?0Ir(#zTK@x_-;0V=@1W zagrfSj9iOJFQC+LfQi<>mS%YrlW5ops?}ShqOmQ&o#J%b&%7=REEDeM8vB3*fPj%8 z`VP}Nl(JH)VdfDkTb=TAH-_1P_CpConinI0JGVhlQ7-AoO{|P0PkLtTX z9a0)`H60Iq8uJ0>cy@=n{!okSFVlXwYsTVK^ed*m8s(3Vc1`Or(!*Ufq?xI|#@&ly z@U`T9Q@|&nuHoEG`(;55;)D8UT8H;&F$qS-H52Ln{txAk>Mqh_o z$VVNPbp#=!*^L`Vr&>NwpK$$gOd!ijcuJ)(9Bc^LFW+kw65FOWe zWrimOI1jY1%!?Y1%kS~`g1rtlf?+N5AlMIejE{48oSFV%B#m|my|-astVN%OW1GhU zc#?pA7o`~~WxSr1f-Ev@m`obas1wwVhJ>qRb~a-;Zmq{ajBsSbTHf~aBQp+`Q;W#( z!BZHd8CqmkYwp8q_mPa(lBPSC#)F!x!b*JW{~*DN?czYM9odRHH{?bT+L;F5wyld12lc zYmpX+mw7ElJdDF=MJ?^BvuGFNAyfO@5$Ie3=c>Jcds!ChaixIz&m_crY_wd=N?<$l zRXo32fX@)&+S)WG;r9$u-crx+9N;rTmiqJi2hy5);}{_k=z{cS`}xck7{(vTAPIQ( zj9g`uH}0PT{c#n6^XI+2VgL5P72J_P&11XI#{E;FCqhSAV{xo;JzG!Vc`TDF>j)j!Ip+0QAj_y_=o@)&W(Xf;vIJ(B3%}UL6n-v1W&8Yt07vsR zc~^=bo-?4OwG=y`)kDgn?_&7jI4sV_Jr}f~DA{O#EX4p=KZ;_{)6cX&Y7O?r=-gbs zG5$mkgQq-}$43sL&1F`~R6p>k2p&I6@G&OCl_TyHur;VLeyLDX2sJD!Fvl+y`ZC^F zj&jlfYqZ_y8Ae)|UpmAS*aD+UOXH)pL(adA{#cLPKDqXF5JsR_?GK=?LZ2d_s^yp%4T4xURoo2=%Xyrr!$O;nuBw+#6Og9^ahMd=HsJg zFzrPNd0Rc!@%wJZ^r_v`Mk|T^`Php^glK(`e~2Ta4_}WnL)$!5(cWjNZHLXYKM7xdaIJBguaTSZYa7O^+b+muRkrg}H#Z?CG zCn5bz>xV%<=J74XZHX{C@)IL_SwGXbd0iKh7QZUxov%O#7pDV0~H5O4_tO4`d7XG?|WIoSqJt7)OZ3^d5KSk$$Fe z4@wtC`FMVUb@o5mmfIR`!+oD1Wd*7K(j z-D>KMpJa$OnlH0YOxj98Bhc@mXNm#Hs6QB^p#~ayV3ZKFKg`P_txa2?4CC$$QWYZs z=7o)|OajahEj&i+mhBMQ9n=I|^;)*Xs7pmaLwrt!w?)RIpq;_z84wq=s)(s^i)1Jt z3{cP-Pz3eb zG9Jvcp>M~X4@>7sSx_?q>~A-KfakjAqjZKAxSNCinXQpbDTW%zOANm{H>N4y0^>W> zH1kn#mmIw;%2*$Zc;vw7z2I3IrVExh$68>#iL1G{x4=^$lp#yJWzit=9<`0F`1!dR zY6IGE-kzc4qIEXbmt-ixh?e1DsRimO#@q}K-e=-oNH6daNRM8yrbJ0J_L?1GBpe+* zEAC9-o~7~51zHreg$NVvj(H2_d693Z3n*m>2l>L|(S>RuYAl0?G|d5AsnPBu1^M0xn{dU2x;bYo{)!~RZb4zwU5g_26Kkme$Tv=*^6v7&mIkfGT+u(dLqQSi{{58k9ea*}m zFD$P?Qz;kll!2{I<7Yi;FFJ z(@YozC7DG_xQAp34oW+-hbTvu=7rG#`aG0j)CtS+0eb&j%6XibC46un7^MScjXgz! z_*nA1I6K2LDobq;8?$=?PCZ5K)p?-_aDiBG{Y>i2>-Lr^!^Qwj)&Z^O$tJ}ovH zu*5wWW*?dFN4;gw;URV?$4G7D3G&O>hB7aOt?-FZc1mX226vG#reS`|@>&jY9SLnv zmywEiMq~-gDr3chC5{Db}R+pW8{D_ zJK}@#V`}SgeTB1@Lqb1-vSHeWVauZBGH7S14fetQOcL?R98PCwgBUZ*jB6V6Hn~ub zYZ<0N$a#K8JDtvkKW)Odk&vJ4eI1kyLz|fZ^JeQM+`UEHipjJUAD^IAdAp6y1{`p1 zY}JGB%^-IXma&CF8DZA27ra~bw)GEh&w9ZVBxpxaGR89L94x_X) z8Df^x9D9^e<|nZQvq9#qx`GB{Y=zMz>NL&*TUoNKnE(f$`)7OFZ)?>V;NX54QiI7o zZ#i?Iq%Y7BB@UU zQ!Pq?F?)d}$FcG06x>&05reVj)>_Rh%FnkwA%pg*3?L@ZIk5GNrDz9j5C1MH?ib*E zEJas1d!z<(3FB*ApD^h*#?R1ZbfFlcCL>);+p+yz^nkpVu+$DcCGufZO{?07%otuS zjD;&V+>t>KRZRW3`M4PSA?~OHr2sP#+FF_~YBcU3;?4@bQ-kMDOlxZQXJQ~KV?G+S z7%eKkyNgZI4K2jk(%GGHOT-r~ zlqG$ZQt3t>q3+|@$R|rJQM1@t37(wc2pIENiWZPBjPKa44DNbEOH1p$9N;y|F0*y` z);QBO)Y!26$_c7FZpog$zwa{{) zy+F%`Pa@;&Fxs-j|Js=`ZLK_ z4K0U(B{HNk!Zx+~hWMreF35L$qLM{A#x@4^m941RiUBP+K5>etVwSjVsXeoysQ>0O zHI<}HXvHjqWloy5XQvuyF;QzxPnh{A4A-M?^CqUUVrd3UH!%L`ZE+Ts`k*I7zP;_) z3`>3R-AsIP8E0cj<}LNf15IN4bCx-1N->N|FpBu2$jY=2#=86(iA9abNlUtb?@Qon z7VDDXX}3}%OZHc2odI)a(N_swXPC>3B`Pp$fTw6^xBn{b_+Gd>WZ8F_QbG#UV|#vo z6ggYM5N$E)0P^;2d# zq0L5%W63MMeYPx?<)cK)C&ZSF3ZDx5)oE+L zJ7`#)UmPDe3Y=V>f}9#U?Q=g9O}Z7^Vm=)$z!EcM8GdWEdp{p`gmyS zjJGfI_{2Uvpt;W^j~)T%>&4WIi*$*Y9lkFjGU7?SHQ`ke&ms~cdPHn>O7+S2`O^M? zQ+9ZfPo>kTI$@!M1FGs=b&3xy3(E|>Q)iaPgn*?U)pa&`%m|now$I~fP^E{H>rWoN zL;YPxghshO@EGm$iO&L`nc}rR%f(K?O9Q?Qu<|?<5FG5`*(CTvz;6NZu3?_3uF1jM z>rD&YAMROiSj3{R=@FO17l(FnZRXj>{dcFnt{v<3ub1QAHMrb$nf-U-fx+K~O>#~0 zoavh5IWG8Rz%HLYp00t5U6sKn#ixAAJnIH74c7T=30Aw_^O@wi(SDZaq#b)g=<;l5YHngu=zjq`o! z^dzjkZ&%*|zA1si5SQ?#b&oix&j!Tic23cKWsut+sY?NOJaf=;Yj9RB4kZ zigbu|C=~T_9_*apaME{CV3tFmU6swRHa#51IM=s}wHx5DA@E1vZ>@J*hk2EW-WNTz znd&@4G|Yjs%dnf{tP;JpS?1iePF6_1-6rSv9SZICILqy(J2={U*%pN8eVciWwL9g! zUL^2a;P8=MdB{qKBrkWr9IyB5_VL;!ni3M`H!oyd_}Y+IzaJbmA(bI5!>@;=_;v9s z^Xl(+#ravt7j;+F{mprj*DSA)u%%uj{hY-i;uE6SuxcT(nr%1LPi42p&f3b}%GpX} z^@;m=;U=%kqKDzXiS~Kzs{34YrtTJpeSuBIGAmCjU#mbXvDJL{Ppq7*pIVhzAF&qO zHneSSTWNj8`iAvA>lfBGHm){)HlwV=Y!YlUY`fa_wjFF6ZPUUg!KR~4o=q>CBAYUs zcfL1YL0PwQfx8t*fodtjAfm zvEJYqZq>oMzEvYDvGw)3H(Zka-5ejemR@>c!9Y69<4=Z!GsqS)rQXl$HgsI&zukY3c&FPH|Ko0bgD$y+)QNZ8%TQ_j>O0gHjwjJI1*a`-@gN*11;sRz0nH*6HO~>0RtN$$gaLEcf&}A382| zpXIp8UFGv1;D_9Ja4**@_8%yx>++8|&1aQnHo$?iR^t*v)E-V~Nt-4>=; zj~2QR7vcf$h6o_)A1Sya>?#;b#*$}(27>W|S%NykXo0PuJ-H!BCldujh2ID}3YrVv z6$}yv3r-7<2}cW83A+iU!oeg+SR|M(`~@uRN#PX1N>U~GP*^U!B$!R63Vnn^;Y;Em z_*Ez;^Tn@|AbT_zWZ2*!Tf_cxLtw1ajc;!U2wZ@9n?ep?Cz&;zf$$rhmpDHR-q z5^pH+AZ=)iL_sXvVNU_+L`%jAu^u7W!TbAm#KyWVpX+aLsQ5)b%#leO?`BMbnn*q980H;IAr5ddM4-~#j( zk?o`*w7{9LUk~Ujg0ftJ2(ZLn&Vb!c!70HG;U?%im(B}kvJLJoKtlL-#E*VmYH~jk zQghW;ct99RHV7O^D0~mWH7Y zuaW+InWO=`=G$XAj}Kf@-WlS{6w z2kWrE;oK00w@De_p2wf3H8gtz->{5tkGS&sVrjm78Q&h^|6Tdv`GqjBKR;ib6JLho zpndzh@D1tD^Tv?=sMGi{#NP+*krw>i4DsR1uzfI5l3C;nGM6lXxMC$yg9q3}_K|}S zF`Oai$rbW5xj}v>kI5?{5I6}$0&hXEARJ=WXn{nqY8Cu#e%6 z6w*0J*C5@6^c<3{57D_p3Wn4GQgcX&kg_0khtv;JDWvg`K8ExKq$QBnLHZ8TK}csH zAsuZXVUBjd3lhpp03D6@y$ZY*Vt*7!3@vA(nom+ZDk+J!x^hC}v zqWJu{mc*UFx#I`tArStp?F@Y^iL9eq`!= zd7~fZ%V+FZpik3$C4V(_p}v7?k$#y>rFT{=*1I=eqR$w;R6jB9Yq@O&q}K-(-{l`t zIb1v>ik*3ikdL(@@6yxq@T@a(yN=(J%9Fq7)jc2R_ly+k4<}iXA5OXGlh@WE-AB5L zx(yJ?lZLx168m|G#`f|gy|TSf*H8y_>4yig*{@m^3T0L%)3>&PufQ5h@j@uD2L404 z%Lu^agL>1XrW~*FgE=N^poO$Xe#(IkUqG4eJGU-=dOH{`gcXdD82srjC-V8_xp1HvImN3tslx4VwF2GVrSc(Z&wcbu<$j7Wm;Cf?9{MZDt^m3GY#@nU z6r^9eC0Nm45hDL#j+o3Zs4MR|I+Tc$!{m{@!xcFZ5%PU)>X9k7k^09$^%dXUsIMPG z8YtQwZ6IHN6_zNg8|km@jaDq5)>!AEiqSd`lIR{yXrjH_p{eeNUd^=H2C=$r?c%f> z9h=L})o&p^^`M2U!oH<+LuE_Zyqm40vo^JsjXm5(I&4;3*?TMAk#;R>ryD(k)0Xv2 z(DfXesLgAWqy_iy-w(srghtzru$i+F5UKN2bthvNA1pn3|;=z z4C(W)Gj#F8aWB@su}n)&fE=HOB=sY@96ur|hx|S0v*1~$FBG1y56ai%A5z@8a7f>D@fq@U+$DYF z-Ai0)#0&ZFdtWFf53<(}K4?#zZ#wBy3!D}0j=0G0e^y7)snS)xG*U#CZ}r!g_y#Mk zC#1uNroUA6{@6~c zW9K`m7TR=Hl@)hZjc$>ns=F>n-2QToDla2f)zc?WtQ(uBvR<91YSE~R>YXQDR8w-g ziq}l+D$e!FSBZbfS3MMWQ|07$6E}a4IFP77Z0YTVJXQsw!2fJ}eg>*j%o1$sMjL z{$#lL+Xf?57xG7nL!XWkPmCF@I&yN1_-^$W@z7=CRG(Ll6IYc^5Vu`50qqge-%Nf? z4dy?RkyWcOruq4)xj*6UC{(|(d6PgUegfI3hP)w^%ZHs?C>yePk^Fa?RbpfW0Y$P zC87)OHW6(uZOZE<`L_9Zq%D3L!5qx!i}bXzw)n-hi#04ZAT##t6ZXQ0}53Vcb06RQvZ8)7BlAmn=M?81mUkd8hHG zcvNA72c*;qyoK{RbHT5~Qn;ZU8>FAPZ75Jvto1q`Yk@Kc3s9+4=-SzApt}b z6+m=hNTmNJ^OgSmv>W2{=kpa%c~hX^z{ixwrt@t9^Xvih?F%{DD~z2*ko+Lwc;S#D zAVosL?V6^L;vnH#O!}bHVqVrTH(Z}dPeSS>C@ur!~*KoWUFvfgHi(tIPkfXjVDDI^i()}n% zN9_m8`FQ^4M@(O5*5pt{KfIo<1vr}k-nWn=&0q_N?j0ZvaSf0AC}K#BA>seXFb$Bk zf&uIxIYYuVZ1u&v8?^KGZa$wn|JF;T}G$CW-S zPZ00DCq(MwCrI3qlepH_{WO)XwPoH}XY@ZFa*?l@O4nU-|8kMO)zVOX{Wf8GzeeGD z$3VJXl1JJ^>RrkciK0!C@o- zO%*rVH&a%-#VU@}i&IwJi&Lz&ZK2ehZlU<}dc1PUhL(zwgRPX4XS62M*0)ji8`_o( znf{JwQulU5TEdC?wn`vPvlAhpN+O;y$)c#|$>edJ_9CZ??G^4%Qzs1Q7;&CR`CA@&mvj}C z9_vcl>GDOntGmhi-R;KHb7aR_AB}d@itSn$daOUahAj@d$6=3#8a}3S_BQ`+sB@nK zosU8~1vIaM9Bo5uNK!}x09nkn$y1MU(tcN079;7{!|Pel&WU)MTlF6te%7j#bClMBw;?HU*E!nQin^DAAs z?Q=!a@20wOUyX8?E-3QgCUo{@`P z>6t*a*WMtF=f+_5lf@xi>Tt38!o<29j|(l?;1pE99~CC z7C1&}eyfU-jK9@Db7EIR^}NbPn)M5#)nhj`*31|mqkeCegj?6YiJB{G$|+NusY7#O zxzfluwRN-RT&`UUN!`GBuKA64iB)w=&i7C&$uE~$Yhu-HB!{-Q)p#r4k*u8GPV=;c zQy-X|pt+cxsQ#ufNwXs+Sv@s5h5P+Mie!0oD%wL_+n;{9w;ks1b)Pbh*Cot5L;auX z6W!p#O|HNCWwLQ?H2{ZC@9xb0O8eI4C@-=yc8@vU+t;i=a2J)|2ZOo^;I?GvVGVL0 zWaKfV)F7hE4uW_Aa+DdIV;e~QYh*^#eCjbtV!;`%PsJ-q`xURblrM>-{zn2Xa->k5 zJk&~)RA8;H*U3f`kzlK~Z(^tE^{JiumdJs-wAMj=T<@qkrgM_~dcj$+vv_pjCN3VL~H4P%w zFJ488KXZ&yU#W@`%WpMM@7~o=HLbFddhx<&)sRh%)sx1@sHC$b5>@{us-|U4CGV#; zQ+ejZN(v+6RF9iBm$bKQA@&H2m(;%zFMjl*rNr@2EAbDPTC3}++laSqZ>zRfz9XJL zzn%J438&gRIYE6qJ5e>aFiE{GCRsHmIYn~yL5lcGbD2p?sFfL%7jwsBVlMu&obd9* zd6@w6F$vQ5kS>9|+=ILsz?YP-`a+lPTA|`z zR!Cy+Z4;MQeJ5ELe^K)Mw~OklewW4Fwq2HZy|^N-nqMWEcjZTMvs|6}^_+*|_iP?X z3N?>a!F8TU8g+UqK5*!%`i$dq)zcNvCAPO;sK$L#E&1$7km`q#!Rk0wi0V``v7~5X zUDaWyP_-yOOf0_^roP`ILVW1^2+1ggM3ouTL@n;xR6NkOnItzpR=nqFtRzI-Ts7cS zbM?W;EyN#G#7lnqv8Aff^j7NEhg+-al(dn|*xy#A&w5AFcal`qQrrpW^QZB+348eX zOM8aVQ+u{HN4oOyrBg7`wF@SqOvq8s(0_1{@PEieeUnU|xrFOHW{diW|5k2#aD_zv zeT8P6&v)vj-+sq!c)DHk!xuX=KVR6Xb{x5j^WDB%J-xez`|7Jbl6|fBYP93`sUHXK z=d5K1Bn@62(6mT6Bw4RJ#Lb>~SiLy&sOH1FM3ROM?UCHF{_s+BrQQmJbo?mD+2(q7$Qcf8tR zM5=hgz?c^ldm=I)HVD{0NfHG^@8-D#4ql(WqwKSZRa#jXE~m4D6i=A8m<4Q z{1PIktgP{P2N;i`b?)AOonHrlKSvK7x-gsgNCfupiz6a=kM7w!*yIW@T8R(Vg)e~Ng+oW zY7c2Kr2hmv#>M(Ys`s6U)bxK|U!A6o;;x#+2_9-r^G=rp-k$L~WW8}g>Aw;m&~Txa zJyeIr?*4oHF^mrifhYYTeGTaw;86wS{|S8gv-s_8`cnve`M1Px2Jt}P-{g;>{+s}Q zIMyXP*SbU`hWtOl-|)P7TdZcPE8qVEe*^Z+$lt8_oBqa-7qLM95=dphi%F3GZ}Bzi zMc;9p#LCQu?buMOE2uO7E`PKCALz>*;LBM^mw-pN{}Xt$>}A9o9}|#T|IPZ-fG4Wz zS*hyIm8snDdui&}P3c_z=?=V~*=(@B`B(S@{)R1Xa8teJ*|p>UARd5&Gu=Col0u0n z7q1}>_>YL!V6Wh9_6&Uucly|ywI{!(;d%6RjeTnewD`O6^c(xe!ZsQYT>b|(ZUFG) z8%V2xKRY1*Ul9vrZL_#H@R5mq`&Z&M8cdtSYft}yebWL@T*8RXBaDc`A;)tNL%S?HL`M%I;qf&o1N#bKHT1e^NaVC zR7ZMgGW@*NF%CYQ;<=Bc&n;igrb~X3o5%e%@w)=l0qX*};zdD{?CHUpMT!vh`*JZ? zSx{GPmlMk6Cx=N|$A)vQ6C%_@yy|hgZ6hU19@8}>#=)AMry58cwl(BduWqDvmPc!< zrZraI&5q&Rhe#v?nm5rj?%Gtn(Y2XI)FM`L>S?UzzISu=Z&#aZzK8v_6MNz{Th6sq zuV37XtK8jMvSoc6jdEyP$(PgL;nsF&$mV|3L|!HS25k@7}g=XZz;aD)xLhw3{%{ipZ+qTq7QM{0Z<{1v%P_Y)FNW@LWT& zxMHFF*>{U%U7sG-I~1JZ?MdO}yEKNifi^fBe24(?2wcV1398k#Iqn~-x3PP}b-^YW zZwsXDkaolPdm%^p4;=L0V1FF9<98CTXjzxpRg7!n!y^>Ru<*w}&3E`=t8BbFcVbrW%xT)6obAkqxon3y?S~xS zb5UP-Z`V)Eb&4P&{|KT(J1+a6Vxja3*zs#09n;>Cy|LrYE!!GBzr@>fe8&i7v;3%K z*)&|U)`u~oAvJ^XnnPX}`spUD`a<8;wL+csvVu#zw@vbX)py*E_=}wV?Teb<{Vq%T zZ@bKey|^N|J->=uapgxzLat8Z@+CW4E7d$!H>&f5Yt@OKt!Z?Q&(*dopL3qKU#LI% zq?%iBBuM?s$Y4#9DnxyynV1_fv99`@Qz-g%$*hWa?%t0r)j_|uM0z7Ge@gF~xrzcwQpU%l}eZX4QaucUMCk3qM#6<5AivzhkuHeIMqA@Wuh0 zNdWHy$h$#5Roe1}O0VsUR1H24P+yUwgP$T&=&!7I-(TTyEkJ31CQ$L)o*?C|4Z(^Ni$g>~!^MjA6YGjz=Y^8v z1H(jDlr_2XmAIQ$J+-{5#aYd6r-X$jEPyeg}E6tH;)~iWB!2^YS6@9`i*1LH(!y-T6me zU60ovebG|-t97e?m4{5z*e}~}`0Ek1wT&%C4hG*9C8JW5Ig$6z$OOghe^g?3)7JJ2+7&+Be(f~s zMRK?nNx72tzvgm&VO=Ekk96T)2=gV5EAzQ4*Sbk=f7D$wq^gJH8^Jr>a_PX zO&9i3A8#V#vZoZN*SYoPn)L4@Irh8{=aJf1U3$7N_o!Zfb?%1#+z+-zlA-g8xNX-5 zN^(XI(wse2EQ#wrShIEg5Q$H#63yJ{a>+Blp_(xzrIJg}N;U6hmrHhDDCb7U4_9B< zJDlt3J5us^^+@iWr=!$gy+4{0pC6;1(0eQ=tQaR*&~ZHX^PCCjhw&pB-%x?MhD@CJ zKI8_%XegNfKVCCme;i(trFa?mL6r4cyEKx%9SVMN!d8&+Cm`cfLAD(u;p`xih?+o- z&&SIyZdoQf`=PhKfBz=3=cP^c>8Z_RmpjMm8%D;-$o@F_@utm5pj`|7&475`9w@9I zwbJk1(ppiyzK#B~?QIoR)8Em{=eJX6N;vtalM@t6vJ>Scg-MFZG0E~y$tj9{uI=T` z!c)nlr>Sx;>vYohe7gL}PaR0A@tT1+jm*~nFion6Ea;@)R@_O#OTk(4_?EGX`>$fi{hTXQkc#^09nok5u`}h_1X( z;{2x+Vf;nIJ-`ju(_GCyXoZH2nOt*^u?bYe8ol;e#ZLgf64ERf>kG&cuCn378Td>R zvzEs1>*`M$^P7LhhhKLr2N_ChuIUYFoPdyl0C*i-D;h1QdjpC(AFAY+<}D`W z$Cl`yOj@cqlk&CxLZ4;iLe6se9ItQ4_GT;en_jITUwN$3e_pkUOn9(bKVsJ!Qc$^8 z-)-SKlC){PzV&#uqSt~A^8EfA6$xWE$y=pvR)qEZRvs9+MPbustNfL1h2loQHo5Nl zHezkFUB36=c5?ma9r~*EJIUewyY!mryUDrk8hIP_9x`q6Uj5|kePl@Ceto}~1B#C` z59-rg4=IYH56k;LJ*>!bIVw**e^l}6qhq+IK^_dN)c?kPPtN9@)t?ZbBU|Hs(65I- zATZbKf_{eXg5uj(7xiVDONu#Fm*vWpR}`amRmn@I{;24&P$$nVx~gb9{+hgb`cI0G z_kPy5>G}(4lX_hr+~Niaj=ZTSKEHwn-O^use2ZMW{+oXPx!Yv_!8`J+yYDJ=>wlN; zTclU)oqkWgY{GrT*Ch|+Q+hpAe3<=6UexZfVgU2$iVm*N^!@Fhlm1Vi>(g((AnE6; z^$icdBn>NG>0MU5CN5tR{qIu*&YbADy$WQI$8$}Le z=~@T*Y`vpmmd;79zTm7Frg72F+*XIoT3wDL5j;8g5|pxhY;7{V)^2Ub;-TF zQ2owd-W!s+y*+m6H;+~uub)eXaVH- zH)Gzm4`j-dzqFzM=*#?`eGLOVKb0kc1*F_)=bwG?CFeoQ6!pN)X`A;A9x3wslEWNp#bjVgWSwn9RsdSEqOgAS|yJbjy+1?RR+6#N5cw30MrTv&@8yH@uc<^@d z8jSTjjQId^w6XGz=jM~v&lckKYxwMt*RHA2Zo;hTdT{?t2A5w4@bfmb;ja;96w%p5 z5s?GrN%wQ!mN7A;I||K7YzTh%+(vQ`}`V=HoKV(Z=&1-X7h6c{(*9BCiHSmjIROV-*Wk zVcQp}owX{(nI((W^`{;a#}=GX4v%>vT2lY1^0vfITw3HI%IoGS&Q13cwP@)rZXV^M z%n9`s`#Sh3q_ zQ6|okh-db1q6{xlPcxwO|q)spN-$`9H-R!wH|r0VDTOf=d4xp>mk=c0Z$Ux@pjuNI{peko3^cqMAE z;h45{U^xLCJlyk9FiCiltYriwX)Ung$ldvifBrA;+s1wRfBUv@HfXwUt9EAlwrET7QvkgAQ+xWi zCCLOLjDtRJw!yg}+|vD9w8xfWZIHD$vGxOytEZ5dA5%+^`F*z^)k+YTUmDtJsKDCp5Epm{K-&~Qt?^HKk+9-E@YLSPoK{ro*RJxbWxuv(>DauC{73!@B7Q{Tn&)lJ{>4KwI@h^azn6e!{QJ<87`L1npjtVEiY8Jeqfk>e_Xh3 zMu!Ofa<6*2vIddzeXk;Qd5%%?WmQqS7PlJ6r|fDd%dKoAFIpHaYrd(myyN&7neQx# z#l4?hO`Gdo>{{q92E^-szaFpK@uH>v^uboTg_l~(@2+p7Q*3W5*G_*&*L!|D`Nk4X zHeqste0FxCte`MSK0GE_mXw^LU-2MC*DE@eG9P}Bc7J>j{Ks!xnh~uH=-lWR*=1~YR`av$~#Lgi_g8WDK%$6W@kQE#P)dR zqufm#|L@GFR#s4EkOwB({ZFlfaeo*CZH4M{CPTc>!5nRcsqCPg;Li)}8o^#Fq(P0~ zQ47fH0vyFhtG*!pTq`s^Usgz^_qK7$s_&#n<1b3RZ(r2D^t;Rr+jd#n4`TQ#B zmMcGU*||Dxz?ToX(Ke5y3e96pt2$4lX`P;OR}Vea-gSJg@m%>_8hZPMX6`4|(p5)- zG!I4wYdfhzG`}?yOFx)cSM#$|sPwd^5vS`HEuA1s(^yY<^XwAR4{`bv`oT$cP5iox z(ZE!H__&>qU%!C4ZihJ^fE@Qf#mmnw)m@$^md@{4SGQ|)sB~PLFdQ2{SWm}?YoxPm zjj^tx&LW+8*p&eD62Pv4{8MQEA01OUcCt}9B-mnnulh}FuRJMoP@N!-O10imwO;2W zI&r~SHACYfTEDH1c-=}@(Tuqwv2v=LsBDz`fBJer{B32bsMsZ4JZDq7DC^UZs6HQ?sm$1tr5e#QTiN&vsj6F>PD;`6&Z^ddIm-KaxvHS*H-DdS%J8=Dea6e5 zvDW|B-PNoWpG{+W@K<8qTc8Ugqv3CjM8h+GkmKJq^8Ih{%$?yo=i(0cTk`LmQ^l|O zL0tdD{_wn@_TlEAJO`(S`CkG$T!$Q=dyHLiPhtA3jI7;4oix7as_b`}we*bCMs}LB zm2Q^U$u^1YrC*2~WS_&|_!_Bql#S3iX=h(>mUYv(XoqjBqZ_u;RhvIor0Y7>P1|ae zyRK!Chc>XAr!FAfOZzI`TlccQk2E0ESN9|Qjjxvie_1vBjjtcC2FR*T2TJ$s36g0x z1WT7L4$<8oMs2h1`@B%?;(=khEpg%6NgX0|UwYNk_H7U;`}S3&Hq|joHm52|8+EIJ zY}BrX()N{&WIYx}OY3iHENeSHM(Q|AqU+YbiS%}9Q(f!SX3~?LV|781aZ+{D=DOFm zEwsl2;&oTA$7|QUXsO$Gu$5MMskKbEzKynYdt2Gw>F;QB=eLu6UBXF+PEL@0n4Kui zDNK?Lh)I^lC8y}-JxI}ZVty8B_!s%Z^XboW%E#jdXOb|)n`d)O_lCi@yz!^^fu~80 zi73Bu&EGK_@L#_V#Q$wYUZ%tT8oekD6sMSuo9+jh(g5o5teO7ZMEHwAy6HfJxsbz? zLS)+biiKm(eYdDMbK$P|CKZ|$zmM7z@A2i{6{oxHi+?m?|B6j54^+4n99;3a-=T^J z+~N8gg-0sRijUTxeeGz)R&xCCb=jHo$sbjwS#|jSe7yl@(|(CMciukjhqOZu=g;4Y zypXo?*9-Y}ZkG-pICCk#yy|k=;FVYMyX>k;%bxmUe*8jRT9cxy`TpatrFo?PlwbYs z&uNe9|8m|p_4;8q_Z#P**SmT6!M&U3FWcTaeD?IM^SiG9c6jTC+vimW@1*V8eK&vS z`rp&w&oAdMntm_s{R#K;Czd=&E9~_!zfbm~wD#>D=cmLxIo!Mc)BHgfpUN7$KGSxz zf3EBB^tm?r<_le;^VM3{!!LDpDqd;tee+tUpF^~jQv|v*BZN}D-0Htdx2w+$6BonZ z=%}v93%xvIU|99MxbWNkJ495E^{RI}y+P#IxvwH`H*|~|JEkh?Havqo7M?XLZcy21 zY`cZg#m<`=kF7gCrugnmNp;))O^UUpO{+svn-y>D99u1jj4Phqw0ZSUwk>X}1LCU> zT#vs!^F_<*Zw|J)U4E(c*!}C<-0rfy?bzkh-?<$>zunl6O1R>@$q8czW+xW6C`=lg z5tCf(mz+{P{z1y^xaibsS!L>NAD8s%#7*h9pWW^-w%3A=#op&K#wLu-lzMK-lC|rZ zEq(mC6xX%5rxV_W?djm2E^MyZ+Wkx4zhz1O{s|o@p$PEn`4-!PaVqZP&8|5o#~fqJ z!!`Gq8^-kfNjz_ZuE6=!D$u*682Fn(F+@}XIqrAA+Uz>(f6F>u|8ZriF2N;TU%V+@ z7j~zEeCmRZGVWZ4e9+iTS?HE5d1lXSne`V^c}$y5vg^Y-%iRKVWQX%|Y$!BdKO+lhG=(DUE8{)a!~z@G7t?qs%BSi$nQtzr1}9M5}xFW@KO z{U^Zh3y^*U-amvK&l)e^`f8DEO`e_hixYOTk&XPMKcDi`-Mtm5?RmJqtaEO%v|~t$ z;ruL}Plg-3TLB==p8a){5S@>NhyozT^GDyrueWHcy&H1Zhc@Ktm|u_45%)Whez@;Z z|64ZK0b4B3+3=h)5@0uk6a(WlsTt>^Q5U{!vw57OUc;TgyH@gQ+Bz;}$9hTL1U0u{ z-3GP1<3?`$XPYG3!#8uXA>XPsUR$`ttgUJv`wGpk;oH>DZfw&Wv)(T8KE0iLm+VwG z`D!P(`Tj1+MdfbIjY^Fq^}RjZXP@qsER^o!`l-l9XAAy?7x#m97CEmQ>vcgpOm{&xrs}e^eB~9{ zySu8SU8er{CJwDT{+iT3{U=>W>UFKJ`wg8S@}~Cry_>q9Y;S2VpT4C#aQ!#!t_`7ZT@WdpMxNi*9$mSw~|(H7PJALAqcC&j-gqkqe5{aN(;|6M)X zKiSW8o!^te+|Z}-dz`#a!{=JNf?jqgwL!YoxO)H*oKk zZ`AhdahU4_|EEV<>!X^%JB~{Gh9B2tFE}oB+J9Q}sP!4?ou!qU9|FGD+Q^=8<6b|} zCbxa2$@%G-w)Lgw+=t)1(5^gC%?+6HQcEgcX@2_Vwf5Q^qB$@{pxr-0$X%0LNw3PR zHJ))cpmnxfnzx;Fnb=-4=83&@3UTBH>K&y;Iw#GC7o4RXH7=R~@b?#&Y;@)3j}vKc zb#l{e?dPsN-o!&Qx4ozK{T^PLG4;H)g{^%w@7nrm+Xwh*+Fke4)_>v8^*j}zb-WbF zy|X?@nln0>6HgD3#&s8S!jii1YG5e$b5@wPSy(uCuyKU;W3PIe|Hs~Q0LHLBzeET@ z4v#eCh)5#HNpq35X>w_DqD5asZ;K$Fww~ziw5L9f1QG4@LvZ>bI6Wj`5xpIvxBtv; zHqO07dGGnX-~avIGMU>e-|o)LH{X2o&CGY*(owz&=45AHI>{%&oNUX*I`ZCoTy(!~ zuPcvUQcssSufE)Ml&h}IP>C+Gdjp+sD>q#|n3J_8J>=)BadR^H_V->mC)1tf=VZFA z{G3dFhM$wkxA1c^UE6LX&cSq3pM}aTYlX>IXNSpOn}qA+o5STG zjf>Vb+1o_N4(04Lc_rVb^31`p!g!!|;>UwNoctPl#edv6jyu|Xf4e3h8;`Yxt*_uX zOF;|}<}Z>UgTVkB05*e6o&szE?G_cw^es7U!uW;l80DLBF`%#J@%={dw;20*g|!1= zOuGs9^b+pv9boif4h9$tFa>}BCiDhyfS1NGl}yc6KQ30W%F{bsY+_aQ2lnHtkNOYu zpxF6@V#aGNf{b9iTy40=FX%VOQxf(8;O(eJ8+bYi^bq{B-^yc9DO@alSvS~w2|x=6MA^Xj>%w9wvCn+- z`pWmu*1$>y$%AX4&N#OI%W_qi&H&jfSf}d%^3xe$FTeqir)Bo=iB9h>rhC?Ctrz;{~8^!KRE*JngX-} zI(7l<`URN~==ekON4|4RG3;j;C-XJ>O1@5C18Ie=1%b*)<5PdI-vRBpF&Jq)4CuNV zU<1%N9k4IbxJgMC^IkB9j$51lhvy^XGYdLRu$;dKBQJzQn2R7rzqRq4#CV;^F!tmO z(BHz75ZQU=^I=(^42(mQ`}4k{|Mqw?A&>CS#*-mGN|=~_rklUO4{jJY{JgBd48UKT z^PnC=J{j```-l(|%wq2n*;VvM&b z${%i6%XlH&>u&&?;l6hQmc#FqLJaelVuB6hSAwUN!uS>O-gTwFHGW0hrRaF}f7+f1 zhuru*5xD&jii}?83$~dS%1~ad7yg%zZy$aZ7zc-OeEVC7Z^!oy^-W>+so`D-T95;4 zFPJZQ5h72(D9bYea7{V^+cM52eK{zFu25x#ajqscFY63lI^`@k)_!A7X2IO!TXQlM z)%E&DneW(Y+V@|z^$g;3Kby1t$$t3%JwCT6`yTyX-y)8mDPX>|x}lo={@0+taI9ST zjdS0^{0D;Vf_8Q^$gmvX5WsPe>3e`%79z(5ImZ9y>~eKK{^SX&F)pajl+i4WESa)NI zg9c(s zQbh83O^SMaUXv(-&ubFZ<@1_E<@vlOk)F?M5}o1mniP-tye5T?&uda_;`5pmbNIX_ zg^(u)IXvH;*YqcCnE!>GJfZdy&hJNp9DGaP{9)h8w`*(9EZn9kY#ac)r@)V_1^GS! z5Fv&5Q7P2=0gOJE5k3A3YFG+;JQ?ieWE@wE_xjDW&8Jn;6`oc?+y-LQ0fL@N0vc=p zTI>Rhdg_j>-fk)(xTM@ar8N7hX$5)za~0Uj4Jyf-ZmA@m*~wWRxvvhJ6YL^?eAJEA z#0ANF1_U$xnuk!W?Lq}T6dRHag)2{68RYQbk!opb*udM1$||xr;fn=T?EL2 zdj$tJr+x+K=J_m)Lx>IeTp!wShqgQc*MQ$)-JgQmAKzHR zA3%=qku=@hxS-Ze9_XUiysRXcr z7nXqk%zE?JYN+zkkd}tmF4lfnA$lJEx#V4b-H;b*z8u&U~s>WxQ6}C46WnfuA^kH;`9uiL>xI z`S-lbuaV|_#SbpXfUuq<$ayBnxf=LhrV;4xMuf-#a0R$ep{|!aGRn%h9!ya+dW`!w zw)4AceLxJfG2ayt*C^=j!uq8kv)*uzk#Mg~0oQ@H1Y7cpIp{yqR=e)mQX;$AQv9)c zoTSd7IPtX)ttDkwv=JY;(pK3xq@CnZQakaAJ?+sp#&~IAe^eXEr@bywM(V=n{6>9W zs!+g75RQuIf2@50{=i}Ew^^A~xQ$~h2ZG-w7vvxw=s5#m9?&)!FzUP&0JiV~T$e@b zJk^y$a*uh3*(WWI%5&}=We*oWC2zU?6kB=0W!=~zS$|Z1@S6P73Tp3jjB->qnSV|} z_X#==F^*rt^9s5TO}axq_|&1Np73oVcW?KQw+HRjUCGjrN1N@_X?E==*SH_h zEnjqyoL1?O?iGEQJW=k5?ukrGZg{BGb*OSobNs@$$ImYKa9P(%ll8SYTFRtDuKb8Fy6xZ1^uQm^p{M4Ea*GMd_Rr1 zf7YJn##>|40mc=yfAf}oyZtNZ$I5VTj=qGb9^e+x#^wpLmXlc@Q&kpER!du7Pm=_n zS)-nMV6AG%=5^|=dAn3w_ol1oo!hONw{(ws*p|JjVWTzbwsZEW+IHM8O&obZJ z+NS*>m7U#TX{^-|iLaSfTD{s)N%cENrAwzAlN_#o!l~Qb6RL6M51c%oK2Sx3J``UV z_fR#k(o3=Jo0k&*5AU2Rym&7e{3{`j%_LNlTa{2YI$T0!`)jHBm#>y`T6)+_(xgpk z@t~~Il8QFvloLjmlZ?JyUfHvwh2+8|kuox%f~wx63d-dTDoL6js3A^ttf|Uf#_!V$ zd01lK+Xegdsvr+b*t=3$RpBCe5f!L$Z5*UamIZ4TSK@LvG>|W)>uwgNfqW@l_#K%B z@}+bx+6WTzrF7*|qe#e?(mkHggtUizsj>s)q$$MVK9a|hw{4rrb&}@f(YIV(QXKp7 zKpGb-LTu3@C<*`gzomGDZ+y!?XdCC!(MG!7Ft^F96!F==l2JB zmqyebE<8?3#?7e4t07(`&ztaTbsSn$Gv*P`rLW@-D@oS#Ag52 zK>54|3ewtO{$SkG|J*sq26Oyh z30Kne^s_Z=&pm7Pv1iw@kxSO=JvMJ(>y1*;O&4#ZBD!y)-A8Pu>V|EhE4SN9m3Q1m zzx3Ho=}W5VOSU_xGq-l=pO@H0Z8^M4e=#?m)os|V-*IpcyJ^N={h|~NJE#9X{m7*K z?2woP`i}7j**Moj^oZ!gY$J;!bccFc-2a9q%N?V=t3F(2yK1uNNmsI|UsmVPz4u(9;wN3DqnEH$i^}=qkqK3Ei7LIq=r&aFipOn1Eo_ldmfA-dWcH4yq`mKi_QfK!*qR(u2 zOl@7Nr?<>_Ld_ffl>W8jx2Rup zeR@d|ds7tV?>6k$GU(5d0B-<3LjSJoPp~rV)3eoFpO%Ne(qh~Y{jaY6tX`F1!;)k8 zeY0?E$0Pi&lpko1^K0h&eFQrcvEjT|us@NnLYxr^e4GRTJ2i=t0b|ULE@aYiQr_u| ze1Q4`**x{5yxDw$^qN>q?wMFz6Fa~}*R)d!jYm^coqJ$OO_c_vbbY6l(!3Itmfu}h zT60-nMt6xdCtqGFtJ|e1M_z*5tIX9FNCt4o`(chQrSoweOUcb>k z;`+|wYdp=^8=o-J&Tt*Y8|dK+&>d(q5HJPT_+5R-Kf7z?c3N=+56ISb&V6 z-%>b?$5w#{!kow02ao4p%ej~eJUR}%$_9+}A*|Vi?=QHXCdu&log#$mw zdqjDZDx+vBs#G27qpAc(CV|_xb8IAP` zv1T;ZC&Zf3Sf3DUMq_=l7Ap>wb~^@GR)FuqvVsjnG$C!!sO#XVC#h{_X~-1uyWm z#_h#td)n@pd+TSU6&{B1Y^C<|JT=w{hW#KL+P?~L9pE-x?=Ij$aE-=ck{R-w?G_!r zK4Ph60I}@w&<-lnZ|FuH5x7Z1$8DB-)Z9X*cyHBpH`%7SXtQ0uD0e$ovsrv(2idP` zhVFKs3{4C7^W>}{=Sknw?^SPRebAi9_^8SrNND7Pi>boN;u^OWCMp+k2{OjtR8@{B zNv4~ZQa!#}imdd(Om%v1Y4Uka8OftmbMoTuvJ&0Ia^#NR%1bu&u+W?zFOtlWarJB@ zL!7vJHmX?_xOz6KgeO(7o{g&IX)CN}qmpjq>e;AlW^(mxRPSi6o{hw0G*{0?^46QH zXCuk6uEp&cx%ah$=GD{Ml4X}1H3wfhsdn#o)~vi(N40dhi)P~Xx~eh1)YJ5uS6|ho zGrwnKXix*PC*>x&=;uyGk{*%}b5BxT&P#Q^WJA(|kV?#sOEoTcy(Rb3xIH5!8IyfA zk5m05>wEcYPEQO_9cxBvHunfrt#uF5%#{VJrdJ9fH#G=V(a-ojBa1%{BZoYZN&2UU zlX0gbRD?Q`Y_u^-QtEKD<`;Pr)%wLTnoB`)m10Cw@`X#RYGAu&WTts@$*qR1G!Hhk zQjHIeLz%(#~M`z2voNF?Rgm^0QfJM-T+mQ!K; zFpOb+1@hS^820cACPdQ!qc4_OrsnqL!!--s-;OyYHpAQ`W+PGN-~Hb{?G58J;nBb! z;eU-=#p@Yk+r)Uxz@w~v0AC-Xz%|ameA29k5)!qO8cz(nB*h-7L ze@T68*;QUv1>J%eeOAJ_n+VUJ40Koz7{}860Kb3{0=rET9`L&A?Dyk^{Ven`UAM9_ z$u@n1(Cus;ky>BLVFz1Qzk`;P-^D&UwTrI!D4jCjv0HC8a6fy$(*gaxrU$9n(nI=; z28Y=VHIC4CZM0NInWOaaH%F=U_l^mB>WHRxIZO0!c1|She{sn3BC&_a6s@X!iBPha zFi(j1MU#bjLd=rYIhZHJFq5ugo)FWy=QYd|VnV`iV4e_D+xaHu2@$~+Z)2Vi;qdeh z<_8gHFFe5fAbh4T+AF`4K%$T?5 z|LyNzroLf+hu54x4QY;tKxf2)-}PJjdj;S@Oei7h2pHvj7r;S)At2|NgP|DzH|)bv z$o?B&%?HV^wMuJvSrl{~Vx);+Qy9u(0z7jfz#@456u{Ntd1ZgYUghKfN`}||q6Fmc zcc9LnTMN&F7@tqjV}kzL0?%_6o+}$L+I?DpNx;*e0skd7{fz9hs-w2&{Bj@VUjlKoAC#iw5etC~fJDj%|;lJOP7#B;ZYsg}Qy ziHFV$S0!DJP|ju|Ro!+*DF;*0D%rv&;#uJ_lCERrN^fyfNm!Ryr7h7+;vC#uEWO)Y zVs773Y;&}wDTnlghH-?tv1>f3*y_{8ghs+gDr@rFwYl0MQQ%H#WoNSatBitnsR zlt^9;Q_lG1XGz7(;mQG>M@aIvj}%Xj93{Ch5BIR(4l(V*;0x7LX3Q^uzc@a8>exT< z74Nf(I#;;uY#2k8NCXjHL|Ph*OQW|#-+c*v_cX}+d4QH-1lu+Y)&v0C!*f|hF^e!4 zQ&Y?1C*|wEXP??4_9yw84&>~x9`|{A7{^ZNpYZP^#xdG>-UaU$>{l`qXt5n=vJbEv zX!3XGasAU`kQTUqo?phrBYn-=CB!9tGv>#KRp`iu!!f^Jka7HxkfVwiUOnj?}#d0 zWoLGf#jdFP&v#|ldYm40;{0yhV<)xbsXbO3w(kAM?zQ7+@md>V_9ip<)f;W?J8v@` zGT&*eT`J@V3_hA(?-FjBiR&M=M z=1<|3wJ)VrGG91Z%6_V8m6>T~EqhVSChqwY8(HQx+qjFTtIBrns}{FoV|Cf$Wp;U| z28-i6kExNju|>^ubT9k7*`Bp>sR)O>!If+02Gwy)nDxOiw^kYFgoG>3xh3z_Nocvp zCEosQ-2~~9dhw>4>nGTZa*e+|OOof+y+QoZfo^$LVeav3Vm|uDH^PlFx znA#q6LMH&ba6%LlPOw#gE>^#O@=X=^l8O!aqA!W_30YX)HS`G)$7epmnlZ$>0eakP zRiHg$yiTF^Nd7$a&O(0dD=OAUn5TAydmatIu z7aGf6_m@Wln{fOptZV<#_xSbq=4*N03O+sKxw4=O1X}?0Y+FIc3|p-q@pJYl60Uo09cVrqqnm%a*9G$;`+9+MfasFYi<=c=LpLOy^8<{P^F=L+E4 z&vrYUU z8y7Cqmz=YlT-_pB-+p`wmFToWAJAwr;XcZK0k*|m)7FECRuzzH`Vi^W*m{J>sR>_ zImG=k=CDx-m9oDjhm9)Z%;&JxuK3@R!}hQ4E>w3vNaXX7F#hNVcD`@#rr-8WZrq<^ z6mKc`8x8#~@_fIG;kfV)`YsVkuxkJ|L=vJb!07YL0vMm~bNt=@;1)y9ibgg&Cw|i6 zyy%|zg7~cGMNtNkDc)N7l4w19NqL&DQK;O^*CG zo8q{zTcV~FZ;Ko8H44R5`5J}d4}6V6G0WE|RKDYD6e_RqH42p)zDA*PIbWktndJIX zwA|^HvfH1mQTXpZ;US8?A)(^4&q76$KgEVTfW5wPoEr1r&_CclFh*|9)WXNG!sH&u zFCn(%G06E-fI(3Zha3fa0Ru+4&jFYSavum-bzHqfX}@c!s;o}QoLRn1f{hXc@8#=9C_7k0G}l+(=lx;vMfkzcFNX75W836w zd|d~8Q~^8%cnN$h9nJIg3f%J;;Ohv$?Av0C65@Ny_ALV|1Qp|yZ`fWWXp4C&$ z&K0$H1x&iL^zF1+1^XWj+jE5;IO(eK-Z6|_hp+GPbMV5a{g6`nKht+^kcV~Sh#_Wg zaDOZ!?#^2R>Le45wC@S1BS%zC{YaFX4~6a_+T`)j;*8Az6UMw#3Fe*LlzB*&WUfd` zVQd?t;bYsF<@z$rY}TCEd#NllSW}Kzwxc{TYqbS2c7cdUm{fu2I;Duj)*B@;P=63hQ$I=?&L_k+6N^c# z6N@Xo2AHTkJC#scH8oXP29{L5k(5$3omNVjEh;TJv97dokG_m*H)}4=x>Q!RR8vm8 zdq;Um`f3aD(gh;Pl1UZBV}@0fFg+{begcxt;g!Xq-c=+a&X&qfHLWDIOIs_0|9QE| zdmDb*PvCp%g?-yBpPz$`ISGt|qu5AZ7L3;eggKZnr#JNfX9M5sHGz7gO<-SOz<$_} zRh>P8Uk_`{=*Ky|68*#Xi^1oH@#v3Fn4v#nteS8L^Iycc27?&KB4ZyKykF2K+XMX< z0Uy=^M*ky|QT>Cs2d`5W?rNuZFLjP%itK)Z`giuKAMBGk%ac;jrmt} z=g-@}0PS9d_OAoRc|0>D{TxMkTw#j)-p2OuJ@BV<`4#*xVGVNX=WRTK-#&vjo&#=! zZ6uywq!*_zrnXE_5Vnt$E^-4syxSxozAATZa(keuZi6s+X!-{Bd!-{EK%!cW*%7$^eX3K<5 zsLGUqam=xMbz;*zJ7!dCF>{isK`aWYN!;&gPmHWti#QSPKy)l#o7mv&NNmeqV9`^%=K(3b9a?Db1=OTQ)jOaaczY!Q+BBzabTQ3^Jr86v7#%<=sE-v z6GDTSP5!}5a{UlyPW4bC(zG$Lu~Znb{ACz1`?icox)@FjJ`zE6+a4*{XG9g#5y&e% z+9JP-1IPaAaf@ruma$N^&PD}wAA#PJ;Xaw%p1T4t(r3Adl|zxf}Z@;{Id!hv-CBsviD^;ZZI zto<0qp&PGH2r-JrvDOQK&yRuMrgDM>^Bp#ZEM5+=kne?W;UWB9kdw;reM>nZvKD?9 zw~^Cj_>6`9UQ&l|NBF%x{LT@6?+h5_Q(@XTNMsuyjK9b8Vi3O~#NzP3B#6Ss2>6UH zZ%I%WU_H(q+G+@GdgtF0Gb!$FQTJmk_cHiRB>XlSusbxGJ={7?@;G^`a-?Hr(I%@Z z;*DmOikq)3mAQAVMAli>%AOfEnjz`7n(k|>YT_1G(}Yj0u4y#FPUF%?tf|_rhNfIp zP0a@%d(C5)S{l~YL37%?Hu=7UBl#%LQT{O3Nj^8xnbe(gCifkzLvGsQLMErwCFjhk zM~+LXPYy|NCA-HJymcAP(XVPOE7)0Nab9ShrZ0zC^2EzIB8vhYY7PM&IEat|DW zzCet6NT45LJQw)d3g|Zl=(qrI82nCmA$c1+Y+yioK@y3$LlQSWC}m$SdvQCA0w<@-C=p!UVq z)Ln_Rr_$qV=`=15RR4yxb<52i*+J$`@`&qB)Sg^tUANLF2C219dTVs?LF(wcG%yD zTDIATnw9KJjh*dBC5-i_V&(@>ExVBHi++LZz^EX$i*GR7ylx2V8W_rYm2J#cs1e3m zJqV-fmkDRz91o{N_afNrHIdZQqfylBInmU))lI0Zgcx~;&9T%{k7n$a*n*miNTI@I)blCTsawPCsEfVDl(uaR zYDZ*ED$U29TI5oTnriDnjWn;#rj~GI7vwqW=jS@<$wX&%_&H~`&%ruu`zYFQxB?mHBTz1fftqg&Wn9*Bc%>!OWCY9 z-c)LOBX;*?ANJr1Uv}wEKX&Cfe|F5m0Cr+ml3Fr0knI&3L@`~0*(iq)s&jBCTi>)X z6=EMoMLr6nYMX>p^-hLUW;Y@zi}jIuw~Q$II2vfe!|YNP9xs~k52OXN%`!x zrRwQjE3spiwQ6XFjV>wORyTNURb98m)pV_swb#9O zsik{l>!7=8UR!Q%>L`Dg=SaQHb)u3Jo#j`~Im=HStRvTKaglFKsViSTr=EOvQhjPx zf-5yPUc#<#-+-M2xzh?iceZz|2RpEuCmZeN#WsKMMfI|kvR*k-D(bB_Yqh%(RX@vz z@?7aliFWx>mgD`Yrwaq9*Ih}rd{Q7jOEHr#khieT0plIU1B+dG+L6*v7-?3Nz7q5l z12o(WwA=?6^<$6eJS|mI94m`8|5; zXrY4vN6-15pXi9MkgnfC&$B?sQn7@nax6cd=fQ8E16TuuO#7U+lD%e@f3>U%eHAfY zv&iudK1Vw#-3$J}@ingWn=E-_r*T>BsfQ z5}@yl|C!|P?O5=EfhPtclu*a1BuG_)5bV9L(O0k|gnrox=sFMRy8!$Heqo!w(HdDY|9aLY*-&LD{WVUb&jgZ+W6YD z=5=ea?`$2|hvu~@Yg0!e@r5I^^R^T5KGT^AI97*|>~tXpt*J}IFRI6^oLZlmH^P;; z4D-yY9UBlGNX&=|Vov)7GyCg>5Tf+_Qp~}+_RE+W?5^-W!SK@(rwwXYrl)$RGH_Fbm^N;^qX_e^!%`o%DVr^=ZTt(r2e`ouCF8a89Rqnb0MWedi(ZcCvLAWe$WAK$($WWJuM zkDU3`9|WEl_X|P(D*{hiH6uhl0i$2&4M2UEP(=dZyy{^0Uds^+^D|!zW`RTSIT`A! zVbI1%fYH$27{I~sy>}zeFB4x&-m2*DSeZz%sv=4@vt%y5wp3iWYekr6Su5IR*r z+p1cxt*VMxTutROwYsYA2s>4^K4Mk*b~RKVBWtSkKK80>F11u=Y#mew%xg;?mvEG@ zd5+}OTqkluqO)ZGIcLe{gLNb+TU;b_Q|d~_OFnp9uX1N=BqNB+{e4Y@ZoNOP`B zFuBYjM6)e8lpJf?STo-~OtbV+nC54baLt&L;hJ_gA~aprN0P%bqBNn?qb1KGo0Iom z6!07AQWU?1k@C}gz3^8MPsEKYNllO&zn>oo{6TDJ(@g`uoCW^e1Wdy3*e+d{%YPZ3 zOfgMTbXx*eu#OHZb)y|uvCo`S<(uxOvg7L9k=M(+L+voXr=!l_V;?-YFQ2pR0XyK- zL%H>WN7M?{V_nqGdaA?961t%-rmP9n7Kfr?;}FI(Dz6dn|RJw))nV+uJy@cd9w*TE2B+`x13@ zrBBwOPVzP*rTSzuqK$(#qi$w2XEW*s)#GeNU5j#@&4@Nmai9U}M)m$}1^fww_X;xs zzw15d-01(3)qp>6pf5qd$PeRn0Gt~+l^`e`%#EG{Z@ijAzI1c&4*Lgv^r5#a>xp|pAIcf;DvpeoNcMJXpzI#)mO07KUE4C&BeQok zPp#C=D>M4Nm#l@YG}ARlDr@-GJF~*>Ml$OxpSb!fePwTV`o)RH`^$0`2E;w>N@`z^ z4U9V%8YG_eHb~HYi1GP~>JNoWKliQ)+Ew~cq-xRjx3RyVBhYgm&~*i1C@tKlr{-|17;XwhsAvJ$Wqn^acI7724Sa?d=8}1Yc3U&RRwsO5RF0bF9oxu&SaT zZ)QpDdTmMXylcfix@=7cWY{R8(rp!fYpW{iFRrGrn_68V8eyj>)<>*(+O~$`Mr2LJ zIUjq)A(vW;ZMF`IRpzxtXG=JW4(2(E59B(DdnY=JR-SVf&4cl6;uaUtu#~!@UUTY+ z+9uT(MJ2e3{Ng2w9_ zgZD4cCQ~{80)3PBFVIJL{{p?5_b<>>c>jWaRh}a|8T<>Ac;AA)H}6}}NAtb~y({lq zprgRI;2P~l*9YH1g?b*e2z(3A%6chQ+-<}x+2W)4Eyb5%=J+W_C;2m-69N>S;z=f? zd7vUVB#5c)5v*{i6+#483)QDR4pT0y+=A|8+EO{Td@j{UAqUb8n(;ev89q2h2a48^t)!n~MaH`toxYA{AhLjn6#)(+jKuip9bGjE0 zg8d0~Y^?>nj>WY(tS48zq-k+r6_FhQf8fZ?$8i|64hLd0XdE%}RIuH9;DQJohIZxz zxQ`hCv*Dh81>6-rPh9hK8RI%;IsIFQWTrx=6neD(3g#KPlI~P}6?0yksvlWuH4!`G z3B9=OQ^sk~GkTOy9<#2+bA5BW7sNHMmwK;{FNw?LU+Z^Wdrin*zo8$)n%9Y}cXYyv z_YAT7gMQWckA%k(g3jt*jOjkQxW0V23G<*+3H@vrQ$ieEl0H(l6w|Dhnf}c~Gh%{i z8M^+dGDO)s=Je)`Wto7Z<@Bw4mnR0LSM8u&yE4pu%H4$;qhJL!+mauP8oxVBQ zj`%rDthb(2gGlhLsbA9Dp4sbGiw<>gU_vU_rX5Wk*|skn<%-)*l2v^;Pz7mS)*g)qYccZTRyUTkt@SqM>_tY(_=*2E9-cWw(Swq(D zrc~bif;ZLda3kG>?LO@IRlf3F^Zlru6a96Mh6PXoy-D4mcAPI#zRE9{T3Ihd{=OpT zi zUVmtJ0ATF5rQvy<0N_87y5Q+DY!hD#^MOoMK5z*03&Z)qNN9rr7z^!<18fD~W5)hC zJ4Qa^m=oR~UZ<$>25kn0ggn#f2-AECa<)i4h(pS-Fm!BeVyuYIAq5y?` zH&W!C6ez5hDQ-K2h~5W>Dvp{q7G1Ru6Ww|gCfa8bF4CS17bV|_5T&h;RP4=&5>1^R ztys3UiKt({7{%DBa>e9UO%+}H#47qUY^Df{YOZKv*+Su5x1~bzvZZdcu9e8?d22=9 zwl<;b)S1N6dvkOA9PgvhNG!S;YQ z*iEgq@_jcmh!1N|=<*JqWV_8cr5m7$d+FsiO+b!_i*R2Q$$=27}L5AG9v zw?5F1KJ}1zI`0v^S@oE47^c_Xn)!ri-R3E6Gw2yJ)hCZ$+TuA=q52E`8LyW_=*O3I zNvqeyv1_mO-ml-tgB;(oQ%0ESRPW8GdoN1s+*g)i)9zGaON)PE6JFb}Rj1jqul7{c zJ(*OE`j}B&zJRi$HvS^kZ4Ix%F6mHH-d=1^N!rws2N0ZG>26)9&7QjNC^yk?a;39d z&97}0o5YVNt2@?bork;X4#|pM+v-%Mp**CdlpXm|s-qrzvmLKC(gmIJVFUO2>S}H9 zW9^sv>q^cDU`=+nNZg&XMZx8%5o?I&5nC^e4F|KXtDr}c=Yg@j`h{g5SMe25U zidqb7Tk`#$+}aj9v#p%k-Mpz>8N#h`$@_#iXU|k_A#dW`QosLsOX^sqf;xz*h}{M8 zu4u~t2>B|sP0&ZEcjaRE1BbE96*dp-3iH5gAZwu@W9tDngS?#rjHd6u@@BM_`WMQZ zyyUI|dBe3-LwRG(+clAACw;fvS%ci6iCw5{{6RhTm9{u}yDf%`!tp)wb`4}KDh}+5 zI6~ALF!}>6V6gWB7y`yXX*g$sr%o*6l01{S<#~cW+4{8J-{*{Im)m(JcUvwo-}NDJ z#IFo~4_7K7f)6Tn<;RJ{!<%pj^Nbf=_;X`BH%Fh5ZtrzAHDdNTsVeAv>W+mM)PepNQ)|`FRC~Y9Of6ad zvfB32<6)8*q~lF# zJ&RkZs{?MU!(ZJ_onm)a?UH#nweQD!>T>G)sWDd{s2|UJ=+sC1NPT+fW2YvmdiCa3 zPn;wZpGtM)GpC9@@}!%@&zl}}e#u95&FMs{>8N7r68(y&-fnNAz7<`7d^%Xh!y7cAqu1x+MQQ(y@H=H<> z=b>Oj8uD%#@GAx2KJe}XU|i#w^X}|;x6y~ViNBMA$s$E5F(oQgAlH~AB2gF=KLT`y?UGxYbvk%*elPv)KVU_bx>|KuPwe-!clxC&rxzZ*GV!g(OJCZ9OnZR|GI_q0g4mn za6UkBn*`1Ws2tM1fwJj9H)WikyV5<@L)oaBr?Rq}m$K@6FR`1gRQVxWD*oxMw~|e7 zBz|$(NBn+;uQ+q3pZMxHfAP+R0pfjKN#%vHf#T%QAf>uXuy~w9h;m_YsJOdnW910@ zFy+`sVag6B;mWQj!w=66bApb*mt^42R=_yFW5*?~rmj@kIdSaXpBmw z8^p`Sxwim5y|nqD-a3G*@` zMjzBc&RlBVln(C`i(?=9(B8m01rD8t%H8Dan*5J^gIixJS%a4sV><%R5nhS;&kM_O zfzRm#m^WPkTlO``*Lwh}4J+vZ*<$&<(yyr=@-pWuQzMW*#od z$qg3|e-ME>4Zn}dlNVzCJlmhY7h@le;8PQP#)dYD2=1d2fL&WcWS@Uews22X0Z`_W zMm}AZDzU1LuLb#g7sFpiXg6EVpBuL832jLMe4t%Fz`^j9`?>%B*MaEVr+f~>&ar$B z!{RQ%L{EnhW>hej!@yAXVNCZ&VN8%oI1_#{oT+sqf^k_NNd#s@G3BO5%ZvAGg6ly@ zkMEX0qpfpF!H6w_b}EnN>0i|NjN_c)`j2rQgCLucz^56&uVsKupvjK_)jmwz-Gon-tr$>u# zMm9$}e7F1vmL87_?7i}>kRzYgk_2CGQTCr8FFC*uyLN=AK|9{wOCC3NyrN0>om6fa z4Rv$#F|&>{kL&x&&X_%)Sq1e6%12rYuTiMKg!L}m5ej9vWkLg5iE50b=0ZVgXl#CKV<5HEqUm_+Gi~v%SpP_WR_@^ZN+a|A6l7wgXHh@gaGs z`G<(GVn=k1h8-a?ZfJFT+a6^;9y}(0=6jro}i7w=E;cO{DRdYCT4cE$dQ z&m!2J0v&t7J&uNZ9S0ccWDk9=7C;!hp5C_SIQ`(T6o_p3trM?Iitk zr!(}c#IyQ!LFec-k>~YM2^aLu0xs%9Tr=r6uQKWLTLbJlR5g! z%dY6RZo8^aAH(YB{d!IB9CKYiY{(70xoa-{YsZ`VhvomWdA(ILeqL|o`D5qxJU7zP zkXC$u`13P-99swWftkc-kob#a|t@1Im3*V*;rcnPkv(~bkVb#}UR zEVs^1-*4bWKYiz=FJ&vG&t*&Xci(u^+tM59k6-rDpIPCnU$@gwzh#`ie#XK8{jXg~ zdhOUiePU=3J-th?zKuf&O$UeSeN7wFvG!qf>qlXSpIsZ- zoLFqz3Tg3Y^;JQVN=dUx32V3ZW?j{n(PEq!?pHMd{0_=th^32(Ha0C3DhSw{4yZkyJphxh)`@wOY z9>AY}x-TJstn;=4%fgiK5ZS)Rt0by==>Zo2~B9kR%w4{oaf(VrwB2ghWtQ!{Hgw* zKi|?cg@)h|e1%2XDng$x)dBi^2SU^iFxuR;th4^3wj1CI^?4ut#IMs8mAPs#iIqx?ZSe2E|5;L|Oz?~jE* zV{jPz1&ZR2(09%QKP)>!esIV9eBAszo4+DI%P=Pd<2DTQ-Kd;{TEDdE>p|? z`g}KTd?*hV zaka`$vy@@K^N}+Nmum=p6u(!TtFz4SeZ+L*??IR+ea%lk6Rx!guDJv-+8v9ZT%7W% z%B@*+@slm+!4KNtdpKI+x_oYaJe2WTmT>>;p^eSZ)>go1PrL$X0QLmFmi+c~nP%4H z68KuEcOWlU7|STOCtZZ`Rjke{7HzD zFKS*X=)ncRpR2&9CxES>ordFnT>rwjMnRSk)#K zPK2m7VAS2-Ah0dq#ZT}$QO}PR=r4M;y?1U8t4cQ$O*-GY6IJ1MBJFyoN8ar_iLn;< zdR#biFVW-i{T}L74-%`Kde~#(ghz=3JoO4HS*+;ZuZALMY)wUY6MMXWY!@P749!Z; zq``YP%a5TD#vjBut_u7?jOT{^#tZJ-8z2Urp#@+Z$BM%xE*Hr6waqc)C@hZ-y3y-*z0?d?p89Bd0Op~enj9U zaqQ0OibXx`lu4t+ijm-n=&uamiA86~|LM%jj33BMbC4M)Xp0GNae!&v{LB4e!R<>J&q1Gn zI-#xEY(0^dLkt>tg^u5Pl^8dI^D#5(_Sfm-LvIid{Bvosfq8pGIrlU=X3hL&`DqMVyH7;lJEzPqV&ArClPqh!2K_R=U5Kd4SrW_;g8KfM>`wq zEMXlJth0o5OoTd1=%>OuOSq1P^_6fP4eKl6IvUnj!gVyPuY~JpSYHX((XhS}uA^an zCDoA}QeQPGkiHvQkVDBmQxWvyMB(?Jfrp9rvwlQjf*E-cQ;g zVkh-)Sx(x;$DP$R_tcT;*1AYrF0HFInO;vliK#DZ*xyyUyOTuQOWr`O4|3B!c5_z` zcJPqd|KurMUD8Y2^0}8f`*uUw^$E=K0ms$`d+d3gy>hp6G?VTPm z>V9qI+P;xZ)zAE5Wy|U|lRDZp*PbiaLfwXFDKmTAQab%wE7`Quanec$TWd9&+Nc|^ zXe-N`-A?-R`1V@I!5!4=x_8vBZAGcwB09-fX=iCSmoC~$Hu35OWxLA4-gcFqc-T#r zk<(ootLvftxTmLj+{RuqcV%zsj#+)Q-Ny7)KN!?cwy<-5skj9#vkM&{UD0r$Hn#R4 zb*5EihTs8SMQX=F*xGSt4x?Bfx<*A9kP7^Vy*qk zg^1Ow=PyDmYc*jpVrkB>-?;Pao=dp%&h3^W)>=d=5v%9=Eki5|uDcwu^l0^D#M&X{ zQV^@(edMtA?c){P`Ize~5zAapuR<){dLR|Cw)3Xdh}Cye(h$pjoxKLJwCeb^h_y!( z)*)8MbYIV%H)*wjJ3kk$LM;15x)HI|%w-c|?N;l}h}Ax2w;-0?eZygC|A$+-^L{zo z5KHq;ZAYwKwnvRv?YMCVV%hnnI}uCU&f0}oYc?hwv3mNT-Q4-KE_=B1N-g&ymhB7C zAeM$T+=p13=dd5K`e&;Hh-FTt4kDJWf6Zapy1R$CbNB4Sh_&poBZ$@Ac54yKep-7J zv2@{*V~Dk3(~cunpWsDOo6-LScOKjMBw`uS^b}(0_+TAkt-IT4#OfWj&LEa`uXGl% z^r7iF?)<@X4oe5#IgjUByNeePt5+Poh*;K4orzd_DeV$s?f3?Se<|>Y@c;Ew|z48T{)o_IU?|u!>QwE<;f=>y!!6#gpo;Qr+6ZZ9l z-_TuQ?Z?@SpiR5LdLKX^0H1iU4dVH6APB~RV1VM#d_v`0=ToCQ75MY;Jt3Z|Y;*ql zLQIU{mo=Q*^?$w3iA#E-RF=Nl zx-9+mTiN=LQ*-c|I2WN)_IE7}uM2ny^uYO&38(#jKhH9>gO&3^*8m zd#TCI|LD4kaZQsyy4Iz-d$Q1ajepc{uXz>Dt*anSzR$k@eU}l|Q&z=5V{iy^A=q+) z?-w!3tzeJr2cDGb3Vy$?yc~vsoX7ysCW?CWDaT)kk;nK0#&vB~j6Yy1@bL%CGd})+ zInT!*Fx&a~17-mqe}MIZv4$Yl3&z+3tQU;22Usr{V-K)iFvcEWy(BJ99q8MgA|wh>&@Go1l*m=_njX7g&uj zig?8H5?BWETmE5qBo`mqn6uhoPD>+KKIymboiEP>eq98<-31&7?X(YTy-L3=c`Fs_ zSXnpBstTKEW+`9y+LBs(*Gk7;wx$|n*kCRX_ASii!M=sLJlMA|mk0Y6=JH_Q!dxEg zTbRp(eM__za(UV(I%EIBd>-syn9qa#3-fufe_=ik_AkbBAlJYC4!^9xFQmu!>AT-e zzzWtYlZPXhKJiKL_Z7u2LGOJ8z65sz|6ezrKOOFU>R11(b!UDAf0hs9`6Kiv!7nNJ zO^o>^@TWWQWexCWH(<0yJV0qS0YF=(>7b{}tlm96uJ69^G~P452lfePm$0e{axK(~^B>iU!;g1}&S}#{ht#_j!qi4I#J~or-^P;PZML^&M20SJ0rT* z`mE?o_&M^S_j&S)^9Ayh^+i%sI+NV^I+I*}_YygHN47?}DMvGN`4!DCv#x3e{laSE z2VT>(=zLw%IQE97VNkBd$?c}by5=o1)b=*%WqyZre0ztqx__4}opq0VbL>8Of9C@- zJMAHPV&Nll&*aDChM)DMvey&M@h(p_>CK*L)&=KjmbgFH%&>oHO@K z$azsYkae}`A^ndi(r^UQmB zp7*mqYQiQWWyFj*s{4HP1$U}W^9tD9UDE@o;@@{Caoii^Ko3KWX;8W^Q7_y zH-MF#k5{&KQz`^c*ei*zmD5u9eG~p8KTt>V6V-m8@I)Lx%ReZlxZZ@X@mS9$kHm1{ zWiiFSONTW6QyN_h_bsgiZhi`k|2zS9(|HQ4OFRYE2>|K>4?V7XF@g#wbro9(J!&)3 zS9l2e)B@f><(Fkh4U4Iwnjck_p9kyh58Y1iL)KJ>c?UK4wo_}W>I^gD&3n{R6*(I7 zpPSTHm^U=x@9LT=K36cKKD{(k+`VT`-ASvXIDft_6?3$nVs}t|{?$BEv2s}h{_3EH zs-S5W{0WCfsuevg`E9itt0vl5QOAp{RId8g)Yd!JD(k2J)4jg2j$r)tilk1dVZ zXBM^CTQ!Z@6s_9q+1It%BM(eiF2j`dxoAe5i8dz=AFM+J2Gk{3?|Q_tMfC~y86+`& zWCLQLTSLOd$%1Izv=L!d$C5BHY|J`qTd}R)TCtXony|*#tXZ8O+m>WYnBw&POnn!( z1F@cEq6H zJkFyTQ!b!ULt#Etr&!d<@ekoVc7< z4tBq%bRBnJ>Dd2)(z?q-rCEzdN`r=vWo^u#$eI}B$V>}!Wctr?Wrf*KWzQ3z$!?!| zE=vu|lbze~LZ)1sFAMrrkoit~sXR0OmGba_*UCWWH%hkUTjer~cS`q~@0H`V3X}t1 z7a(*0D#&nMRi=$+HKxg2f-#*?oi!O)gDG?_H(%h+{P*((tS>**&l34&$)feA=f;;? zT_~?+AdaJIA{3XK*buP&OZk8_mjZ|5<45_>Ca{V6(-7f2P0TBXK|WCtGL5eAEVqJM zhwAwRuwH39nSSoO2(4>@XypKoZG9q$v2qF_=0=4ev)*x}Rfl+G?d*8Yx$$MP@x{xW zW#uHU%fTcv z=?!jxWhS}#LncXA%_bLQWplM3-6V%a-6C71-sa{7-yw|;-{poZ=gETg_qev>?vvRw zA8_Vf9+J_$A90@>JmxZ6KOxNwa=25*x#Xv(xm>`zr{tZ)XWWwO&&imuJdQf?f(%}p z&vn@%ke<_Cl4F*>;xJFjkAB_22yw)R5ABgX3 z`)JJVZ_5iUB<|XM37$`acv663|G;TX*G)0Q`(Bi3wT_K(vcDvIR4YzC%p_h};Y+-{ zeL;fq!JW(Ux|xZ}OJ|ehUrr{=mbFfi7gaY^MrW9ku`kVJ6T-~Ny=ir1-PYDsj@VaE z-g;VnWd|=(Zql=Xvi_8Y@{iUQ$_m{Z$#3ad%IY<5EI*suSca-MiOEiHBD*hG$3!2q zk;STPV|MvAmF-nFi(%aDWL~U&%((tCWyr$j@}=!tC|3<@DIaUzN;##igWS2$LD|!^ zjok8P8)efEj&hyTZB;f|?f68W_6pYvPW-{y9Te8V9jVL1J1Gp7%c%qHIxF(VcA?gr zIV-Mo?n=%2*i~_~K{u*jb~jaues_N6#qO#jPkZovclT8JB=+K)uW-Y>#77zbNnJZE zVesD(aOpdsiB#Pe7JFHgNooH{+H7BM$C7F&_gX=n8 zkE`in zZMI%p(wllUS=Y?V((`Q^vb>EsQcw(Td$<(cDnbn+w11+anikbs)KIg%8t5wc68D`I8p9(enn^94K7{0 zc8_=V3bX6#wbI!|x4(8bugMm!y8GX|>h5^b9s60>o{ZKR&%u<|s-d1EI0)O*H^W)K z?dkZ_SD+8rr+dB({=gx%wQXA|USo>$?vj0?qVG10Fznb2aIu zAgyx%|1-U24Qu~Ldi^rrvZP+?9zXJ}UWa?A_1bIm(EqAlW4T~`p5P_wHNU6Q{F3@T z7=G8RAMvMExqR~w^Sb~~Q768q-;%EUNY4vEPYMBjXTY8zGZ66$P^>G`-mpK??f=^g z_Q!j{N_I|GvQv-s1hWpqLng}Dd+EaMPh4ZYcgwtE=qa*P~UI!di=fm@O*K8FXqwve#LM2ejZ&i zf76)>bAbRx0P~rMs1LLTJS&Ur6X4k*I>0Y>U{a;2@kvHgP9|6C6`Er7vYMVz)f0L~ zLo1jW%?mX%>ThZ7ckYO_U&L$|zmeMAjNC0;jTXLeH5#bZ!)Qcm4@pK!{k>`#)r7dq zk&ik*R(ar5BV?%Q=ff29x8xcgGDx2@z!lQk8z_!{BJhz0jDYhW+1LN=#r)Up#VpD8 zAu6`NksyO|TsTVGVR?CSNn0EN`Z5}D3UChe=LS$Ci041`H_)71 z8zym^EweGbDRa=HnZ%c?!FlerApUZ{1Z)?7%R%*s(6ZlBQsv(9LuS;)O zSg#;#Ykis?Mta0E4FvBl4GSJjwGe)3*r?!A4@==@zGXqEwUuz@idDfHJ!>KGh;_mA zTpK!io2`dedQ*CrM>CIU$LwfksJ(}quX_Cu4?A)F5D!9JKg8pexPC}MRdM|gkNctO z^+O8M#q~o9BExX!39`8(Vw(IJ#KwLk>W0-}ju<5;P!MZ2iC4Syo zU_7U-nx8O5Oa1Q;x&WH|ve9OU1P)1m_H7ccG1b+9b~~Wo^JU>TI=zCJx0`@0%mD)d zLqIl@HL{rsa+wVn0MY)eZvBFH@ArU(U-y1Teha?8_j?JSy0nfn zvyX{;zYp;Jsj()~-tRv%2T;BDdx>?^q`luIJuq2mJO`J3`1m`^`<&IHUPyX^>G$*k z=QBB)I?htigXMthfSaHfFEx5G19ZY2fXmnD3w;8J4$%SRm4Qj@ocJWp{bVvbC^UsD ztft4AoY3RORWRe0hMI9BEv?DKBi7`(+2yYND)Ho@@sG>bfEnWV;BguFz!ZN^Nq<0l z7K~X1m;>t1g7$u(+Hfx<8;ycLV*%3uvjG1y{a#~O`)Bo=NoC6FcR6*&^7Nb8Fw~y; zALzH!(*KTr$Es%^6z79K((n5k{cZ(1(GIWyunBZzKhV~oA5?8aeS8_>eMv*|=Su==#s= zZ^PeN;ZOYw3&Vaqwl{oWicz}H4Z`S!PwDmDWm?=zg=xBX7yaQ>AX9Y} zN8j8L#1wfY33_AG(d2@ULXVt6)FbngV4Lt6*+v!#RrY;BRkk4^e{}_vzn~&*I9rP` z7+Z;cq0naXruR|Xr^w91@dbFRQ&uroi1Z9>XGcTr!;2TvH`R=bK%-v3) zxofHmgBR97gQwOM9ETYp#~!r=v$n>_%(^z+rlAS?q-#o>Ry1QwUYXH__sp4(X?5t^ z=j$@Jj@F~k1=UB@=96^LvIgkopoYSkX%;BMp^*^S(-Ix8)mT_&W5uKuSqbCyt(ovU z)`H7Z8)oxaTY6kZQ)a=gW^~tZJ7(Atd%E!^8Ja)2IjuXV1sdwwl70$%l(w^Spp#*b z(mFbAgdEtTwCJ&;kO+H}@^S5igRn>Grz1|n2H2zY&c==c4SSTv%#zdVVUN<_zFp{9 z6Py`O`>u4qt}f{0YZqZ=Ggn-m3UDvWWi@`b8+(iuil0 zD+v68ic}G;MSrF$QM^K%zALXxU6AR}rt5X65JFGzJETXg67=bnya6AaWk^qsslt0k zRTWn3uf|W>P6!j%ROh=dtRc8gt;yRAGZL(O)Zz`>8Vd&2wJGa{CPJRBDP>U6jMjf; zM&;czr=O?Qp{{^Wo_e$%bu_5Hfaa6brezI;`-2+tN2XZ_u?~%RpPrV&-dc_MIW|`G z#Ud+ykiIp&=Z-bs=BW+6`m8NAFrz6wbyqX0b+{efYl%H&vPmXPp4^=J=-xu;;o6eA z)xVWsYvn+lZP8k&qSJ=YHg^>AA3O5Vg>8kjxOV)mo9*crN1S-(bO&0su_HguzY~3I zmYiA&b0+=zcA>^jaHi+mccq-Wx(NGTyYOS0x&GbuA;}ce^7H}1zuLzgka?Tgi0CsL z@wgpi6Zj;4fFOVpa1`(X@DWf5_yqV2z`7$H6;V&np0Kb{5XTe%{!JR6fc0vYCX9ks zcKi$uo2v;^Oe_vts0ot~DGpnr36sVZ;b&K9!leEa9Ok77lloY2*m_Nv#19HDQwfi^HC1!ld?I9QGW-aJ$?w(3NmN1mFZ<5-7)9z&yZ00Q~=YNP6@V zp8cqaQ~D9dS-~CgV(~4ZBeMbw&xPzXv<%&4XuLJhyEDrf7R(RwwxELz zvj^|?)>ecVMz`MMJ;HLY_j#*O?+2szdGFTQZ#ewLe(#k}!VK?Q9rT_Yf6#DJ-XZTE zp@+S zo^5j4`+4SR!#y9Oy;CF4cxPsx^;T{>=RN97jJL1HdGDmq3nNF}xCMhp|L$>TSl@A9j9+;1nk8lsR^0wvx;;j0kH+=h zl1J(9EZu$%zkel}bswGP`F_x$3)>c;dSUUzi zHIX^oG>uuAY{vUWo8x@~`MKggf&5@`pFrMG+$WGX6ZZ)e^y^p(g$C;VL1`0MZw~ed zq@Tswd~Y1M5Iw57uocSJT^(8q3!!{v>N*G`pnM(oxi#$p|3J z>b%#9t_S6-<*JS}g7Q^o($_T!gd%A3O0w)K@XjvuA??c%x(%d_-hbVD8~dx6A7#gfe19UjW00o{gWCn#)VMckY$5j!ZrV+w#6RHbg{cBLm zyQ%ks{3nh7!{3UBRH!-p?Lhk;ybt0K8uX6t^KqgMFz0 zw*dD6j{vU$jo>%lp`uz2W$NDEL1NwezdUwT%0s#fPNT8@T0$DjZ67b)V*vN!Z*{B* zf8Z#chm_a$Uos!@2VMmO;sHs(yX!!mAf7+dW`VYxNMp_pO(*&WrL*SaGgymW>TzN0 zlgZc6oPF1b2rM`^j>Ypm11m&ZQ^Sy;`5xj%Ce;RJLd#KZLHx{bChzdcD-kQylLp*Z^V zk|3dmU9jNx>uzD0LkMj#dXL~>xmWPF3Kc5YsQ0X#RrLN6Z@ zEJ6>{eYqoo_G+atea%tMjXuU5a}Ve26cOa`9>+OCc7mJV;w1N~S|s=2OC)zSKZ;y; z{S^5!^E7!sA)3sHJj1OCJWC$mc8)ank0G~uoF~1eTp$+?y~x>jiX}%lULw!8i{q{j zjpy{T<4Mz=W&9;}^e~M%>&xY}7xNz5)2P#H)7iZW^}H@sPJ{`AE1o_c3(>_Df42n8RLpne$JX&mq}Wo$wdaT>iX# zzAZ69+_=1ahzG#I@ki{#73Aa%*a$KU28z>n5&Vg#09$1!epb zitVFPg<92;-!d^)+|;cdQ|)7IRn?~^a_5_-s!hpe0vByA_#CVwEDxwF%<--#OjuN3 z7&L;HlI@F;h^6KA8l21Xx ziNUzMEN`U&HS=|J%l?yQnn@Jog8Tn;&EXFmrRCzGv9C9{Vg*NOJ1gycSA4-vE&+Vt+$X%Te|3ZF{+#Se0CzEDC$}?T7iYFLkUX`L<37y~A_FD{a|yrg z<|6xqkcFN0kd)bN ztCgI}_xJsmp*5n^{tL<#{g)v(MgOJf=(qmM=U+wtC8S?j|Ap^ym1|J%8hNnKb+Sp* z8{`JN;&~6xpNaDxQj>0RA7XEFPb0*656ON!*WLXdId}Sf&ZhqZjve@rGi>pQJk;Sa zX>I<58)%e68WiS`vp?sOdD&0N0k@x#S57_WK8EL!M|Zs7CNIk;7Y7PlkFn}`59Fkt z;=G3{4a9j5`AD4ikj9Ji9>QbP^B%YZzkQ-l$A2c{Xf1N6btQ72LYr)-SD9QZ(;@5R z>X0)CJ+df4kL)Apb7sd3NSVrz`{YxFtf8#R-I-mDv)fK^G5xA@#KIa}aPykntD)k2 zzc@#6zTZ9*6ZL$*wFTmQztDT)e7`m4#rb~IgT(oMy_bpe{p`o8=lij}dW!S?n%Ri+ z{i^9(tLOW@%oXR-W?UBM(jMP0&ZXVDrp#R0Tl#53eAW8>gDKx-ou@w#g7;qiqyNPA ztwtZkw&iLisFc5nmL=wQQdoI;7u@%??Pn?2({g~(0@!b00jvuEbPn9W9ct`PYX7@! zifwG1$PU|@$kt0sV%slBX3^0UVkni$-e<2!bpxVBVH#O9HJ!5_pFwJDkp0|6oeyJM zpY=t|$C2=M&?!{RH>b z<>kcMBAZJ4C=y?!`K{O=k=m`bJ)my|VDEv5wm|WC=$gQjM8HH~5(C%U-mYVD9Rd3% z{P>B%isirUrkFkhQf&;sO+~$VsX8^`6;*l4Ta{z|cl;?1slumM-~*V7s?EK$sHmxx z6vtX=)0=*&%m4z_x~l7kF(a+i^~0EsIwlIgHKs9sw@g(Tadi~6{C~=~rLDBBaJx0F_ozwf zHB5vR>%LjTvq1;@ z5WoD=mtEnlVAl@m$4;ElpE&C{fOT~n$o8o{h~3q3Fe`gMSYdYsFHf%SXUGJ9dl{U6 zzki|F?#nqV?vW|y$MWZZlphMq+o=0*nrM9XDH>W@?@RmUCCAnE0$uM5fHCfT2I&4< zpnc%mO4o&dG-isf@KHs1ZcbAiTAR){zM1}4)RRiKF-UbJM)#UE^C8xTc^YBM+ze^T zB>OdE&Uo4}hv(WeffHmXX;gExx44dkHr91O(_k#l?9Z*4RWKH3z->on3XH{ZIMa^l z31e~k?R8?B!dRS^t2#1OVJuFqNpfThV{zWQbU{^MEKZhXSCsGU!kpIbhSDrtQRWL* z6t2|+ol5P2Hb3Zz0uJ|LjwZXIB^!Fvwr&F@9qB$oY{$f92R`tUh~MO2;LDU*4djow zH?aB`WcA-|cm9zMlpM#czYy`m0Am&+VlL3e5buOg(=f_3U^c*hQ8Rn_Gx0_KbY4IJz2fpt+I{IR;Y3&f-4R6BAYmcWJ(#L2o_i(zBB7$~nagt7`7D-2aiKN5wqlA+Q z(ZasSGxV6iv%=bK=LBI#j4;FFyfA9Y1)&d&RnYDfE68B1!j^V%^ueL=^xdp@+P=bN zntXX#7Isjydo>(7Vm79g<|8t(l)bszj zNxtg&|F|BE=l`qg!7%?H*Msr=e_Rj7^Z(WLV3_}}t_Q>Xe_Rj7^Z#)@7|;JFH~p%f z|IZy6ub%(U`8ccR|8sLJ)bszjL0anh|H$Tm;Xi$i3al^RpQI z{%5=%6YhV;>oMW}XS^O0F7N)f{%2X2vTgjzS^v8JXRM!CHk#(iax~)`r7Zb-v8(j6 zXYekpXR+GfsF?afDhKd|bY9wjh48Osf&e--WDz3V7eO5mo)Ipc+J)M3fr2^9F*BA0 zVP6RA2=;|a>V~vd7uF5DR+sSnvU&|Jp%e5wo6n4;=C-}WjA#%qY1mr6C;eT6>BsHkcy(&+6}I`wi~2K}4-D&MVMmM|hWi%+SdOZ&dj<&Wg)(Q~ir z^L0)e2<8XW?G?i3wco5O`A?dQBh~xRad8Zy0{B@{(Be7dKtE{jD{dSpdxm`i?@vFM zzew_zd;)0=fRgfG4RXH$xCuHS038hR(*=(HeDXDaA?Z>8g--6Ii`MMo>wr$cxEAWr zk|4Ux$Y9FMZMWbs;~U#8**+|!yQQGyT730tkNv&^&Wc-)b2yyXWzHtPeO zUZ+rqh$^HbK7A6lZ2v5%9u?8y%fAT6E+KmJxC->}fQs~jE?UC;m6e1|w%T+{v&wWM zeI44kwl3W&TbDI@ug6rmpwGU$ZouRR8M2utsxWEGsdfZOHHZ-| zHPJDf|M}X8l739cI78$7Vo16AQC4IAM2NZ#G@uH+01m7N62<4grw_V|p$z~qela3` z1?mhBI)-W50qD`+v;*L|a#ThAzpOtTw-w@cfPZrPjwHh-i6;i*@glJMpgLo(tW)M+ zS>r7~q%vMtC{sO9x~!}cG~qvLqbS*y;|+3C0Wv^_kAUKFP<<7YkKp0zPdB~cPy0j% z2;F1Ub}iV5b}bk(-yN?;Hy--abUd6 z3!7b2+rHoRVTfm7?u<+hx0}iLIt9hdBtn!eI)o^v)Y=oH)nSjqrCzAI4e~D>b0hUj zsPme09@eG%CQ7fb{&)3F{Oq`TTvp%%^A9%}SJH;eMpvhDg)&(V&V(wKA6zzn(yI({ z;x$Iv;*K>qO6M^PG~t+Hxnqj&DczTVuW`6E9>3&Tivgemfq;C_he}JuKD9h}*AUQ& zY(P8w;s;j#)Nfb)sop2Q@h3{o#U^ydrL*LbXN23n=R}8Xd4!$U3!=V9K0!FhqO$#aHeGoCPa6+3qtOd@EYJ4v>3^bZmnstKGTsPR z1mGxbqvh2t;hwL4KMAsE1Q-JtzZ4O3fMQ!+4>Gw4z;&5*|BsXzyw$gUD%LChUSHL} zvAn=~BOO>qe_VgmP6(74f1nZqFU0i@sXTzwuZN^}*#4z+Ofe)sQ|fDnu&?^J8uV=~ z;5F!8RYugkv!Hjk0ZtJ6AMJa@>outJh&qhA&Ot_Ox=OlNyhb>>eKY5Qpev~7Jm5VE z{)u(Rf~QwzNb<$wIm`6={*ex6hzQRW_0rS>B%zg-(~t6s#vmVSz$CzQ1`&&ZIzVJa ztD|-mZSV|S(#?x2TAsw+dHLiJHQ`8tV&b?m^#nX_lU~FAC3S^A-6tH~`|G%KVrN|k z!lOlNV(I7Bte?3f;e4hYJM?CIiT9Y|Z`6W+8W+E8xmvzIuZ{cUu}>fTo%vKa9Lu0| zd0V<%E!}Pd_p9wE9mdZ3f((*C7TG|x;9YmYZtekI0REFYJkA@XF2{r-L|SBZsQU7hgEPJ5B)i%8f`Y0pzj{4PJbHkk6CX8myzl< zKbJ+*e|P;o;yYu0e`K$3K_@-{6w6>;&t-_122>8|{yY1ou`i9{ku{#9T6+Em6(r98 zpjL?UKlnB7E&iK(7D;ndF%K%1DfV#nsY2P{@@!kHVkKM-zOjQpa7bldX}gvD;L`I_ zmw+73fjm@yp*%{@Cy?qk|MVz9=g1IE`_}qOW!e;Y$G|Ip*;p65^^LJy6jx$xsMH8s#nF(al+v%6D(-@M zUwMV+TzmQC2{IKYj$e}INR=t8#@=>h=NGn>$}D!wUMIHQ=?-k?RUO$n{+(EhNphmC zXJ=NsTNlE7f;0QXvMce~+2u!L;Rx>y<&K48MzGa8v&a@=Al5F;`qy1^NVcc zo~FnjU!CtXs0N-V$2V{=Qsp(RMaR@MR$YO0-TtC^a*Di%KRwS!(g7HwsxEgLrJfj! z%O8^m7u|6-q$M^OmC4o0%f7HH# zvy}hfZvu8|#^hBjGbrzSnE+;AE6sTA(mMHPedG+#&2hlzt-$X+K=ItGDXqe`(5q*D zZDV3{BU6O7O?0V_`MQFKwmv^wrBCt71iE|j_O{EI#$zIxYoQjpZcjb?T1XMF*i+xBgy}@eYXCS`DEMhL_au6^SgAN zqofX>1wOt8epc`l_bUCn{YmCu&^B9g&66&`&$5m1SnFW~tB)l4m( zAJ!4S=i}4>84dA7#8jY`5LJ2WT=KhuRKn*+X@8}EgZE-A&&R=Yz)O$gmHY`4<%?-j zeyquk%j6xEwM(*4jE%V*BD8h^m zW=C}0T^?_xd8&lV(lopd2i~jm>zr`S`Wu3kI2Zqay@re=2m3MKj#n4gv;L_9QfiYf zrXWzDeCtP*w%ApkRW>*OnFS&OyTa+wEI4)NaV_Vzy>@BVMC!wPvmw6=PH|h+rQVAj!x|0(3y!r z)S|_Ei97SZ`bE+@$mo=(iuT>CUe|*DGETh?jC!tpIco^vbt{;NmpROyd%4V(w5Mo` z(Q_1j>NyGt%R`%YygEvjqw4zIISdjCR3 zf1r9k8uR?muUGO{?rVzqDdvVyn*Ygcu{eX2&cv~NlAZpjER*uh^7>Q?K>me*zN-*1 zaTTm<0_m0Q*0gOr3Hv6mq_3a(OXu)N?M}Z}xBXuEuX|C<2l4v6n)ZZpd$vouO1LUk zK*Aq5e#A2sslKgDzqQ26()C*G2mA*7+YJZ>ULFIA+t@FFUF8AZ0vh2cr`1kW?$$5cUsbO=A>~oHzWa0CM`C?fy#WwB^HDQSq%@y@)DE2r zd`kzs2YdlO>aP~_C(Js0;I&6dO<2h_Y%Mim0q>WrQQm$mlhTj#wh9$BYsWaNqd?MH ztJ3&=K7q4B9;{-8&-QTg!criX{O;%}t#}4qkGsNRkBA$@O zV*5eK=Wh)cpPw>9q|)g{22q!jfdwa zYM%FeJi)8H=RaX52@a_~An{=qJU0iPpQm|#+eQ1l@}AG>Dn4KO{Y&Aw74ZB@pm@0P zHtnTrTD4Lu*&4%4m3EVG+~N$NJ`sAh;u8%y#uJ4 zH*J%Bq_{BshCkRjDiN0y$6k2$0K{s9Fw~vh&5c3giOY!6Y~l_qMo6mtTaQ}Ex~(zM z#JV=)($Iv^*E40TDw?s4Uzss__sv<|^g7J5^L5#$N9!>uum(!q`6P2>Sp(wBpoS=U zngwysp%FR^YoJ`L)flm`2Fl$cE94Gqpq#&BjRrooVRxUkW$28i?8;runEv5*?Bpf( zOsh>Y!gF$S#&}K(Vv=i1rl5Z-qPvv?lMQ{!Hacz4d+1X(eBy|*pq>6jTsw67W_wm& z>4bJdJN@&G9nn%~r%#-A!kh59EvF~>Tab|0hh{j{n$rS}3iAk`>@5Ibc zME8Wxq-$gmVYBZGX}t{*hF%p&1CNSqli6CF)!0g`em`wauXAPgxlD(1Ua!NZ!umJo z4(YK^Ec|a?2u$2Rw)1o!$sMChfuH#6WJ#i$S6t*Ql#kC_Y z-)zsBD4mD{r#o;TH+Ce}`*-4Q&62bGJv(z}`*va1O>pKo`>yQFt}bN8YZqeee`mi3 zX$^Kv%acEZf0e%v$o(0}zv3D(KQM+&!3u!Km!MDDK8}^fWh<=$-FVxry{Y?)`*7<% z_u;E7=*t&PQsDdpm%r$8hhIwO?|Kj}wv0i|7Jk6(z>E_9z|noQW{$%ckxJ{?V~W!% zt>caV6g@fRzPui^%#%~s0)d#2WSEq3)eZvB9uc{ z_d7b}k;gIWk#qQz_@NP0yv6Y;`yEeE`?XF^S!WW-UwR%nWoAJXADVn>O5e=We9nXD zoOg%M@QD}C=45U-#~<7slXL2q^ZbSt7jgpnT;%ETu{lfZE>Y{<;&P~}@zkuQ2~!ro zPmu4(PUkQQ_0_4!Y`4EXbzhWwo+RruYJRr#3F)%ca$2tK%Db$+r(4c@a} zO}@uaBfPg7Z|i7Gt$1Qgb#GuoO^i38Y$}*huAyd>;XQN8dUYM@MND1FU~)Z5#nq?s zx{=f|rU9=fYsjZgwcwvsYeZGBv*f!}Y)n0Ul*iaS+Y^gPe zno`>9no-l&+fh%Z*;Bn|%BagdoAXl#x1bK#wB&oWZbhxvci@{Dx90c1Zp~MF*M?u0 z;mE(d)|Q_c-j1qzu07v(lM@AtQuEE{bfnUkbfWrH-6=NSIX(m*S*#Dxt;8c^=2H%rNZx2yneD1to*LAuc2ZeS>|qd861&8 z#Z(t2%;IaTZ_?fZn0`q z|K6m(*vH3)kQ#0?Hcbx!0Cqj2xBQg*9uS4|94CmAQq8_LB>3}G& zt_MZx7G6xhr(?G#JlDXEsPHCT&AAO5rQj)(YZ@Hrw#k>m29`cGYEe2^RZOKaV zz8$rdRZwMlw1JK?|E`X_W|6Kk?VO(c?QMP8i+u+28)pn%E?CFxyPUFFA`yC~_`E^@!>uCj{xuJZXWyUS)p_mB@w>#0PMz2xnV zy2*6nddDnR^iduT=o^#OMWJLD^^?D|?XPqnIY6FiFi`fm&5)SqM~27}Y7dJ^-8f7Z z_I`Mba@GjtxF6L(*+co;7*)K-&OQk(!qzbFuR}Z@>JV|^t*`2Rx@k=I1vAy8Z1b4t;5v$T z{&iz^Ew87@{H?zH^mI~jYD5G1&Rz`_0qrg1OKlq|mY7@0#~L(NP=$@<&d;n=iyk(S z_giMI>Yi&OZ#mW$^9aXXE`Q)RY&*$2;SU^AK8z{0W2x@y1nC<9=^PFe+eS>K!8Md^ zwe9gaw!;a7+ANV|fho3S$ZynnMZg%5SJIw7l7A%mMJ4n6Nf7^3z$|#zY@lF*NV)wp z&Jxt`@zT5pUrX&P9-8=nFWzMk*9yRDh<6Q8+z%0RwB`=JcKg=U#~ZDwR*f8~TPGd) zrqz2<)eiTfo?dX{JG=L$l7sv3v1Wa#!^;)?-j51Cae9B%ui5>nCj$ra(@qVfF1J#T zFT(Ma<*&%wm{9l;j?(3D={gK9gB^7d#e;5ag)|<3v>pX2efuZF;{8G}U+|joI6tfe z$0?m zlB5$E5bsTh|29yZo=V*neo}lT)3->oSCX`6lJxvDcrG8Fe+d+SBVHhQFaSU6-Lmi3 zxNw{$<9o9iZh%A5)sk~~3L&l{fR;BRw7tc69UxA;fi{*Azqw74u*Ob>&bE0j6tvDq z$iL9rs#6iFSK*5=bkrBrbSt9gb*q4?!d`zv8f&4gw2qLLtb=@X4V$iBWXSlPt3vOd zSXC;Ak}rwnkXy52+2Hiz?q0S3Q!3-ZzEG>y-o_b$8Z2J#==1(+M5cBsumNo8EoS`fKo$+IG&;yn63&oIY&x zYJJrb%gc&0#54^pL2(+Sw$74i=??F91M~+BfwT+u+zlv1K4+MDTH9Y$kD3)U-+b6pda@l0T$~iq}UxVscZ>0nO z4!u%ZIKNt?;hmILsU9TBO^W{u#H+I&5qj%Ic~K6|w;U7a5)wb$27arrzzlWy6+wyV zlv_&8~fH*aq4l^nA3x+DFWgMd48Mf zLNBfb-PNR}#7j)^y_)h%oi`Y2cq-`~rX~FW1;jrB-ZL5~{>BdQQC$E%0doLY-nxa_ zK5(l3R^5be+f#7(*Yf))T)R^oe~;Qe#r#XHA6L7G@0Pwr%DV>o ziQg#6tt1b_Asr_G(SVDPt~j95J9USDTaPdKy#<=Odg(g5#J?g2M8HB7eFuSt#{W@NJPixKJ4)_B|I9Aj>5Lz8U_5==Pfie}q}?K9&n@0)LH z?^TD>xlngo-6{3B+@Si~zPOTH(y|67b(%LMbEa9C6cLTc#GaNWykJQlw6QX|pu$ah zYJMg3Y_kL}B?{Ts_x`Ev*ilp7NM)Q4Wz29sQkDH|h>mk-rX=-V>=)tUK7gah_6K{};>r?uYPO zVGR`xmHct3ow6i9oFNUp0RtdCLx4)}Nrtiv$N$auX!4HI-!mDWn-0&M!SGdz z7d=)}d2PKED(f}W71OnfV|weTqaW5O{BpgiO+8$U zS-wd%*xipGG;XuXvHupnO_!}IvliQU(}vp>ZOnI2tquGYriK2L$+G}OVfIeyW8yBw z?Nfo&tuRh;ZbuMxc5Sfg=8D}EH!VbUX8ayLdca;)pmQj{tK~k`GK>8@Q!`98Uh4oq z?)3qc%Y%b_*Nj7oamk1I#^Fa4T@NZL=YXS%#@@#$%SGV|-5C*-&dB46r@c;4x$RFX zl5HcYB=aa$j?pRpNu|@O#5bq;%MYVf2d|#t55%5TZHPF>uMde)(SGOoS)LaZ>la_- z`%Q>d%o=rx`lWZAqF;x2O3^Gq(X#GkszsGVMXk??lu=%i;{B~;>Rob*s%Bg&U*p6T z)!RKP{>|nz)s2@Swex}-Y{W%{Rm#4UN?gbTlBwrC4C{TMhzErGP`ik3-yU7JO7S%PHiWW`0stOZd!JjVw~V4E@3j zTahYbbp<|sK}Cf?Yf<@BB}KYIn@W>cRz%2jD1-Gn3dTW?Uv)@N;Q{MUFW?PSep!at z&%k*zKk4NnOjSMZffobU-P4w+uWipS#daVaxH3 zIM_#EeSHzTe3Rhr?uXpRZ59Uh-+~5q*($Vdu?@9uxSbwgzJuv+;7>ag`ZKMb1<yMhtO0w4)6Svjbjy9hPK*6$XU#BSsn!9s z^z{K@?1O`7Y{nsaN%CRj9DanR4l0>30Y~XB-p82Ei^AzfGa?v^k;m!EZYLORr;~J! zZ6x!=JW8lk>lCV_eOh?@<}`ZzFj`2sdIlxLo)y9(&Y`f77{S}`Jo5IuK<{6C5&beD zmR>jN60^2<96hr`JTs$N0^PUnWu{M+M7sIsL`IgEMAy8X%+yFp5$xkqk^PA)LiN2W zRDDaD@OouBdOa^gxHj=Bx;E&VaMI;EI_YqOzG|0=w%5v{PgKojju&Oq+g{vcw%)l# z|C)N6S$OsiJ@W8fX2dR@?zrI|(_zVdVfcavO#RUh1*aj8kdy0Up$hU^c52_>ovW4%o}D^-?#MC zPVbm0_V0z2O$wNv)jkN5bw8rX1s{bTIfbZ4<|n~6;WM(0EE1~h`+}-$Lqh)Q3Me1y z*M_sT7=y8u=oboYCQn|OR>^c2tMxi`fP)^|eMpb?6ZDx)yn&!Bwm-B&v_D~@Xn$y; zXn%t1zt8?8`@yt4`-AYW_BR9UON&K(H5L(VfMWZT+GDW&xk4DWKf&=R!!6U=;; z^Pia&!YA63GMl!NHXXi-`qX1JUD(!(x?{bDzHPdeiqTs~pZl%UUByr#Pr(xp0@yTn0`P>z|>BG}P_@wcBgv0@R`9sd3!oimN_>C6( zg$*^s_}N+q1p4&>e!zo+LjR0I6rFrnXcc~h>VHs4_X{{mwemhjw_FrX8PABIYmGck z74$kmzi)q%%C?Q9v&^ITcSfg#x0O!wnQu-DHy%dwr>>q6qGHeT0TJf}|Bx7diQjo) zvF8QKfAK|O%!F8K@u*AmqTX@Tm=5vu=w=C2=en0^d6h)U;&US1FfWPHzMV{0N=e}x z#H9-4i7UL;UX@UBOB(+O)-8TGFN2StcvXlSbdBHda$VTxaD$4o%M{kug1rx_X49cX z+0@z>H|aHZZc#H*Z`0Gy-l6&&zDxJs#Z$5k_h|bi_j$Jk59sQnAM$oX9tq7{AM-?; zCqgx=9R8JYuJE$*Q~v6^r$WZ#XZ(rl&xPZ0c~r)^7j*iOeCl|hKu2tRNo`&FirzBo zHMMZe8``7qTWUn7cl2=k_xyq;1+-JO5BxCQkHXM`k9_-_LZMyeC%$gNXQ57H5&tFh zi%_%`@%L6&5cmZZsUlj7{!CS(c!f57S6-RAAk(2u*XvLl9P|XgLweLIL7!g98}Px! z_9x60?T;TK+Mh63v_HP>zt8?8`@yt4`-AYW_U8un^#G6$_E+0Sv_Hw;$M$EX36sXD z;IO6;hHa7ox2L)3wYk zy>;x(59^q-x!&v}gqFGc5zM&FXng-I#JDb7 zkxPqhMAwGfnX%?OSjxbkaW3>{yF3eEEN|{)8zqTjpDG^^$3Eq(4Mvqcce9DpLQu~5 zJ;aj%dr_iuC~>*vK6KDxKXIUD7}}t9fLQGU~~?Ux1^zptI~-I^E1%HiC2jSgRY@Cm+QnO zha1d&yG$aqRu&UmHJiOyl+EmYag*J1=N99YdYfH+_6{@c@LhK5E}n7QaF6Y^tocc%@d-jRSv3FJC~@c^Ax>&_mmJGKSLSUpA+eEc})Jf7wn58 z`Ak}%z^XRBWWtxeVvo&w&1@d?hV|?FmRZp09XsFtJ@RW(zz(hU0nOL__#cfm3v(zl z)=ctA2_MlW(Ol6ci8-Q8qQS*B`ET$?CEJnu(n|ZI5dPI3n}cm705W_KQK+#!sT~H} zo`J7ud*t||4ACuU8R_D)oM^Ia1!*3;WvL+4zz@zw{LsGwU_Gd(0ayxbItbWv6$Xr~P{(xJdywsoDp^Q}^S4G}bTD zq0Cr6$@a)V(e{YtqV18(MB5`K6x$xI6aE|QPqH6O%PS8d{Hy&DU|*qta9>2+1d9Dp zsecdK-!qN<$z~sA$bmu2WCMJbldYGnkU7lu$vP(Z)H~2dh&hx2Ic9fjbzp_R+bs;L!S2am7Q9?iQMV#Ckq(2nOxd` zi)=}kt>oAi+hkP3?c8GX9m+)p{@j>Cf92?B0bJ*sJC*XJU7W?~KxM-N9H;Fcq^z_q zm~7~|Tlsie2w7>u9+}p_z2sx(P}!rF`^W@~{j&I)VPu%r0onf72T1P+2W9Ir4srXE z56fnTAK}&>R4Ug59OY(sA5%_W6wdXT5uxlo@;E1RJE639I?2^&8mX*aCyKPMbxLNZ zeVVNP_Oy(66ivRqdPept_AGfV;+*ViNDO(>@4W1U=LIff@kQCT39;PqQJ0hvz2ms8 z9paT+nk8@x>t0rRR7vDUd`?sj&r9Mu+)h?HrKFI<<5FeAPFx|K_Nrv@sB+Yh`hK)okV6qHONMi<`>xcW!YZskfE8&)(ry z9lonvxr^tfY`CYKyyQN)a=`;-kI@gw$wMB=Cb>Q)d$f5X>u!}p+SbmM+2}katGs_I zGko%l%)kCz_98BiGl+Si)K}(nd4YoR`No&rm8Gwgsk2^lN5{NTD*L|WHg$TZ^tFFa zDw`B2=T`eb`s#j^`4oI4=jIg3=45^%2Pb@%4T>xx9rt~awb_P9Gp`CVQ;&*V>)BdL zhq0A7lYZLD+MO$NA7whqe(QC(I0rph${{`Ote~$v!yAw{i~UjAA<_OwRDpH`34q z2#4i*)Yq8)W{OB{Geq_W;QKJ}GZ^@pbWJ$E7vnWtw=fsq@e5EXuC;J3y#rI}etfm#x=TyFsS z1~7_5C@BnxN zYBWY9=I>IVj#h|V1qcM-cSZm`4s=fwL}mi|!nrfxBb*b~h`b2k0rhMUxfbvoz}O=4 z699k1x~7Os1v~;|!u9@Uh+F|Q59k;>L~;Pe9+6)F(swtMA+jByKfs|mJO|KefyjP< zM}XEX5xE193#iiyk-q@q0XSY82jB_dJfJIFcYZvo5NAkq`AF>kiP`FX$sM?}KV zR(03~IDZDPYAdG03FutFAwV~{$F3bBhXa&==K!bnh}3sNWGlcqKo;DC)8DZJB3pJu zWEjA%6CxJ^4CTOkK*P?6oC5F!4A;dg<)0eB6E<+uO_BR~d#9`IXNpyoi2 z0lfz3I1-WL0W1K2Pa{BuQ6L8Z1K11DgRoCPCyz#?3eb8C*c9L+eB*wg(Et^oF@?w_ zfIe`)FVJd0i-0yCi%1Hv3vdZwHx9lDa0r0LBl6b?h%|3ix|F_lxZcIlQuXe2x6f)+*=0EBoW;%sJ@(Bij8BrEDoT=u_=Ips!rsAaR z@tyo|aojyQ^oVy5asHw4KgQTUNPpDyk#ZR^x6|}5WI3_DK~_-9g@1k5V zj*GOuE1#y_XwPO9*5c=7^5DPrH|PG0Sxm=z&3uNewSUDaHI11C*On3Wem4_I4JjMIYrb&1 zu~@pr|$b_;R+;(CQR#~2p|@`Bz?#b=>cs!mh7xqxY0 XS~5Bn+Qz0r=dhI3_zrV$Q>y#}%346P literal 0 HcmV?d00001 diff --git a/shell/platform/fuchsia/flutter/tests/tzdata/README.md b/shell/platform/fuchsia/flutter/tests/tzdata/README.md new file mode 100644 index 0000000000000..bc384ec4083b5 --- /dev/null +++ b/shell/platform/fuchsia/flutter/tests/tzdata/README.md @@ -0,0 +1,6 @@ +# Timezone data for testing + +This directory contains the fixed timezone data version 2019a for testing. It +is used in the runner tests to show that loading these files from a specified +location results in the TZ data version "2019a" becoming available to the +binaries. From 6ebfa3ba554aca741eac8eacdc2fd3ca9e382c03 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 5 Dec 2019 22:31:45 -0500 Subject: [PATCH 342/591] Roll src/third_party/skia cc92b27c78a0..3e01360677b2 (2 commits) (#14152) https://skia.googlesource.com/skia.git/+log/cc92b27c78a0..3e01360677b2 git log cc92b27c78a0..3e01360677b2 --date=short --first-parent --format='%ad %ae %s' 2019-12-06 mtklein@google.com GCC gets worried when we memset Stats 2019-12-05 reed@google.com fix make_path to not care about param-eval-order Created with: gclient setdep -r src/third_party/skia@3e01360677b2 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index eaf635c69ebac..c79f42ca4c2ea 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'cc92b27c78a0eb76e3bf3a68da7342fad57ae9af', + 'skia_revision': '3e01360677b2babae9a4a63641aad10bda804fac', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index c946caa8b8171..3be22cb90ab00 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: be418c8df8becf9ec26322125192d207 +Signature: 2368c13278da0eade44ebc8c4468975a UNUSED LICENSES: From fd240d0d532adf25a6f8e7bf1a8c7add9bc0eefe Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 6 Dec 2019 02:35:49 -0500 Subject: [PATCH 343/591] Roll src/third_party/skia 3e01360677b2..aa4f7f55829d (3 commits) (#14153) https://skia.googlesource.com/skia.git/+log/3e01360677b2..aa4f7f55829d git log 3e01360677b2..aa4f7f55829d --date=short --first-parent --format='%ad %ae %s' 2019-12-06 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src a25cc4cdcd47..36a639fc2d93 (535 commits) 2019-12-06 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader dee5b5f3cf3f..b9f03f47d5f5 (14 commits) 2019-12-06 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 5f857839b9ec..11e6944ec5a8 (8 commits) Created with: gclient setdep -r src/third_party/skia@aa4f7f55829d If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c79f42ca4c2ea..44c9607f44293 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3e01360677b2babae9a4a63641aad10bda804fac', + 'skia_revision': 'aa4f7f55829d1d600dcdd196f0d7accd4c4b1333', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 3be22cb90ab00..73754d97cb1e3 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 2368c13278da0eade44ebc8c4468975a +Signature: bdf22e71fb98405640221ab9e009258e UNUSED LICENSES: From ed2d00ba02a4d2f3df5a54f16fbbdd2941bbee00 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 6 Dec 2019 03:21:16 -0500 Subject: [PATCH 344/591] Roll fuchsia/sdk/core/mac-amd64 from qQlb5... to VKso5... (#14154) Roll fuchsia/sdk/core/mac-amd64 from qQlb5... to VKso5... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 44c9607f44293..7aaf33d45ac09 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'qQlb5qhKaBOvJuoCe6UIzRcz4OR8N6CWAFvw8WHZ4QwC' + 'version': 'VKso5leyX17hxtf_SX8j2j8o5UxJDf8PsR2dihw1s90C' } ], 'condition': 'host_os == "mac"', From b1d4f88ae2e3be07d601e351cf0669c6c6523893 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 6 Dec 2019 07:13:49 -0500 Subject: [PATCH 345/591] Roll src/third_party/skia aa4f7f55829d..46e2d8d4a1ae (1 commits) (#14156) https://skia.googlesource.com/skia.git/+log/aa4f7f55829d..46e2d8d4a1ae git log aa4f7f55829d..46e2d8d4a1ae --date=short --first-parent --format='%ad %ae %s' 2019-12-06 borenet@google.com [infra] Use "mod download" and "install", not "get" in chrome_release_branch Created with: gclient setdep -r src/third_party/skia@46e2d8d4a1ae If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7aaf33d45ac09..0f77a78eb2932 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'aa4f7f55829d1d600dcdd196f0d7accd4c4b1333', + 'skia_revision': '46e2d8d4a1ae653af9de691f8497c9e5dd49637a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 73754d97cb1e3..ba6e7475b1ecf 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: bdf22e71fb98405640221ab9e009258e +Signature: 3d03c62c99c9e83c0b501a6e7f55fb92 UNUSED LICENSES: From 5f6fa92a8fd30e1a45241552df62ca9d81b31132 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 6 Dec 2019 11:14:07 -0500 Subject: [PATCH 346/591] Roll src/third_party/skia 46e2d8d4a1ae..24ee4e0341fd (2 commits) (#14157) https://skia.googlesource.com/skia.git/+log/46e2d8d4a1ae..24ee4e0341fd git log 46e2d8d4a1ae..24ee4e0341fd --date=short --first-parent --format='%ad %ae %s' 2019-12-06 bungeman@google.com Add feature support to shaper. 2019-12-06 nigeltao@google.com Allow one-pass SkWuffsCodec decoding Created with: gclient setdep -r src/third_party/skia@24ee4e0341fd If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 0f77a78eb2932..5dd957d6e0755 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '46e2d8d4a1ae653af9de691f8497c9e5dd49637a', + 'skia_revision': '24ee4e0341fdfce2f93f3b9c86bc79bd49bb6084', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index ba6e7475b1ecf..94ceeb673dfcd 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 3d03c62c99c9e83c0b501a6e7f55fb92 +Signature: 8c948c83f6cf605f68276543397504a4 UNUSED LICENSES: From e7b69ced2e2408da86261227a04f09e0c0fe905d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 6 Dec 2019 15:16:02 -0500 Subject: [PATCH 347/591] Roll src/third_party/skia 24ee4e0341fd..274a766baf2f (8 commits) (#14159) https://skia.googlesource.com/skia.git/+log/24ee4e0341fd..274a766baf2f git log 24ee4e0341fd..274a766baf2f --date=short --first-parent --format='%ad %ae %s' 2019-12-06 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-06 michaelludwig@google.com Merge consecutive entries that share proxy in bulk texture op 2019-12-06 mtklein@google.com simplify GrCCPerFlushResources::recordCopyPathInstance 2019-12-06 jsimmons@google.com Get baselines from cached default metrics if the text is empty 2019-12-06 robertphillips@google.com Add storage of programInfos to GrRecordingContext and SkDDL 2019-12-06 herb@google.com WS: reorder GrTextBlob struct and move all methods to .cpp 2019-12-06 robertphillips@google.com Pull creation of GrPipeline explicitly into the Ops' onExecute methods 2019-12-06 mtklein@google.com don't memset TriangulationVertex Created with: gclient setdep -r src/third_party/skia@274a766baf2f If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bsalomon@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: bsalomon@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 5dd957d6e0755..509a03db6b248 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '24ee4e0341fdfce2f93f3b9c86bc79bd49bb6084', + 'skia_revision': '274a766baf2fa26e3c3b75174a3ab406b611e5c7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 94ceeb673dfcd..4564c2c5af410 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8c948c83f6cf605f68276543397504a4 +Signature: 576dbf072c4630d253f61b4a71884bc1 UNUSED LICENSES: @@ -665,7 +665,6 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrRegionOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrRegionOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrShadowRRectOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrShadowRRectOp.h -FILE: ../../../third_party/skia/src/gpu/text/GrTextBlobVertexRegenerator.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorPool.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorPool.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSet.cpp From 6447d2b83124a9cfba3fefcf9a2c197ef655b977 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 6 Dec 2019 16:09:55 -0500 Subject: [PATCH 348/591] Roll fuchsia/sdk/core/mac-amd64 from VKso5... to 9C6UA... (#14161) Roll fuchsia/sdk/core/mac-amd64 from VKso5... to 9C6UA... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 509a03db6b248..190adacbb16cb 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'VKso5leyX17hxtf_SX8j2j8o5UxJDf8PsR2dihw1s90C' + 'version': '9C6UAGvxO7dHRbo0DCAb4qLfqL8Wfqbht64NfKKGn2sC' } ], 'condition': 'host_os == "mac"', From f5250ec9b1ef54a6357a8d5ef2ad164270b727f2 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Fri, 6 Dec 2019 13:47:36 -0800 Subject: [PATCH 349/591] [fuchsia] SnapToNextPhase refactor + add tests and documentation (#14158) --- .../platform/fuchsia/flutter/vsync_waiter.cc | 66 +++++++++++++++++-- shell/platform/fuchsia/flutter/vsync_waiter.h | 5 ++ .../fuchsia/flutter/vsync_waiter_unittests.cc | 45 +++++++++++++ 3 files changed, 109 insertions(+), 7 deletions(-) diff --git a/shell/platform/fuchsia/flutter/vsync_waiter.cc b/shell/platform/fuchsia/flutter/vsync_waiter.cc index d39a114693233..ca390fdd983e2 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter.cc +++ b/shell/platform/fuchsia/flutter/vsync_waiter.cc @@ -4,9 +4,14 @@ #include "vsync_waiter.h" +#include + #include + +#include "flutter/fml/logging.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/time/time_delta.h" #include "flutter/fml/trace_event.h" #include "vsync_recorder.h" @@ -61,14 +66,61 @@ VsyncWaiter::~VsyncWaiter() { ui_latch.Wait(); } -static fml::TimePoint SnapToNextPhase(fml::TimePoint value, - fml::TimePoint phase, - fml::TimeDelta interval) { - fml::TimeDelta offset = (phase - value) % interval; - if (offset < fml::TimeDelta::Zero()) { - offset = offset + interval; +/// Returns the system time at which the next frame is likely to be presented. +/// +/// Consider the following scenarios, where in both the +/// scenarious the result will be the same. +/// +/// Scenario 1: +/// presentation_interval is 2 +/// ^ ^ ^ ^ ^ +/// + + + + + +/// 0--1--2--3--4--5--6--7--8--9-- +/// + + + +/// | | +---------> result: next_presentation_time +/// | v +/// v now +/// last_presentation_time +/// +/// Scenario 2: +/// presentation_interval is 2 +/// ^ ^ ^ ^ ^ +/// + + + + + +/// 0--1--2--3--4--5--6--7--8--9-- +/// + + + +/// | | +--------->result: next_presentation_time +/// | | +/// | +>now +/// | +/// +->last_presentation_time +fml::TimePoint VsyncWaiter::SnapToNextPhase( + const fml::TimePoint now, + const fml::TimePoint last_frame_presentation_time, + const fml::TimeDelta presentation_interval) { + if (presentation_interval <= fml::TimeDelta::Zero()) { + FML_LOG(ERROR) << "Presentation interval must be positive. The value was: " + << presentation_interval.ToMilliseconds() << "ms."; + return now; + } + + if (last_frame_presentation_time >= now) { + FML_LOG(ERROR) + << "Last frame was presented in the future. Clamping to now."; + return now + presentation_interval; + } + + const fml::TimeDelta time_since_last_presentation = + now - last_frame_presentation_time; + // this will be the most likely scenario if we are rendering at a good + // frame rate, short circuiting the other checks in this case. + if (time_since_last_presentation < presentation_interval) { + return last_frame_presentation_time + presentation_interval; + } else { + const int64_t num_phases_passed = + (time_since_last_presentation / presentation_interval); + return last_frame_presentation_time + + (presentation_interval * (num_phases_passed + 1)); } - return value + offset; } void VsyncWaiter::AwaitVSync() { diff --git a/shell/platform/fuchsia/flutter/vsync_waiter.h b/shell/platform/fuchsia/flutter/vsync_waiter.h index ba1edea09e7d7..96b1cf4cd364c 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter.h +++ b/shell/platform/fuchsia/flutter/vsync_waiter.h @@ -18,6 +18,11 @@ class VsyncWaiter final : public flutter::VsyncWaiter { public: static constexpr zx_signals_t SessionPresentSignal = ZX_EVENT_SIGNALED; + static fml::TimePoint SnapToNextPhase( + const fml::TimePoint now, + const fml::TimePoint last_frame_presentation_time, + const fml::TimeDelta presentation_interval); + VsyncWaiter(std::string debug_label, zx_handle_t session_present_handle, flutter::TaskRunners task_runners); diff --git a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc index 5f00940e475b3..f1bcfc12ba350 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc +++ b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc @@ -10,6 +10,8 @@ #include #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiter.h" #include "flutter/shell/platform/fuchsia/flutter/task_runner_adapter.h" @@ -85,4 +87,47 @@ TEST_F(VsyncWaiterTest, AwaitVsync) { } } +TEST_F(VsyncWaiterTest, SnapToNextPhaseOverlapsWithNow) { + const auto now = fml::TimePoint::Now(); + const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(10); + const auto delta = fml::TimeDelta::FromNanoseconds(10); + const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase( + now, last_presentation_time, delta); + + EXPECT_EQ(now + delta, next_vsync); +} + +TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNow) { + const auto now = fml::TimePoint::Now(); + const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(9); + const auto delta = fml::TimeDelta::FromNanoseconds(10); + const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase( + now, last_presentation_time, delta); + + // math here: 10 - 9 = 1 + EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(1), next_vsync); +} + +TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNowMultiJump) { + const auto now = fml::TimePoint::Now(); + const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(34); + const auto delta = fml::TimeDelta::FromNanoseconds(10); + const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase( + now, last_presentation_time, delta); + + // zeroes: -34, -24, -14, -4, 6, ... + EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(6), next_vsync); +} + +TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNowMultiJumpAccountForCeils) { + const auto now = fml::TimePoint::Now(); + const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(20); + const auto delta = fml::TimeDelta::FromNanoseconds(16); + const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase( + now, last_presentation_time, delta); + + // zeroes: -20, -4, 12, 28, ... + EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(12), next_vsync); +} + } // namespace flutter_runner_test From 5b870a218f1584e80b13722f124dc5b0e9c5ab3a Mon Sep 17 00:00:00 2001 From: Andy Weiss Date: Sun, 8 Dec 2019 13:58:58 -0800 Subject: [PATCH 350/591] Add support for setting window size limits for glfw (#13415) Add a function to the window which calls the glfw function for fixing the size limits of the window. This can then be called after window creation. --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/glfw/client_wrapper/BUILD.gn | 1 + .../flutter_window_unittests.cc | 57 +++++++++++++++++++ .../include/flutter/flutter_window.h | 7 +++ .../testing/stub_flutter_glfw_api.cc | 8 +++ .../testing/stub_flutter_glfw_api.h | 4 ++ shell/platform/glfw/flutter_glfw.cc | 10 ++++ shell/platform/glfw/public/flutter_glfw.h | 16 ++++++ 8 files changed, 104 insertions(+) create mode 100644 shell/platform/glfw/client_wrapper/flutter_window_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2a5be4e4d7acb..78b87fec6793f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1061,6 +1061,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmservice_objec FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmservice_object.h FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_controller.cc FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_controller_unittests.cc +FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_unittests.cc FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/plugin_registrar_glfw.h diff --git a/shell/platform/glfw/client_wrapper/BUILD.gn b/shell/platform/glfw/client_wrapper/BUILD.gn index 18dd862993c0a..8d9e94a82b26c 100644 --- a/shell/platform/glfw/client_wrapper/BUILD.gn +++ b/shell/platform/glfw/client_wrapper/BUILD.gn @@ -75,6 +75,7 @@ executable("client_wrapper_glfw_unittests") { # TODO: Add more unit tests. sources = [ "flutter_window_controller_unittests.cc", + "flutter_window_unittests.cc", ] deps = [ diff --git a/shell/platform/glfw/client_wrapper/flutter_window_unittests.cc b/shell/platform/glfw/client_wrapper/flutter_window_unittests.cc new file mode 100644 index 0000000000000..31065d7e771d3 --- /dev/null +++ b/shell/platform/glfw/client_wrapper/flutter_window_unittests.cc @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h" + +#include +#include + +#include "flutter/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h" +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +// Stub implementation to validate calls to the API. +class TestGlfwApi : public testing::StubFlutterGlfwApi { + public: + // |flutter::testing::StubFlutterGlfwApi| + void SetSizeLimits(FlutterDesktopSize minimum_size, + FlutterDesktopSize maximum_size) override { + set_size_limits_called_ = true; + } + + bool set_size_limits_called() { return set_size_limits_called_; } + + private: + bool set_size_limits_called_ = false; +}; + +} // namespace + +TEST(FlutterWindowTest, SetSizeLimits) { + const std::string icu_data_path = "fake/path/to/icu"; + testing::ScopedStubFlutterGlfwApi scoped_api_stub( + std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + // This is not actually used so any non-zero value works. + auto raw_window = reinterpret_cast(1); + + auto window = std::make_unique(raw_window); + + FlutterDesktopSize minimum_size = {}; + minimum_size.width = 100; + minimum_size.height = 100; + + FlutterDesktopSize maximum_size = {}; + maximum_size.width = -1; + maximum_size.height = -1; + + window->SetSizeLimits(minimum_size, maximum_size); + + EXPECT_EQ(test_api->set_size_limits_called(), true); +} + +} // namespace flutter diff --git a/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h b/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h index 854e96a46293b..2933c5a256b60 100644 --- a/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h +++ b/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h @@ -88,6 +88,13 @@ class FlutterWindow { FlutterDesktopWindowSetPixelRatioOverride(window_, pixel_ratio); } + // Sets the min/max size of |flutter_window| in screen coordinates. Use + // kFlutterDesktopDontCare for any dimension you wish to leave unconstrained. + void SetSizeLimits(FlutterDesktopSize minimum_size, + FlutterDesktopSize maximum_size) { + FlutterDesktopWindowSetSizeLimits(window_, minimum_size, maximum_size); + } + private: // Handle for interacting with the C API's window. // diff --git a/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.cc b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.cc index 02219f59bdf7b..51b0e6b5f1cd2 100644 --- a/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.cc +++ b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.cc @@ -107,6 +107,14 @@ void FlutterDesktopWindowSetFrame(FlutterDesktopWindowRef flutter_window, } } +void FlutterDesktopWindowSetSizeLimits(FlutterDesktopWindowRef flutter_window, + FlutterDesktopSize minimum_size, + FlutterDesktopSize maximum_size) { + if (s_stub_implementation) { + s_stub_implementation->SetSizeLimits(minimum_size, maximum_size); + } +} + double FlutterDesktopWindowGetScaleFactor( FlutterDesktopWindowRef flutter_window) { if (s_stub_implementation) { diff --git a/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h index 61ff639eb169f..b0c57c46d9e24 100644 --- a/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h +++ b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h @@ -68,6 +68,10 @@ class StubFlutterGlfwApi { // Called for FlutterDesktopWindowSetPixelRatioOverride. virtual void SetPixelRatioOverride(double pixel_ratio) {} + // Called for FlutterDesktopWindowSetSizeLimits. + virtual void SetSizeLimits(FlutterDesktopSize minimum_size, + FlutterDesktopSize maximum_size) {} + // Called for FlutterDesktopRunWindowEventLoopWithTimeout. virtual bool RunWindowEventLoopWithTimeout(uint32_t millisecond_timeout) { return true; diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index b91dbdd93cf62..a38cb60ae8a47 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -34,6 +34,8 @@ using UniqueGLFWwindowPtr = std::unique_ptr; static_assert(FLUTTER_ENGINE_VERSION == 1, ""); +const int kFlutterDesktopDontCare = GLFW_DONT_CARE; + static constexpr double kDpPerInch = 160.0; // Struct for storing state within an instance of the GLFW Window. @@ -736,6 +738,14 @@ void FlutterDesktopWindowSetPixelRatioOverride( } } +void FlutterDesktopWindowSetSizeLimits(FlutterDesktopWindowRef flutter_window, + FlutterDesktopSize minimum_size, + FlutterDesktopSize maximum_size) { + glfwSetWindowSizeLimits(flutter_window->window, minimum_size.width, + minimum_size.height, maximum_size.width, + maximum_size.height); +} + bool FlutterDesktopRunWindowEventLoopWithTimeout( FlutterDesktopWindowControllerRef controller, uint32_t timeout_milliseconds) { diff --git a/shell/platform/glfw/public/flutter_glfw.h b/shell/platform/glfw/public/flutter_glfw.h index ed6f87fb0d7c8..610c63ff7948a 100644 --- a/shell/platform/glfw/public/flutter_glfw.h +++ b/shell/platform/glfw/public/flutter_glfw.h @@ -16,6 +16,9 @@ extern "C" { #endif +// Indicates that any value is acceptable for an otherwise required property. +extern const int32_t kFlutterDesktopDontCare; + // Opaque reference to a Flutter window controller. typedef struct FlutterDesktopWindowControllerState* FlutterDesktopWindowControllerRef; @@ -26,6 +29,12 @@ typedef struct FlutterDesktopWindow* FlutterDesktopWindowRef; // Opaque reference to a Flutter engine instance. typedef struct FlutterDesktopEngineState* FlutterDesktopEngineRef; +// Properties representing a generic rectangular size. +typedef struct { + int32_t width; + int32_t height; +} FlutterDesktopSize; + // Properties for configuring a Flutter engine instance. typedef struct { // The path to the flutter_assets folder for the application to be run. @@ -167,6 +176,13 @@ FLUTTER_EXPORT void FlutterDesktopWindowSetPixelRatioOverride( FlutterDesktopWindowRef flutter_window, double pixel_ratio); +// Sets the min/max size of |flutter_window| in screen coordinates. Use +// kFlutterDesktopDontCare for any dimension you wish to leave unconstrained. +FLUTTER_EXPORT void FlutterDesktopWindowSetSizeLimits( + FlutterDesktopWindowRef flutter_window, + FlutterDesktopSize minimum_size, + FlutterDesktopSize maximum_size); + // Runs an instance of a headless Flutter engine. // // Returns a null pointer in the event of an error. From ff6fa47659c00de52f5a64f903b6e08b6a59afc0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 8 Dec 2019 21:43:24 -0500 Subject: [PATCH 351/591] Roll src/third_party/skia 274a766baf2f..b60ec7f98b08 (9 commits) (#14256) https://skia.googlesource.com/skia.git/+log/274a766baf2f..b60ec7f98b08 git log 274a766baf2f..b60ec7f98b08 --date=short --first-parent --format='%ad %ae %s' 2019-12-08 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update SKP version 2019-12-06 herb@google.com Fix fix chrome build bots 2019-12-06 bungeman@google.com Protect against invalid text. 2019-12-06 herb@google.com Change from std to skstd for aligned_union_t 2019-12-06 herb@google.com Turn GrTextBlob vertices into actual structs 2019-12-06 jvanverth@google.com Fix typo in box-plane test. 2019-12-06 brianosman@google.com Switch to float vertex colors for wide color vertex attribs 2019-12-06 michaelludwig@google.com Avoid implicit conversion back to int 2019-12-06 kjlubick@google.com [canvaskit] Add _RTShaderFactory for demos Created with: gclient setdep -r src/third_party/skia@b60ec7f98b08 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 190adacbb16cb..060542ee00437 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '274a766baf2fa26e3c3b75174a3ab406b611e5c7', + 'skia_revision': 'b60ec7f98b081c593c439da2f4619c192e26aa98', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 4564c2c5af410..087f59440dfd2 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 576dbf072c4630d253f61b4a71884bc1 +Signature: ccf7b6206918956bb9c05a75904a4da0 UNUSED LICENSES: From 4beaa696fc59a159aaa31a9dc0fdbd409bc8720c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 9 Dec 2019 01:40:58 -0500 Subject: [PATCH 352/591] Roll src/third_party/skia b60ec7f98b08..9910d829ad18 (3 commits) (#14257) https://skia.googlesource.com/skia.git/+log/b60ec7f98b08..9910d829ad18 git log b60ec7f98b08..9910d829ad18 --date=short --first-parent --format='%ad %ae %s' 2019-12-09 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 11e6944ec5a8..27c89d2b5c56 (9 commits) 2019-12-09 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader b9f03f47d5f5..31916f494c2e (7 commits) 2019-12-09 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 36a639fc2d93..4e96c2bed379 (454 commits) Created with: gclient setdep -r src/third_party/skia@9910d829ad18 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 060542ee00437..2db7b9323aff2 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'b60ec7f98b081c593c439da2f4619c192e26aa98', + 'skia_revision': '9910d829ad18341d7bc410151014649c4a9d7b19', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 087f59440dfd2..ab1c7ddf6c91b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ccf7b6206918956bb9c05a75904a4da0 +Signature: 0eba0a09903d72f0810fc389684330ee UNUSED LICENSES: From d9f856510d1636fe45f7bf7246ce19103842e280 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 9 Dec 2019 09:57:26 -0500 Subject: [PATCH 353/591] Roll src/third_party/skia 9910d829ad18..f0148c4881df (1 commits) (#14258) https://skia.googlesource.com/skia.git/+log/9910d829ad18..f0148c4881df git log 9910d829ad18..f0148c4881df --date=short --first-parent --format='%ad %ae %s' 2019-12-09 nigeltao@google.com Release SkWuffsCodec two-pass memory earlier Created with: gclient setdep -r src/third_party/skia@f0148c4881df If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2db7b9323aff2..710a24d8d9f73 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '9910d829ad18341d7bc410151014649c4a9d7b19', + 'skia_revision': 'f0148c4881dffc95d73d71df23561d280a916fbe', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index ab1c7ddf6c91b..81a43fc5515ba 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 0eba0a09903d72f0810fc389684330ee +Signature: 1cca375d5e74ce101673e792ca4da988 UNUSED LICENSES: From 562fb8fc8ed8a687749dba9f0c00885fb8d1db6c Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 9 Dec 2019 09:22:33 -0800 Subject: [PATCH 354/591] Roll src/third_party/dart a9c77229c2..7fcaafbb3e (88 commits) (#14260) dart-lang/sdk@7fcaafbb3e [infra] Move IE registry settings from Puppet to the test runner dart-lang/sdk@de11b7b373 [cfe] Implement legacy erasure dart-lang/sdk@168cf90131 [vm] Make ELF loader Fuchsia compatible. dart-lang/sdk@db525fe0f9 [kernel] Remove old unused 'method call' transformer dart-lang/sdk@dd7499dd22 Revert "[infra] Try to handle failing to restore the crash resource limit instead." dart-lang/sdk@ffd757640d Duplicate kernel package's graph.dart in analyzer, and prepare to publish 0.39.2+1. dart-lang/sdk@eb862ca54d [dartdevc] Revert change to libraries.yaml to fix analyzer test bot dart-lang/sdk@9f2be5cadf Move tests for 6 more diagnostic codes to the diagnostics/ directory dart-lang/sdk@159c6da02a Add test for a @required field formal dart-lang/sdk@bd8da4757e [vm/ffi] Remove simulator code dart-lang/sdk@52dc7690ef Issue 39667. Use flow analysis in IfElement. dart-lang/sdk@c17aff87e1 Restore skipping all dynamic type arguments in DartType.displayName dart-lang/sdk@acac2d8d63 NNBD preview: Extract Dart page styles into their own file dart-lang/sdk@c31db57c97 [dartfuzz] Add support for simple recursion dart-lang/sdk@942c58f32d [dartdevc] Fix build errors in runtime patch files dart-lang/sdk@98da22a11f [observatory] Display process-wide memory usage with heap snapshots. dart-lang/sdk@f024d9dcce NullabilityEliminator - Never to Null*, remove required from formal parameters. dart-lang/sdk@310c2eb5c4 Display target file name as link text dart-lang/sdk@80aa5fda4b [observatory] Account for external size in the "Classes (table)" and "Classes (treemap)" views. dart-lang/sdk@b92cd2cb95 Deprecate DartType.displayName, use getDisplayString() instead. dart-lang/sdk@0939101320 When reporting unused elements, do not count a CommentReference as usage dart-lang/sdk@70750329af [nnbd_migration] use animation/delay to make tooltips easier to use dart-lang/sdk@afdc8caca8 [dartdevc] Finishing migration of dart:_interceptors and dart:_runtime. dart-lang/sdk@84cc410c7e NNBD preview: move navigation; clean up styles dart-lang/sdk@4c442cd73a [dart2js] Remove `treatAsDynamic`. dart-lang/sdk@de53a2c33c [vm] Fix late local variables in AST mode dart-lang/sdk@6267a8194e [ VM / dart:io ] Fix issue where SIGPROF is disabled in processes spawned via Process on POSIX dart-lang/sdk@16bc1806ea (html) remove type parameter from callback argument dart-lang/sdk@652926bbaf Deprecate Element.getAncestor, replacing it with methods matching those in AstNode dart-lang/sdk@dfa32032fd Add scaffolding to trial_migration for more package sources. dart-lang/sdk@d85eccb186 Prepare to publish analyzer 0.39.2. dart-lang/sdk@a455e099bb Ignore _printEvent as an unused element dart-lang/sdk@3281a4ab7f Move tests for 10 diagnostic codes to diagnostics/ files dart-lang/sdk@4b21941d33 Fix prefer_iterable_wheretype in analyzer. dart-lang/sdk@7bf088e4dd [release] Prepare the CHANGELOG for 2.7.0 dart-lang/sdk@54e1c16c1c Fix empty_catches in analyzer. dart-lang/sdk@54b7543c55 Fix unnecessary_const in analyzer. dart-lang/sdk@80a400297a [cfe] Add initial version of support for nonfunction-type-aliases dart-lang/sdk@97c2ca5ff1 [cfe] Use valid static types and file offsets in collections transformation dart-lang/sdk@daa400bada [sdk] Fix all uses of the view() constructor to always use offsetInBytes. dart-lang/sdk@0dc6d59d52 [CFE] Fix crash caused by name clash of loadLibrary tearoff method dart-lang/sdk@0bca6aaf10 Fix avoid_return_types_on_setters in analyzer. dart-lang/sdk@e11cf75bd9 [dart2js] Propagate kernel variance to emit annotations in RTI table. dart-lang/sdk@364dd8936b Fix unnecessary_new in analyzer. dart-lang/sdk@723baf767f [analyzer] fix an npe in fix_internal.dart:2171 dart-lang/sdk@5adff879e3 Widen the SDK constraint on dart_internal to include 2.7.0. dart-lang/sdk@eee20b4e60 Fix prefer_equal_for_default_values in analyzer. dart-lang/sdk@f772b572c8 Fix curly_braces_in_flow_control_structures lint. dart-lang/sdk@bf7f27a2c5 Remove comment about inlining constants. dart-lang/sdk@b99599bae1 More cleanup of the loader code. dart-lang/sdk@a3953b607d linter 0.1.105 dart-lang/sdk@41d8b9ee63 NNBD Migrator: Handle super redirecting initializers dart-lang/sdk@baf1afa902 [dartdevc] Opt in dart:collection patch file for NNBD support dart-lang/sdk@845fccc337 Issue 35818. Use getDisplayString() in _ElementWriter. dart-lang/sdk@e20ff1e054 [vm] Prevent late fields from being unboxed dart-lang/sdk@7ac3f3e904 NNBD migrator: Visit metadata on top-level functions dart-lang/sdk@eba38af5d1 Add DartType.getDisplayString() dart-lang/sdk@f7aff739bd Fix Isolate.packageRoot to account for the fact that it returns a Future. dart-lang/sdk@1ff20f1e24 Rename "inv_cse_licm.dart" to "inv_cse_licm_test.dart". dart-lang/sdk@7fcff7e87f Allow passing the sdk root for analysis to trial_migration. dart-lang/sdk@929877c822 [gardening] Fix gen_kernel path on Windows for product_aot_kernel_test.dart. dart-lang/sdk@bef857b6cb Fix dart:collection analysis errors in NNBD fork dart-lang/sdk@88e26cd5d9 [dartdevc] Fix DDK sourcemap tests to use the right version of the SDK dart-lang/sdk@1d4c45d2d0 Re-land "[vm/cfe] Elaborate for-in statements during async transform" dart-lang/sdk@47a9ebea76 [cfe] Make API of as-instance-of and legacy LUB/GLB library-specific dart-lang/sdk@ae14121ce7 [benchmark] Test addition of the whole d8 directory to builds. dart-lang/sdk@e4e3f3fbc4 [cfe] Handle for-in on type variables in TypeEnvironment.forInElementType dart-lang/sdk@c1565a7e64 [SDK] Cleans up a few unused functions in core_types.dart. dart-lang/sdk@225682f868 [infra] Try to handle failing to restore the crash resource limit instead. dart-lang/sdk@1a41670ed0 [infra] Temporarily disable exception for invalid core pattern dart-lang/sdk@47383da053 Update LUB for Null vs star. dart-lang/sdk@5ff9a2ed37 Cleanup the loader code to remove some unused pieces. dart-lang/sdk@b8dbaaa527 [dartfuzz] Fix infinite recursion in call methods dart-lang/sdk@6dc48ab999 [dartdevc] Destructure optional positional parameters dart-lang/sdk@53bbe6c88c [dart2js] Added variance support for static subtype checking. dart-lang/sdk@f56b0f6907 [vm/aot/tfa] Whole-program constant propagation dart-lang/sdk@9929b53c71 [vm, gc] Allow old-space collections for allocations during safepoints. dart-lang/sdk@a4bac1fb10 [dartdevc] Migrating dart:_js_helper and dart:_interceptors to NNBD. dart-lang/sdk@2bb60763c3 Fix remove_type_annotation fix/assist to work with for loops (issue 39628) dart-lang/sdk@c52acadd15 [vm/ffi] Split up some tests/ffi into vmspecific and non-vmspecific dart-lang/sdk@1bbbc9f599 [vm/aot] Check in test to ensure in PRODUCT mode we don't retain any code in dart:vmservice_io/dart:_vmservice dart-lang/sdk@eec49f34c4 Always truncate thread names on linux to 15 chars (16 with the null terminator). dart-lang/sdk@28fc03709d NNBD preview: Improve details for overridden nullable parameter types dart-lang/sdk@1c2ad7e2e1 [vm/aot] Remove assertion around committing object pool into GOP. dart-lang/sdk@60afc24502 [infra] Convert some multi-tests to the new static error framework dart-lang/sdk@b0155a72a7 [vm/ffi] Add script to extract existing positive ffi tests into bundle to be used for flutter/flutter integration test dart-lang/sdk@f9327d3bac [SDK] Adds --lazy-async-stack support for async*. dart-lang/sdk@42a4442521 [cfe] Handle promotion of Null to Never --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 710a24d8d9f73..fbc0b08508c12 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'a9c77229c20db10521dcf565b2d64aae3d740d9a', + 'dart_revision': '7fcaafbb3e8689cf3494078df6199c053a61ec80', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -63,7 +63,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.15.7', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.104', + 'dart_linter_tag': '0.1.105+1', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.3', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1ccc601e2ff0f..8088e5c472e01 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 6daeaf1ba161a771918102a7ef9c0889 +Signature: 3c0bee0502d504a936b8199a32b4caad UNUSED LICENSES: @@ -7555,6 +7555,7 @@ FILE: ../../../third_party/dart/runtime/bin/entrypoints_verification_test_extens FILE: ../../../third_party/dart/runtime/bin/entrypoints_verification_test_extension_dllmain_win.cc FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_dynamic_library.cc FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions.cc +FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc FILE: ../../../third_party/dart/runtime/bin/ifaddrs-android.cc FILE: ../../../third_party/dart/runtime/bin/ifaddrs-android.h FILE: ../../../third_party/dart/runtime/bin/namespace_fuchsia.h From 3b100d77b1e94262b3cf865518f958e855ad5b60 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 9 Dec 2019 13:44:25 -0500 Subject: [PATCH 355/591] Roll fuchsia/sdk/core/linux-amd64 from 2Nav3... to Zkpa_... (#14261) Roll fuchsia/sdk/core/linux-amd64 from 2Nav3... to Zkpa_... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index fbc0b08508c12..4aab773fca531 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': '2Nav3tcxx2Ds1aiVVybULj9D8HgFjiGX1mDizCy5Vh4C' + 'version': 'Zkpa_dlYeNNn7sPFtAsDgjHM0hH8WArOqHxUo7p9GowC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 64262b6ecf14a..bac1fabff6aac 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 1a927dbf378d689cf7aed0c567181af4 +Signature: 27d62b2ba3dbe139698c88787b0db230 UNUSED LICENSES: @@ -468,12 +468,14 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json @@ -538,6 +540,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/memfs/meta.json @@ -947,6 +950,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wait.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wchar.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wctype.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wordexp.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/exception.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/clock.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/definitions.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/Scrt1.o @@ -1201,6 +1205,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wait.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wchar.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wctype.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wordexp.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/exception.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/clock.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/definitions.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/Scrt1.o @@ -1360,6 +1365,8 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_address FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_control.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_pipe.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/admin.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/hwinfo.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/image_pipe2.fidl @@ -1370,6 +1377,8 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/property_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/namedplace.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json @@ -1458,6 +1467,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/device_listener.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/pointer_capture.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/types.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.vectorial/meta.json @@ -1534,6 +1544,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/inspect/health.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/hierarchy.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/health.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/hierarchy.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/inspect.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/reader.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/value_list.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/block.h @@ -1543,6 +1554,11 @@ FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/reader.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/scanner.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/snapshot.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/include/lib/inspect/service/cpp/reader.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/include/lib/inspect/service/cpp/service.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/reader.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/service.cc FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/memfs/meta.json @@ -2128,12 +2144,14 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json @@ -2198,6 +2216,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/memfs/meta.json @@ -2717,12 +2736,12 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fit/sequencer.cc FILE: ../../../fuchsia/sdk/linux/pkg/fit/single_threaded_executor.cc FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/images.cc FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/include/lib/images/cpp/images.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/inspect.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/inspector.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/heap.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/limits.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/state.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/types.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/inspect.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/inspector.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/heap.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/state.cc FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/types.cc @@ -2858,7 +2877,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story/create_module_parame FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story/story_shell.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story/story_state.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/surface/surface.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/user_intelligence/user_intelligence_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.mdns/mdns.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/flat_namespace.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/commands.fidl From 5e346c425cf50cd9318b051529a6499ad2c5a5fa Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 9 Dec 2019 13:59:07 -0500 Subject: [PATCH 356/591] Roll src/third_party/skia f0148c4881df..c937bc50250d (11 commits) (#14262) https://skia.googlesource.com/skia.git/+log/f0148c4881df..c937bc50250d git log f0148c4881df..c937bc50250d --date=short --first-parent --format='%ad %ae %s' 2019-12-09 brianosman@google.com Improvements to uniform handling in runtime SkSL 2019-12-09 halcanary@google.com [minor] fix definition with different parameter names 2019-12-09 kjlubick@google.com [canvaskit] roll to 0.10.0 2019-12-09 herb@google.com Remove the CACHE_SANITY_CHECK system. 2019-12-09 bsalomon@google.com Add testCompile function to SkRuntimeColorFilterFactory for Chrome test 2019-12-09 fmalita@chromium.org [skottie] Explicit seek(0) in tools 2019-12-09 bsalomon@google.com Support mirror-repeat in GrTextureDomain 2019-12-09 mtklein@google.com lift Wno-class-memaccess 2019-12-09 mtklein@google.com mark SkMD5->SkUUID copy as sane 2019-12-09 kjlubick@google.com [canvaskit] Expose SkShader combiners 2019-12-09 nigeltao@google.com Add SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY option Created with: gclient setdep -r src/third_party/skia@c937bc50250d If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4aab773fca531..c4eb371c1f97c 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f0148c4881dffc95d73d71df23561d280a916fbe', + 'skia_revision': 'c937bc50250d7723843471cc0d0dc409fb570229', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 81a43fc5515ba..8a7c0e333cc37 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1cca375d5e74ce101673e792ca4da988 +Signature: c3729b248a3e5fb802fa56c9d6a3e387 UNUSED LICENSES: From a614c0a2d45fe6133aae02379af6227d6a621125 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 9 Dec 2019 11:00:44 -0800 Subject: [PATCH 357/591] Convert radians to degrees in canvaskit backend for drawArc (#14163) --- lib/web_ui/lib/src/engine/compositor/canvas.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine/compositor/canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas.dart index 1f765793900e2..d32f6f4adee01 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas.dart @@ -53,10 +53,11 @@ class SkCanvas { bool useCenter, ui.Paint paint, ) { + const double toDegrees = 180 / math.pi; skCanvas.callMethod('drawArc', [ makeSkRect(oval), - startAngle, - sweepAngle, + startAngle * toDegrees, + sweepAngle * toDegrees, useCenter, makeSkPaint(paint), ]); From b9080c92b98a38cce385e7d9f3233c046d6fe0e8 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 9 Dec 2019 13:16:43 -0800 Subject: [PATCH 358/591] Roll src/third_party/dart 7fcaafbb3e..8b8894648f (1 commits) (#14266) dart-lang/sdk@8b8894648f [vm] Fix Meteor regression in "[vm] Enable multiple entry-points for unoptimized calls." --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c4eb371c1f97c..c905ddd094654 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '7fcaafbb3e8689cf3494078df6199c053a61ec80', + 'dart_revision': '8b8894648f0dbe53b046133caab11fbe8e14dc3f', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 8088e5c472e01..b56dca67acee8 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 3c0bee0502d504a936b8199a32b4caad +Signature: 63d3e0cd0caf64c5b89692fc9f875051 UNUSED LICENSES: From 3a2ec830edcfdf39d38c21e0dd7a4b325332be52 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 9 Dec 2019 14:28:13 -0800 Subject: [PATCH 359/591] Use the versioned canvaskit from unpkg. (#14264) The one from particles.skia.org is not intended for production use. --- lib/web_ui/lib/src/engine/compositor/initialization.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/compositor/initialization.dart b/lib/web_ui/lib/src/engine/compositor/initialization.dart index 9f043e15cf827..27a41a0450cb4 100644 --- a/lib/web_ui/lib/src/engine/compositor/initialization.dart +++ b/lib/web_ui/lib/src/engine/compositor/initialization.dart @@ -9,7 +9,7 @@ const bool experimentalUseSkia = bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false); /// The URL to use when downloading the CanvasKit script and associated wasm. -const String canvasKitBaseUrl = 'https://particles.skia.org/static/'; +const String canvasKitBaseUrl = 'https://unpkg.com/canvaskit-wasm@0.10.0/bin/'; /// Initialize the Skia backend. /// From b6bb57d014744681b0e2634e6bb8a07b0f14b4be Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 9 Dec 2019 18:00:33 -0500 Subject: [PATCH 360/591] Roll src/third_party/skia c937bc50250d..095d2468a075 (7 commits) (#14269) https://skia.googlesource.com/skia.git/+log/c937bc50250d..095d2468a075 git log c937bc50250d..095d2468a075 --date=short --first-parent --format='%ad %ae %s' 2019-12-09 bsalomon@google.com GrTextureDomain: Go back to doing vector impl when modes are the same 2019-12-09 jlavrova@google.com Small changes 2019-12-09 halcanary@google.com skparagraph: fix another leak 2019-12-09 herb@google.com Pass SkGlyphRunList to GrTextBlob::Make 2019-12-09 herb@google.com Cleanup GrTextBlob management code 2019-12-09 jvanverth@google.com Add persistent shader cache support to Metal. 2019-12-09 halcanary@google.com skparagraph: fix leak Created with: gclient setdep -r src/third_party/skia@095d2468a075 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c905ddd094654..9479cc7b6a3d1 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c937bc50250d7723843471cc0d0dc409fb570229', + 'skia_revision': '095d2468a075e01bf99ab51c76620f7cc4629f62', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 8a7c0e333cc37..c4bf19ec16575 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c3729b248a3e5fb802fa56c9d6a3e387 +Signature: 1b07920409af783a5d68de2bc2722f19 UNUSED LICENSES: From b2ab78fb4105444825bcacdf088c74802f6b0148 Mon Sep 17 00:00:00 2001 From: Craig Stout Date: Fri, 6 Dec 2019 16:48:30 -0800 Subject: [PATCH 361/591] [shell][fuchsia] Add lib/async/default.h For async_get_default_dispatcher(). --- shell/platform/fuchsia/flutter/component.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc index 4ba6f06e71735..2cbd33586e1d8 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include From 2805da9e5dd2a3bfa8c45fbe9a800ceb13464db6 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Mon, 9 Dec 2019 16:27:14 -0800 Subject: [PATCH 362/591] Enable web engine unit tests on Firefox (#14267) * Enable web engine unit tests on Firefox * addressing PR comments * addressing PR comments * fix the version name on the lock file --- .cirrus.yml | 4 +- lib/web_ui/dev/browser_lock.yaml | 2 +- lib/web_ui/dev/firefox.dart | 3 +- lib/web_ui/dev/test_runner.dart | 108 +++++++++++------- lib/web_ui/test/compositing_test.dart | 3 +- lib/web_ui/test/dom_renderer_test.dart | 3 +- .../semantics/semantics_helper_test.dart | 5 +- .../test/engine/semantics/semantics_test.dart | 3 +- lib/web_ui/test/paragraph_test.dart | 12 +- .../test/text/font_collection_test.dart | 10 +- lib/web_ui/test/text_editing_test.dart | 15 ++- lib/web_ui/test/text_test.dart | 6 +- 12 files changed, 105 insertions(+), 69 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 17dafd5cc2f2b..3c1704595c788 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -75,8 +75,8 @@ task: $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get cd $ENGINE_PATH/src/flutter/lib/web_ui $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get - export DART="$ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dart" - $DART dev/firefox_installer_test.dart + export FELT="$ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dart dev/felt.dart" + $FELT test --browser=firefox - name: build_and_test_android_unopt_debug env: USE_ANDROID: "True" diff --git a/lib/web_ui/dev/browser_lock.yaml b/lib/web_ui/dev/browser_lock.yaml index c2da9e63e9fa3..169220c34f9b7 100644 --- a/lib/web_ui/dev/browser_lock.yaml +++ b/lib/web_ui/dev/browser_lock.yaml @@ -4,4 +4,4 @@ chrome: Linux: 695653 Mac: 695656 firefox: - version: 69.0.3 + version: '71.0' diff --git a/lib/web_ui/dev/firefox.dart b/lib/web_ui/dev/firefox.dart index eb05a07a5553b..787daf6befc8a 100644 --- a/lib/web_ui/dev/firefox.dart +++ b/lib/web_ui/dev/firefox.dart @@ -48,12 +48,11 @@ class Firefox extends Browser { var dir = createTempDir(); var args = [ url.toString(), - if (!debug) '--headless', + '--headless', '-width $kMaxScreenshotWidth', '-height $kMaxScreenshotHeight', '-new-window', '-new-instance', - '-prefs { "dom.disable_beforeunload" = true, "toolkit.startup.max_resumed_crashes"=999999 } ', '--start-debugger-server $kDevtoolsPort', ]; diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 48891327206f0..d21aa65fd2a70 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -83,6 +83,8 @@ class TestCommand extends Command { String get browser => argResults['browser']; + bool get isChrome => argResults['browser'] == 'chrome'; + /// When running screenshot tests writes them to the file system into /// ".dart_tool/goldens". bool get doUpdateScreenshotGoldens => argResults['update-screenshot-goldens']; @@ -98,54 +100,74 @@ class TestCommand extends Command { 'test', )); - // Separate screenshot tests from unit-tests. Screenshot tests must run - // one at a time. Otherwise, they will end up screenshotting each other. - // This is not an issue for unit-tests. - final FilePath failureSmokeTestPath = FilePath.fromWebUi( - 'test/golden_tests/golden_failure_smoke_test.dart', - ); - final List screenshotTestFiles = []; - final List unitTestFiles = []; - - for (io.File testFile - in testDir.listSync(recursive: true).whereType()) { - final FilePath testFilePath = FilePath.fromCwd(testFile.path); - if (!testFilePath.absolute.endsWith('_test.dart')) { - // Not a test file at all. Skip. - continue; - } - if (testFilePath == failureSmokeTestPath) { - // A smoke test that fails on purpose. Skip. - continue; + // Screenshot tests and smoke tests only run in Chrome. + if (isChrome) { + // Separate screenshot tests from unit-tests. Screenshot tests must run + // one at a time. Otherwise, they will end up screenshotting each other. + // This is not an issue for unit-tests. + final FilePath failureSmokeTestPath = FilePath.fromWebUi( + 'test/golden_tests/golden_failure_smoke_test.dart', + ); + final List screenshotTestFiles = []; + final List unitTestFiles = []; + + for (io.File testFile + in testDir.listSync(recursive: true).whereType()) { + final FilePath testFilePath = FilePath.fromCwd(testFile.path); + if (!testFilePath.absolute.endsWith('_test.dart')) { + // Not a test file at all. Skip. + continue; + } + if (testFilePath == failureSmokeTestPath) { + // A smoke test that fails on purpose. Skip. + continue; + } + + if (path.split(testFilePath.relativeToWebUi).contains('golden_tests')) { + screenshotTestFiles.add(testFilePath); + } else { + unitTestFiles.add(testFilePath); + } } - if (path.split(testFilePath.relativeToWebUi).contains('golden_tests')) { - screenshotTestFiles.add(testFilePath); - } else { - unitTestFiles.add(testFilePath); + + // This test returns a non-zero exit code on purpose. Run it separately. + if (io.Platform.environment['CIRRUS_CI'] != 'true') { + await _runTestBatch( + [failureSmokeTestPath], + concurrency: 1, + expectFailure: true, + ); + _checkExitCode(); } - } - // This test returns a non-zero exit code on purpose. Run it separately. - if (io.Platform.environment['CIRRUS_CI'] != 'true') { - await _runTestBatch( - [failureSmokeTestPath], - concurrency: 1, - expectFailure: true, - ); + // Run all unit-tests as a single batch. + await _runTestBatch(unitTestFiles, concurrency: 10, expectFailure: false); _checkExitCode(); - } - - // Run all unit-tests as a single batch. - await _runTestBatch(unitTestFiles, concurrency: 10, expectFailure: false); - _checkExitCode(); - // Run screenshot tests one at a time. - for (FilePath testFilePath in screenshotTestFiles) { - await _runTestBatch( - [testFilePath], - concurrency: 1, - expectFailure: false, - ); + // Run screenshot tests one at a time. + for (FilePath testFilePath in screenshotTestFiles) { + await _runTestBatch( + [testFilePath], + concurrency: 1, + expectFailure: false, + ); + _checkExitCode(); + } + } else { + final List unitTestFiles = []; + for (io.File testFile + in testDir.listSync(recursive: true).whereType()) { + final FilePath testFilePath = FilePath.fromCwd(testFile.path); + if (!testFilePath.absolute.endsWith('_test.dart')) { + // Not a test file at all. Skip. + continue; + } + if(!path.split(testFilePath.relativeToWebUi).contains('golden_tests')) { + unitTestFiles.add(testFilePath); + } + } + // Run all unit-tests as a single batch. + await _runTestBatch(unitTestFiles, concurrency: 10, expectFailure: false); _checkExitCode(); } } diff --git a/lib/web_ui/test/compositing_test.dart b/lib/web_ui/test/compositing_test.dart index ea0b98901c55c..377d32bf7d056 100644 --- a/lib/web_ui/test/compositing_test.dart +++ b/lib/web_ui/test/compositing_test.dart @@ -173,7 +173,8 @@ void main() { expect(picture.buildCount, 1); expect(picture.updateCount, 0); expect(picture.applyPaintCount, 2); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); }); } diff --git a/lib/web_ui/test/dom_renderer_test.dart b/lib/web_ui/test/dom_renderer_test.dart index 7ff6fc078c29a..3fa1c678f0322 100644 --- a/lib/web_ui/test/dom_renderer_test.dart +++ b/lib/web_ui/test/dom_renderer_test.dart @@ -115,7 +115,8 @@ void main() { final DomRenderer renderer = DomRenderer(); renderer.reset(); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('accesibility placeholder is attached after creation', () { DomRenderer(); diff --git a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart index c7fd8bc204176..5aa8bc4f75c0f 100644 --- a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart @@ -21,7 +21,7 @@ void main() { if (_placeholder != null) { _placeholder.remove(); } - if(desktopSemanticsEnabler?.semanticsActivationTimer != null) { + if (desktopSemanticsEnabler?.semanticsActivationTimer != null) { desktopSemanticsEnabler.semanticsActivationTimer.cancel(); desktopSemanticsEnabler.semanticsActivationTimer = null; } @@ -142,6 +142,7 @@ void main() { mobileSemanticsEnabler.tryEnableSemantics(event); expect(shouldForwardToFramework, true); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); }); } diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index 892e19b247f0b..e5a979301c4da 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -806,7 +806,8 @@ void _testTextField() { expect(await logger.actionLog.first, ui.SemanticsAction.tap); semantics().semanticsEnabled = false; - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); } void _testCheckables() { diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart index fce82efd3561a..dba932d6b3f8e 100644 --- a/lib/web_ui/test/paragraph_test.dart +++ b/lib/web_ui/test/paragraph_test.dart @@ -7,7 +7,7 @@ import 'package:ui/ui.dart'; import 'package:test/test.dart'; -void testEachMeasurement(String description, VoidCallback body) { +void testEachMeasurement(String description, VoidCallback body, {bool skip}) { test(description, () async { try { TextMeasurementService.initialize(rulerCacheCapacity: 2); @@ -15,7 +15,7 @@ void testEachMeasurement(String description, VoidCallback body) { } finally { TextMeasurementService.clearCache(); } - }); + }, skip: skip); test('$description (canvas measurement)', () async { try { TextMeasurementService.initialize(rulerCacheCapacity: 2); @@ -25,7 +25,7 @@ void testEachMeasurement(String description, VoidCallback body) { TextMeasurementService.enableExperimentalCanvasImplementation = false; TextMeasurementService.clearCache(); } - }); + }, skip: skip); } void main() async { @@ -104,7 +104,8 @@ void main() async { expect(paragraph.minIntrinsicWidth, fontSize * 10.0); expect(paragraph.maxIntrinsicWidth, fontSize * 10.0); } - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); testEachMeasurement('predictably lays out a multi-line rich paragraph', () { for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { @@ -126,7 +127,8 @@ void main() async { expect(paragraph.minIntrinsicWidth, fontSize * 5.0); expect(paragraph.maxIntrinsicWidth, fontSize * 16.0); } - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); testEachMeasurement('getBoxesForRange returns a box', () { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( diff --git a/lib/web_ui/test/text/font_collection_test.dart b/lib/web_ui/test/text/font_collection_test.dart index c3cb51d865284..6581e4aae4be6 100644 --- a/lib/web_ui/test/text/font_collection_test.dart +++ b/lib/web_ui/test/text/font_collection_test.dart @@ -84,7 +84,7 @@ void main() { expect(fontFamilyList.length, equals(2)); expect(fontFamilyList, contains('\'/Ahem\'')); expect(fontFamilyList, contains('/Ahem')); - }); + }, skip: (browserEngine == BrowserEngine.firefox)); test('Register Asset twice with exclamation mark', () async { final String _testFontFamily = 'Ahem!!ahem'; @@ -101,7 +101,7 @@ void main() { expect(fontFamilyList.length, equals(2)); expect(fontFamilyList, contains('\'Ahem!!ahem\'')); expect(fontFamilyList, contains('Ahem!!ahem')); - }); + }, skip: (browserEngine == BrowserEngine.firefox)); test('Register Asset twice with coma', () async { final String _testFontFamily = 'Ahem ,ahem'; @@ -118,7 +118,8 @@ void main() { expect(fontFamilyList.length, equals(2)); expect(fontFamilyList, contains('\'Ahem ,ahem\'')); expect(fontFamilyList, contains('Ahem ,ahem')); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('Register Asset twice with a digit at the start of a token', () async { final String testFontFamily = 'Ahem 1998'; @@ -136,5 +137,6 @@ void main() { expect(fontFamilyList, contains('Ahem 1998')); expect(fontFamilyList, contains('\'Ahem 1998\'')); }); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); } diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index 9a5e32f973fba..621747353486b 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -186,7 +186,8 @@ void main() { // There should be no input action. expect(lastInputAction, isNull); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('Can set editing state correctly', () { editingElement.enable( @@ -217,7 +218,8 @@ void main() { // There should be no input action. expect(lastInputAction, isNull); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('Multi-line mode also works', () { // The textarea element is created lazily. @@ -262,7 +264,8 @@ void main() { // There should be no input action. expect(lastInputAction, isNull); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('Same instance can be re-enabled with different config', () { // Make sure there's nothing in the DOM yet. @@ -922,7 +925,8 @@ void main() { ); hideKeyboard(); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('Multi-line mode also works', () { final MethodCall setClient = MethodCall( @@ -980,7 +984,8 @@ void main() { // Confirm that [HybridTextEditing] didn't send any more messages. expect(spy.messages, isEmpty); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('sets correct input type in Android', () { debugOperatingSystemOverride = OperatingSystem.android; diff --git a/lib/web_ui/test/text_test.dart b/lib/web_ui/test/text_test.dart index 6889e890faa70..13bca16e9dd14 100644 --- a/lib/web_ui/test/text_test.dart +++ b/lib/web_ui/test/text_test.dart @@ -244,7 +244,8 @@ void main() async { expect(spans[0].style.fontFamily, 'Ahem, Arial, sans-serif'); // The nested span here should not set it's family to default sans-serif. expect(spans[1].style.fontFamily, 'Ahem, Arial, sans-serif'); - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('adds Arial and sans-serif as fallback fonts', () { // Set this to false so it doesn't default to 'Ahem' font. @@ -261,7 +262,8 @@ void main() async { expect(paragraph.paragraphElement.style.fontFamily, 'SomeFont, Arial, sans-serif'); debugEmulateFlutterTesterEnvironment = true; - }); + }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 + skip: (browserEngine == BrowserEngine.firefox)); test('does not add fallback fonts to generic families', () { // Set this to false so it doesn't default to 'Ahem' font. From 434f6f9c87e6bf9cfcbe9385efb57edc6a21ba7f Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 9 Dec 2019 17:59:50 -0800 Subject: [PATCH 363/591] Add support for platform views in the CanvasKit backend (#14263) * Add support for platform views in the CanvasKit backend * Respond to comments * Update license file --- ci/licenses_golden/licenses_flutter | 2 + lib/web_ui/lib/src/engine.dart | 2 + .../lib/src/engine/compositor/canvas.dart | 4 + .../src/engine/compositor/embedded_views.dart | 544 ++++++++++++++++++ .../engine/compositor/engine_delegate.dart | 1 + .../src/engine/compositor/initialization.dart | 7 + .../lib/src/engine/compositor/layer.dart | 168 ++++-- .../compositor/layer_scene_builder.dart | 2 +- .../lib/src/engine/compositor/layer_tree.dart | 31 +- .../src/engine/compositor/n_way_canvas.dart | 86 +++ .../lib/src/engine/compositor/path.dart | 4 + .../engine/compositor/picture_recorder.dart | 6 +- .../lib/src/engine/compositor/rasterizer.dart | 10 +- .../lib/src/engine/compositor/surface.dart | 48 +- lib/web_ui/lib/src/engine/platform_views.dart | 10 +- lib/web_ui/lib/src/engine/window.dart | 6 +- 16 files changed, 849 insertions(+), 82 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/compositor/embedded_views.dart create mode 100644 lib/web_ui/lib/src/engine/compositor/n_way_canvas.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 78b87fec6793f..ed92e13fd2a57 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -386,6 +386,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/color_filter.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/embedded_views.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/fonts.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/image.dart @@ -394,6 +395,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/initialization.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer_tree.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/n_way_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/path.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/path_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/picture.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index d9f9dca215784..a45179ec73f17 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -27,6 +27,7 @@ part 'engine/color_filter.dart'; part 'engine/compositor/canvas.dart'; part 'engine/compositor/canvas_kit_canvas.dart'; part 'engine/compositor/color_filter.dart'; +part 'engine/compositor/embedded_views.dart'; part 'engine/compositor/engine_delegate.dart'; part 'engine/compositor/fonts.dart'; part 'engine/compositor/image.dart'; @@ -35,6 +36,7 @@ part 'engine/compositor/initialization.dart'; part 'engine/compositor/layer.dart'; part 'engine/compositor/layer_scene_builder.dart'; part 'engine/compositor/layer_tree.dart'; +part 'engine/compositor/n_way_canvas.dart'; part 'engine/compositor/path.dart'; part 'engine/compositor/path_metrics.dart'; part 'engine/compositor/picture.dart'; diff --git a/lib/web_ui/lib/src/engine/compositor/canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas.dart index d32f6f4adee01..81210d35756dc 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas.dart @@ -12,6 +12,10 @@ class SkCanvas { int get saveCount => skCanvas.callMethod('getSaveCount'); + void clear(ui.Color color) { + skCanvas.callMethod('clear', [color.value]); + } + void clipPath(ui.Path path, bool doAntiAlias) { final SkPath skPath = path; final js.JsObject intersectClipOp = canvasKit['ClipOp']['Intersect']; diff --git a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart new file mode 100644 index 0000000000000..798a3d7dc8476 --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart @@ -0,0 +1,544 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// This composites HTML views into the [ui.Scene]. +class HtmlViewEmbedder { + /// A picture recorder associated with a view id. + /// + /// When we composite in the platform view, we need to create a new canvas + /// for further paint commands to paint to, since the composited view will + /// be on top of the current canvas, and we want further paint commands to + /// be on top of the platform view. + final Map _pictureRecorders = + {}; + + /// The most recent composition parameters for a given view id. + /// + /// If we receive a request to composite a view, but the composition + /// parameters haven't changed, we can avoid having to recompute the + /// element stack that correctly composites the view into the scene. + final Map _currentCompositionParams = + {}; + + /// The HTML element associated with the given view id. + final Map _views = {}; + + /// The root view in the stack of mutator elements for the view id. + final Map _rootViews = {}; + + /// The overlay for the view id. + final Map _overlays = {}; + + /// The views that need to be recomposited into the scene on the next frame. + final Set _viewsToRecomposite = {}; + + /// The views that need to be disposed of on the next frame. + final Set _viewsToDispose = {}; + + /// The list of view ids that should be composited, in order. + List _compositionOrder = []; + + /// The most recent composition order. + List _activeCompositionOrder = []; + + /// The number of clipping elements used last time the view was composited. + Map _clipCount = {}; + + /// The size of the frame, in physical pixels. + ui.Size _frameSize; + + void set frameSize(ui.Size size) { + if (_frameSize == size) { + return; + } + _activeCompositionOrder.clear(); + _frameSize = size; + } + + void handlePlatformViewCall( + ByteData data, + ui.PlatformMessageResponseCallback callback, + ) { + const MethodCodec codec = StandardMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + + switch (decoded.method) { + case 'create': + _create(decoded, callback); + return; + case 'dispose': + _dispose(decoded, callback); + return; + } + callback(null); + } + + void _create( + MethodCall methodCall, ui.PlatformMessageResponseCallback callback) { + final Map args = methodCall.arguments; + final int viewId = args['id']; + final String viewType = args['viewType']; + const MethodCodec codec = StandardMethodCodec(); + + if (_views[viewId] != null) { + callback(codec.encodeErrorEnvelope( + code: 'recreating_view', + message: 'trying to create an already created view', + details: 'view id: $viewId', + )); + return; + } + + final PlatformViewFactory factory = + platformViewRegistry.registeredFactories[viewType]; + if (factory == null) { + callback(codec.encodeErrorEnvelope( + code: 'unregistered_view_type', + message: 'trying to create a view with an unregistered type', + details: 'unregistered view type: $viewType', + )); + return; + } + + // TODO(het): Support creation parameters. + html.Element embeddedView = factory(viewId); + _views[viewId] = embeddedView; + + _rootViews[viewId] = embeddedView; + + callback(codec.encodeSuccessEnvelope(null)); + } + + void _dispose( + MethodCall methodCall, ui.PlatformMessageResponseCallback callback) { + int viewId = methodCall.arguments; + const MethodCodec codec = StandardMethodCodec(); + if (!_views.containsKey(viewId)) { + callback(codec.encodeErrorEnvelope( + code: 'unknown_view', + message: 'trying to dispose an unknown view', + details: 'view id: $viewId', + )); + } + _viewsToDispose.add(viewId); + callback(codec.encodeSuccessEnvelope(null)); + } + + List getCurrentCanvases() { + final List canvases = []; + for (int i = 0; i < _compositionOrder.length; i++) { + final int viewId = _compositionOrder[i]; + canvases.add(_pictureRecorders[viewId].recordingCanvas); + } + return canvases; + } + + void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) { + final pictureRecorder = SkPictureRecorder(); + pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); + pictureRecorder.recordingCanvas.clear(ui.Color(0x00000000)); + _pictureRecorders[viewId] = pictureRecorder; + _compositionOrder.add(viewId); + + // Do nothing if the params didn't change. + if (_currentCompositionParams[viewId] == params) { + return; + } + _currentCompositionParams[viewId] = params; + _viewsToRecomposite.add(viewId); + } + + SkCanvas compositeEmbeddedView(int viewId) { + // Do nothing if this view doesn't need to be composited. + if (!_viewsToRecomposite.contains(viewId)) { + return _pictureRecorders[viewId].recordingCanvas; + } + _compositeWithParams(viewId, _currentCompositionParams[viewId]); + _viewsToRecomposite.remove(viewId); + return _pictureRecorders[viewId].recordingCanvas; + } + + void _compositeWithParams(int viewId, EmbeddedViewParams params) { + final html.Element platformView = _views[viewId]; + platformView.style.width = '${params.size.width}px'; + platformView.style.height = '${params.size.height}px'; + platformView.style.position = 'absolute'; + + final int currentClippingCount = _countClips(params.mutators); + final int previousClippingCount = _clipCount[viewId]; + if (currentClippingCount != previousClippingCount) { + _clipCount[viewId] = currentClippingCount; + html.Element oldPlatformViewRoot = _rootViews[viewId]; + html.Element newPlatformViewRoot = _reconstructClipViewsChain( + currentClippingCount, + platformView, + oldPlatformViewRoot, + ); + _rootViews[viewId] = newPlatformViewRoot; + } + _applyMutators(params.mutators, platformView); + } + + int _countClips(MutatorsStack mutators) { + int clipCount = 0; + for (final Mutator mutator in mutators) { + if (mutator.isClipType) { + clipCount++; + } + } + return clipCount; + } + + html.Element _reconstructClipViewsChain( + int numClips, + html.Element platformView, + html.Element headClipView, + ) { + int indexInFlutterView = -1; + if (headClipView.parent != null) { + indexInFlutterView = skiaSceneHost.children.indexOf(headClipView); + headClipView.remove(); + } + html.Element head = platformView; + int clipIndex = 0; + // Re-use as much existing clip views as needed. + while (head != headClipView && clipIndex < numClips) { + head = head.parent; + clipIndex++; + } + // If there weren't enough existing clip views, add more. + while (clipIndex < numClips) { + html.Element clippingView = html.Element.tag('flt-clip'); + clippingView.append(head); + head = clippingView; + clipIndex++; + } + head.remove(); + + // If the chain was previously attached, attach it to the same position. + if (indexInFlutterView > -1) { + skiaSceneHost.children.insert(indexInFlutterView, head); + } + return head; + } + + void _applyMutators(MutatorsStack mutators, html.Element embeddedView) { + html.Element head = embeddedView; + Matrix4 headTransform = Matrix4.identity(); + double embeddedOpacity = 1.0; + _resetAnchor(head); + + for (final Mutator mutator in mutators) { + switch (mutator.type) { + case MutatorType.transform: + headTransform.multiply(mutator.matrix); + head.style.transform = + float64ListToCssTransform(headTransform.storage); + break; + case MutatorType.clipRect: + case MutatorType.clipRRect: + case MutatorType.clipPath: + html.Element clipView = head.parent; + clipView.style.clip = ''; + clipView.style.clipPath = ''; + headTransform = Matrix4.identity(); + clipView.style.transform = ''; + if (mutator.rect != null) { + final ui.Rect rect = mutator.rect; + clipView.style.clip = 'rect(${rect.top}px, ${rect.right}px, ' + '${rect.bottom}px, ${rect.left}px)'; + } else if (mutator.rrect != null) { + final SkPath path = SkPath(); + path.addRRect(mutator.rrect); + _ensureSvgPathDefs(); + html.Element pathDefs = _svgPathDefs.querySelector('#sk_path_defs'); + _clipPathCount += 1; + html.Element newClipPath = + html.Element.html('' + '' + ''); + pathDefs.append(newClipPath); + clipView.style.clipPath = 'url(#svgClip$_clipPathCount)'; + } else if (mutator.path != null) { + final SkPath path = mutator.path; + _ensureSvgPathDefs(); + html.Element pathDefs = _svgPathDefs.querySelector('#sk_path_defs'); + _clipPathCount += 1; + html.Element newClipPath = + html.Element.html('' + '' + ''); + pathDefs.append(newClipPath); + clipView.style.clipPath = 'url(#svgClip$_clipPathCount)'; + } + _resetAnchor(clipView); + head = clipView; + break; + case MutatorType.opacity: + embeddedOpacity *= mutator.alphaFloat; + break; + } + } + + embeddedView.style.opacity = embeddedOpacity.toString(); + + // Reverse scale based on screen scale. + // + // HTML elements use logical (CSS) pixels, but we have been using physical + // pixels, so scale down the head element to match the logical resolution. + final double scale = html.window.devicePixelRatio; + final double inverseScale = 1 / scale; + final Matrix4 scaleMatrix = + Matrix4.diagonal3Values(inverseScale, inverseScale, 1); + headTransform.multiply(scaleMatrix); + head.style.transform = float64ListToCssTransform(headTransform.storage); + } + + /// Sets the transform origin to the top-left corner of the element. + /// + /// By default, the transform origin is the center of the element, but + /// Flutter assumes the transform origin is the top-left point. + void _resetAnchor(html.Element element) { + element.style.transformOrigin = '0 0 0'; + element.style.position = 'absolute'; + } + + int _clipPathCount = 0; + + html.Element _svgPathDefs; + + /// Ensures we add a container of SVG path defs to the DOM so they can + /// be referred to in clip-path: url(#blah). + void _ensureSvgPathDefs() { + if (_svgPathDefs != null) return; + _svgPathDefs = html.Element.html( + '', + treeSanitizer: _NullTreeSanitizer(), + ); + skiaSceneHost.append(_svgPathDefs); + } + + void submitFrame() { + disposeViews(); + + for (int i = 0; i < _compositionOrder.length; i++) { + int viewId = _compositionOrder[i]; + ensureOverlayInitialized(viewId); + final SurfaceFrame frame = + _overlays[viewId].surface.acquireFrame(_frameSize); + final SkCanvas canvas = frame.skiaCanvas; + canvas.drawPicture(_pictureRecorders[viewId].endRecording()); + frame.submit(); + } + _pictureRecorders.clear(); + if (_listEquals(_compositionOrder, _activeCompositionOrder)) { + _compositionOrder.clear(); + return; + } + _activeCompositionOrder.clear(); + + for (int i = 0; i < _compositionOrder.length; i++) { + int viewId = _compositionOrder[i]; + html.Element platformViewRoot = _rootViews[viewId]; + html.Element overlay = _overlays[viewId].surface.htmlElement; + platformViewRoot.remove(); + skiaSceneHost.append(platformViewRoot); + overlay.remove(); + skiaSceneHost.append(overlay); + _activeCompositionOrder.add(viewId); + } + _compositionOrder.clear(); + } + + void disposeViews() { + if (_viewsToDispose.isEmpty) { + return; + } + + for (int viewId in _viewsToDispose) { + final html.Element rootView = _rootViews[viewId]; + rootView.remove(); + _views.remove(viewId); + _rootViews.remove(viewId); + if (_overlays[viewId] != null) { + final Overlay overlay = _overlays[viewId]; + overlay.surface.htmlElement?.remove(); + } + _overlays.remove(viewId); + _currentCompositionParams.remove(viewId); + _clipCount.remove(viewId); + _viewsToRecomposite.remove(viewId); + } + _viewsToDispose.clear(); + } + + void ensureOverlayInitialized(int viewId) { + Overlay overlay = _overlays[viewId]; + if (overlay != null) { + return; + } + Surface surface = Surface(); + SkSurface skSurface = surface.acquireRenderSurface(_frameSize); + _overlays[viewId] = Overlay(surface, skSurface); + } +} + +/// The parameters passed to the view embedder. +class EmbeddedViewParams { + EmbeddedViewParams(this.offset, this.size, MutatorsStack mutators) + : mutators = MutatorsStack._copy(mutators); + + final ui.Offset offset; + final ui.Size size; + final MutatorsStack mutators; + + bool operator ==(dynamic other) { + if (identical(this, other)) return true; + if (other is! EmbeddedViewParams) return false; + + EmbeddedViewParams typedOther = other; + return offset == typedOther.offset && + size == typedOther.size && + mutators == typedOther.mutators; + } + + int get hashCode => ui.hashValues(offset, size, mutators); +} + +enum MutatorType { + clipRect, + clipRRect, + clipPath, + transform, + opacity, +} + +/// Stores mutation information like clipping or transform. +class Mutator { + const Mutator._( + this.type, + this.rect, + this.rrect, + this.path, + this.matrix, + this.alpha, + ); + + final MutatorType type; + final ui.Rect rect; + final ui.RRect rrect; + final ui.Path path; + final Matrix4 matrix; + final int alpha; + + const Mutator.clipRect(ui.Rect rect) + : this._(MutatorType.clipRect, rect, null, null, null, null); + const Mutator.clipRRect(ui.RRect rrect) + : this._(MutatorType.clipRRect, null, rrect, null, null, null); + const Mutator.clipPath(ui.Path path) + : this._(MutatorType.clipPath, null, null, path, null, null); + const Mutator.transform(Matrix4 matrix) + : this._(MutatorType.transform, null, null, null, matrix, null); + const Mutator.opacity(int alpha) + : this._(MutatorType.opacity, null, null, null, null, alpha); + + bool get isClipType => + type == MutatorType.clipRect || + type == MutatorType.clipRRect || + type == MutatorType.clipPath; + + double get alphaFloat => alpha / 255.0; + + bool operator ==(dynamic other) { + if (identical(this, other)) return true; + if (other is! Mutator) return false; + + final Mutator typedOther = other; + if (type != typedOther.type) { + return false; + } + + switch (type) { + case MutatorType.clipRect: + return rect == typedOther.rect; + case MutatorType.clipRRect: + return rrect == typedOther.rrect; + case MutatorType.clipPath: + return path == typedOther.path; + case MutatorType.transform: + return matrix == typedOther.matrix; + case MutatorType.opacity: + return alpha == typedOther.alpha; + } + } + + int get hashCode => ui.hashValues(type, rect, rrect, path, matrix, alpha); +} + +/// A stack of mutators that can be applied to an embedded view. +class MutatorsStack extends Iterable { + MutatorsStack() : _mutators = []; + + MutatorsStack._copy(MutatorsStack original) + : _mutators = List.from(original._mutators); + + final List _mutators; + + void pushClipRect(ui.Rect rect) { + _mutators.add(Mutator.clipRect(rect)); + } + + void pushClipRRect(ui.RRect rrect) { + _mutators.add(Mutator.clipRRect(rrect)); + } + + void pushClipPath(ui.Path path) { + _mutators.add(Mutator.clipPath(path)); + } + + void pushTransform(Matrix4 matrix) { + _mutators.add(Mutator.transform(matrix)); + } + + void pushOpacity(int alpha) { + _mutators.add(Mutator.opacity(alpha)); + } + + void pop() { + _mutators.removeLast(); + } + + bool operator ==(dynamic other) { + if (identical(other, this)) return true; + if (other is! MutatorsStack) return false; + + final MutatorsStack typedOther = other; + if (_mutators.length != typedOther._mutators.length) { + return false; + } + + for (int i = 0; i < _mutators.length; i++) { + if (_mutators[i] != typedOther._mutators[i]) { + return false; + } + } + + return true; + } + + int get hashCode => ui.hashList(_mutators); + + @override + Iterator get iterator => _mutators.reversed.iterator; +} + +/// Represents a surface overlaying a platform view. +class Overlay { + final Surface surface; + final SkSurface skSurface; + + Overlay(this.surface, this.skSurface); +} diff --git a/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart b/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart index bf28ebbfb91eb..0a854696ca9c7 100644 --- a/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart +++ b/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart @@ -58,6 +58,7 @@ class Engine extends RuntimeDelegate { } layerTree.frameSize = frameSize; + layerTree.devicePixelRatio = _viewportMetrics.devicePixelRatio; _animator.render(layerTree); } diff --git a/lib/web_ui/lib/src/engine/compositor/initialization.dart b/lib/web_ui/lib/src/engine/compositor/initialization.dart index 27a41a0450cb4..af69adccea2ee 100644 --- a/lib/web_ui/lib/src/engine/compositor/initialization.dart +++ b/lib/web_ui/lib/src/engine/compositor/initialization.dart @@ -32,6 +32,10 @@ Future initializeSkia() { }, ]); }); + + /// Add a Skia scene host. + skiaSceneHost = html.Element.tag('flt-scene'); + domRenderer.renderScene(skiaSceneHost); return canvasKitCompleter.future; } @@ -42,3 +46,6 @@ js.JsObject canvasKit; /// The Skia font collection. SkiaFontCollection skiaFontCollection; + +/// The scene host, where the root canvas and overlay canvases are added to. +html.Element skiaSceneHost; diff --git a/lib/web_ui/lib/src/engine/compositor/layer.dart b/lib/web_ui/lib/src/engine/compositor/layer.dart index 3321bbe5dd6f8..8656ee8907b0e 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer.dart @@ -34,18 +34,36 @@ class PrerollContext { /// A raster cache. Used to register candidates for caching. final RasterCache rasterCache; - PrerollContext(this.rasterCache); + /// A compositor for embedded HTML views. + final HtmlViewEmbedder viewEmbedder; + + final MutatorsStack mutatorsStack = MutatorsStack(); + + PrerollContext(this.rasterCache, this.viewEmbedder); } /// A context shared by all layers during the paint pass. class PaintContext { - /// The canvas to paint to. - final SkCanvas canvas; + /// A multi-canvas that applies clips, transforms, and opacity + /// operations to all canvases (root canvas and overlay canvases for the + /// platform views). + SkNWayCanvas internalNodesCanvas; + + /// The canvas for leaf nodes to paint to. + SkCanvas leafNodesCanvas; /// A raster cache potentially containing pre-rendered pictures. final RasterCache rasterCache; - PaintContext(this.canvas, this.rasterCache); + /// A compositor for embedded HTML views. + final HtmlViewEmbedder viewEmbedder; + + PaintContext( + this.internalNodesCanvas, + this.leafNodesCanvas, + this.rasterCache, + this.viewEmbedder, + ); } /// A layer that contains child layers. @@ -100,9 +118,9 @@ class BackdropFilterLayer extends ContainerLayer { @override void paint(PaintContext context) { - context.canvas.saveLayerWithFilter(paintBounds, _filter); + context.internalNodesCanvas.saveLayerWithFilter(paintBounds, _filter); paintChildren(context); - context.canvas.restore(); + context.internalNodesCanvas.restore(); } } @@ -116,29 +134,31 @@ class ClipPathLayer extends ContainerLayer { : assert(_clipBehavior != ui.Clip.none); @override - void preroll(PrerollContext prerollContext, Matrix4 matrix) { - final ui.Rect childPaintBounds = prerollChildren(prerollContext, matrix); + void preroll(PrerollContext context, Matrix4 matrix) { + context.mutatorsStack.pushClipPath(_clipPath); + final ui.Rect childPaintBounds = prerollChildren(context, matrix); final ui.Rect clipBounds = _clipPath.getBounds(); if (childPaintBounds.overlaps(clipBounds)) { paintBounds = childPaintBounds.intersect(clipBounds); } + context.mutatorsStack.pop(); } @override void paint(PaintContext paintContext) { assert(needsPainting); - paintContext.canvas.save(); - paintContext.canvas.clipPath(_clipPath, _clipBehavior != ui.Clip.hardEdge); + paintContext.internalNodesCanvas.save(); + paintContext.internalNodesCanvas.clipPath(_clipPath, _clipBehavior != ui.Clip.hardEdge); if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.saveLayer(paintBounds, null); + paintContext.internalNodesCanvas.saveLayer(paintBounds, null); } paintChildren(paintContext); if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } } @@ -152,31 +172,33 @@ class ClipRectLayer extends ContainerLayer { : assert(_clipBehavior != ui.Clip.none); @override - void preroll(PrerollContext prerollContext, Matrix4 matrix) { - final ui.Rect childPaintBounds = prerollChildren(prerollContext, matrix); + void preroll(PrerollContext context, Matrix4 matrix) { + context.mutatorsStack.pushClipRect(_clipRect); + final ui.Rect childPaintBounds = prerollChildren(context, matrix); if (childPaintBounds.overlaps(_clipRect)) { paintBounds = childPaintBounds.intersect(_clipRect); } + context.mutatorsStack.pop(); } @override void paint(PaintContext paintContext) { assert(needsPainting); - paintContext.canvas.save(); - paintContext.canvas.clipRect( + paintContext.internalNodesCanvas.save(); + paintContext.internalNodesCanvas.clipRect( _clipRect, ui.ClipOp.intersect, _clipBehavior != ui.Clip.hardEdge, ); if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.saveLayer(_clipRect, null); + paintContext.internalNodesCanvas.saveLayer(_clipRect, null); } paintChildren(paintContext); if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } } @@ -190,28 +212,30 @@ class ClipRRectLayer extends ContainerLayer { : assert(_clipBehavior != ui.Clip.none); @override - void preroll(PrerollContext prerollContext, Matrix4 matrix) { - final ui.Rect childPaintBounds = prerollChildren(prerollContext, matrix); + void preroll(PrerollContext context, Matrix4 matrix) { + context.mutatorsStack.pushClipRRect(_clipRRect); + final ui.Rect childPaintBounds = prerollChildren(context, matrix); if (childPaintBounds.overlaps(_clipRRect.outerRect)) { paintBounds = childPaintBounds.intersect(_clipRRect.outerRect); } + context.mutatorsStack.pop(); } @override void paint(PaintContext paintContext) { assert(needsPainting); - paintContext.canvas.save(); - paintContext.canvas + paintContext.internalNodesCanvas.save(); + paintContext.internalNodesCanvas .clipRRect(_clipRRect, _clipBehavior != ui.Clip.hardEdge); if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.saveLayer(paintBounds, null); + paintContext.internalNodesCanvas.saveLayer(paintBounds, null); } paintChildren(paintContext); if (_clipBehavior == ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } } @@ -223,12 +247,16 @@ class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer { OpacityLayer(this._alpha, this._offset); @override - void preroll(PrerollContext prerollContext, Matrix4 matrix) { + void preroll(PrerollContext context, Matrix4 matrix) { final Matrix4 childMatrix = Matrix4.copy(matrix); childMatrix.translate(_offset.dx, _offset.dy); - final ui.Rect childPaintBounds = - prerollChildren(prerollContext, childMatrix); - paintBounds = childPaintBounds.translate(_offset.dx, _offset.dy); + context.mutatorsStack + .pushTransform(Matrix4.translationValues(_offset.dx, _offset.dy, 0.0)); + context.mutatorsStack.pushOpacity(_alpha); + super.preroll(context, childMatrix); + context.mutatorsStack.pop(); + context.mutatorsStack.pop(); + paintBounds = paintBounds.translate(_offset.dx, _offset.dy); } @override @@ -238,16 +266,16 @@ class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer { final ui.Paint paint = ui.Paint(); paint.color = ui.Color.fromARGB(_alpha, 0, 0, 0); - paintContext.canvas.save(); - paintContext.canvas.translate(_offset.dx, _offset.dy); + paintContext.internalNodesCanvas.save(); + paintContext.internalNodesCanvas.translate(_offset.dx, _offset.dy); final ui.Rect saveLayerBounds = paintBounds.shift(-_offset); - paintContext.canvas.saveLayer(saveLayerBounds, paint); + paintContext.internalNodesCanvas.saveLayer(saveLayerBounds, paint); paintChildren(paintContext); // Restore twice: once for the translate and once for the saveLayer. - paintContext.canvas.restore(); - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); + paintContext.internalNodesCanvas.restore(); } } @@ -260,11 +288,12 @@ class TransformLayer extends ContainerLayer TransformLayer(this._transform); @override - void preroll(PrerollContext prerollContext, Matrix4 matrix) { + void preroll(PrerollContext context, Matrix4 matrix) { final Matrix4 childMatrix = matrix * _transform; - final ui.Rect childPaintBounds = - prerollChildren(prerollContext, childMatrix); + context.mutatorsStack.pushTransform(_transform); + final ui.Rect childPaintBounds = prerollChildren(context, childMatrix); paintBounds = _transformRect(_transform, childPaintBounds); + context.mutatorsStack.pop(); } /// Applies the given matrix as a perspective transform to the given point. @@ -307,10 +336,10 @@ class TransformLayer extends ContainerLayer void paint(PaintContext paintContext) { assert(needsPainting); - paintContext.canvas.save(); - paintContext.canvas.transform(_transform.storage); + paintContext.internalNodesCanvas.save(); + paintContext.internalNodesCanvas.transform(_transform.storage); paintChildren(paintContext); - paintContext.canvas.restore(); + paintContext.internalNodesCanvas.restore(); } } @@ -340,11 +369,11 @@ class PictureLayer extends Layer { assert(picture != null); assert(needsPainting); - paintContext.canvas.save(); - paintContext.canvas.translate(offset.dx, offset.dy); + paintContext.leafNodesCanvas.save(); + paintContext.leafNodesCanvas.translate(offset.dx, offset.dy); - paintContext.canvas.drawPicture(picture); - paintContext.canvas.restore(); + paintContext.leafNodesCanvas.drawPicture(picture); + paintContext.leafNodesCanvas.restore(); } } @@ -432,26 +461,26 @@ class PhysicalShapeLayer extends ContainerLayer assert(needsPainting); if (_elevation != 0) { - drawShadow(paintContext.canvas, _path, _shadowColor, _elevation, + drawShadow(paintContext.leafNodesCanvas, _path, _shadowColor, _elevation, _color.alpha != 0xff); } final ui.Paint paint = ui.Paint()..color = _color; if (_clipBehavior != ui.Clip.antiAliasWithSaveLayer) { - paintContext.canvas.drawPath(_path, paint); + paintContext.leafNodesCanvas.drawPath(_path, paint); } - final int saveCount = paintContext.canvas.save(); + final int saveCount = paintContext.internalNodesCanvas.save(); switch (_clipBehavior) { case ui.Clip.hardEdge: - paintContext.canvas.clipPath(_path, false); + paintContext.internalNodesCanvas.clipPath(_path, false); break; case ui.Clip.antiAlias: - paintContext.canvas.clipPath(_path, true); + paintContext.internalNodesCanvas.clipPath(_path, true); break; case ui.Clip.antiAliasWithSaveLayer: - paintContext.canvas.clipPath(_path, true); - paintContext.canvas.saveLayer(paintBounds, null); + paintContext.internalNodesCanvas.clipPath(_path, true); + paintContext.internalNodesCanvas.saveLayer(paintBounds, null); break; case ui.Clip.none: break; @@ -462,12 +491,12 @@ class PhysicalShapeLayer extends ContainerLayer // (https://github.com/flutter/flutter/issues/18057#issue-328003931) // using saveLayer, we have to call drawPaint instead of drawPath as // anti-aliased drawPath will always have such artifacts. - paintContext.canvas.drawPaint(paint); + paintContext.leafNodesCanvas.drawPaint(paint); } paintChildren(paintContext); - paintContext.canvas.restoreToCount(saveCount); + paintContext.internalNodesCanvas.restoreToCount(saveCount); } /// Draws a shadow on the given [canvas] for the given [path]. @@ -479,3 +508,32 @@ class PhysicalShapeLayer extends ContainerLayer canvas.drawShadow(path, color, elevation, transparentOccluder); } } + +/// A layer which renders a platform view (an HTML element in this case). +class PlatformViewLayer extends Layer { + PlatformViewLayer(this.viewId, this.offset, this.width, this.height); + + final int viewId; + final ui.Offset offset; + final double width; + final double height; + + @override + void preroll(PrerollContext context, Matrix4 matrix) { + paintBounds = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height); + context.viewEmbedder.prerollCompositeEmbeddedView( + viewId, + EmbeddedViewParams( + offset, + ui.Size(width, height), + context.mutatorsStack, + ), + ); + } + + @override + void paint(PaintContext context) { + SkCanvas canvas = context.viewEmbedder.compositeEmbeddedView(viewId); + context.leafNodesCanvas = canvas; + } +} diff --git a/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart index c07fe108351a4..9e25b218ca309 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart @@ -73,7 +73,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { double height = 0.0, Object webOnlyPaintedBy, }) { - // TODO(b/128317425): implement addPlatformView. + currentLayer.add(PlatformViewLayer(viewId, offset, width, height)); } @override diff --git a/lib/web_ui/lib/src/engine/compositor/layer_tree.dart b/lib/web_ui/lib/src/engine/compositor/layer_tree.dart index ee428f5947d0f..d595fa98cfc38 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer_tree.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer_tree.dart @@ -12,6 +12,9 @@ class LayerTree { /// The size (in physical pixels) of the frame to paint this layer tree into. ui.Size frameSize; + /// The devicePixelRatio of the frame to paint this layer tree into. + double devicePixelRatio; + /// Performs a preroll phase before painting the layer tree. /// /// In this phase, the paint boundary for each layer is computed and @@ -19,8 +22,10 @@ class LayerTree { /// to raster. If [ignoreRasterCache] is `true`, then there will be no /// attempt to register pictures to cache. void preroll(Frame frame, {bool ignoreRasterCache = false}) { - final PrerollContext context = - PrerollContext(ignoreRasterCache ? null : frame.rasterCache); + final PrerollContext context = PrerollContext( + ignoreRasterCache ? null : frame.rasterCache, + frame.viewEmbedder, + ); rootLayer.preroll(context, Matrix4.identity()); } @@ -29,8 +34,19 @@ class LayerTree { /// If [ignoreRasterCache] is `true`, then the raster cache will /// not be used. void paint(Frame frame, {bool ignoreRasterCache = false}) { + final SkNWayCanvas internalNodesCanvas = SkNWayCanvas(); + internalNodesCanvas.addCanvas(frame.canvas); + final List overlayCanvases = + frame.viewEmbedder.getCurrentCanvases(); + for (int i = 0; i < overlayCanvases.length; i++) { + internalNodesCanvas.addCanvas(overlayCanvases[i]); + } final PaintContext context = PaintContext( - frame.canvas, ignoreRasterCache ? null : frame.rasterCache); + internalNodesCanvas, + frame.canvas, + ignoreRasterCache ? null : frame.rasterCache, + frame.viewEmbedder, + ); if (rootLayer.needsPainting) { rootLayer.paint(context); } @@ -45,7 +61,10 @@ class Frame { /// A cache of pre-rastered pictures. final RasterCache rasterCache; - Frame(this.canvas, this.rasterCache); + /// The platform view embedder. + final HtmlViewEmbedder viewEmbedder; + + Frame(this.canvas, this.rasterCache, this.viewEmbedder); /// Rasterize the given layer tree into this frame. bool raster(LayerTree layerTree, {bool ignoreRasterCache = false}) { @@ -61,7 +80,7 @@ class CompositorContext { RasterCache rasterCache; /// Acquire a frame using this compositor's settings. - Frame acquireFrame(SkCanvas canvas) { - return Frame(canvas, rasterCache); + Frame acquireFrame(SkCanvas canvas, HtmlViewEmbedder viewEmbedder) { + return Frame(canvas, rasterCache, viewEmbedder); } } diff --git a/lib/web_ui/lib/src/engine/compositor/n_way_canvas.dart b/lib/web_ui/lib/src/engine/compositor/n_way_canvas.dart new file mode 100644 index 0000000000000..7e59da1a1bbda --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/n_way_canvas.dart @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// A virtual canvas that applies operations to multiple canvases at once. +class SkNWayCanvas { + final List _canvases = []; + + void addCanvas(SkCanvas canvas) { + _canvases.add(canvas); + } + + /// Calls [save] on all canvases. + int save() { + int saveCount; + for (int i = 0; i < _canvases.length; i++) { + saveCount = _canvases[i].save(); + } + return saveCount; + } + + /// Calls [saveLayer] on all canvases. + void saveLayer(ui.Rect bounds, ui.Paint paint) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].saveLayer(bounds, paint); + } + } + + /// Calls [saveLayerWithFilter] on all canvases. + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].saveLayerWithFilter(bounds, filter); + } + } + + /// Calls [restore] on all canvases. + void restore() { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].restore(); + } + } + + /// Calls [restoreToCount] on all canvases. + void restoreToCount(int count) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].restoreToCount(count); + } + } + + /// Calls [translate] on all canvases. + void translate(double dx, double dy) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].translate(dx, dy); + } + } + + /// Calls [transform] on all canvases. + void transform(Float64List matrix) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].transform(matrix); + } + } + + /// Calls [clipPath] on all canvases. + void clipPath(ui.Path path, bool doAntiAlias) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].clipPath(path, doAntiAlias); + } + } + + /// Calls [clipRect] on all canvases. + void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].clipRect(rect, clipOp, doAntiAlias); + } + } + + /// Calls [clipRRect] on all canvases. + void clipRRect(ui.RRect rrect, bool doAntiAlias) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].clipRRect(rrect, doAntiAlias); + } + } +} diff --git a/lib/web_ui/lib/src/engine/compositor/path.dart b/lib/web_ui/lib/src/engine/compositor/path.dart index 4dd9e05c8ec62..9995ffc5d3cdc 100644 --- a/lib/web_ui/lib/src/engine/compositor/path.dart +++ b/lib/web_ui/lib/src/engine/compositor/path.dart @@ -343,4 +343,8 @@ class SkPath implements ui.Path { throw new UnimplementedError( 'webOnlySerializeToCssPaint is not used in the CanvasKit backend.'); } + + String toSvgString() { + return _skPath.callMethod('toSVGString'); + } } diff --git a/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart b/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart index 8490f5d35acf7..68e8cbdf3fe3e 100644 --- a/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart +++ b/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart @@ -7,6 +7,7 @@ part of engine; class SkPictureRecorder implements ui.PictureRecorder { ui.Rect _cullRect; js.JsObject _recorder; + SkCanvas _recordingCanvas; SkCanvas beginRecording(ui.Rect bounds) { _cullRect = bounds; @@ -15,9 +16,12 @@ class SkPictureRecorder implements ui.PictureRecorder { [bounds.left, bounds.top, bounds.right, bounds.bottom]); final js.JsObject skCanvas = _recorder.callMethod('beginRecording', [skRect]); - return SkCanvas(skCanvas); + _recordingCanvas = SkCanvas(skCanvas); + return _recordingCanvas; } + SkCanvas get recordingCanvas => _recordingCanvas; + @override ui.Picture endRecording() { final js.JsObject skPicture = diff --git a/lib/web_ui/lib/src/engine/compositor/rasterizer.dart b/lib/web_ui/lib/src/engine/compositor/rasterizer.dart index 2953623ce504f..66c4b194764ea 100644 --- a/lib/web_ui/lib/src/engine/compositor/rasterizer.dart +++ b/lib/web_ui/lib/src/engine/compositor/rasterizer.dart @@ -8,8 +8,11 @@ part of engine; class Rasterizer { final Surface surface; final CompositorContext context = CompositorContext(); + final HtmlViewEmbedder viewEmbedder = HtmlViewEmbedder(); - Rasterizer(this.surface); + Rasterizer(this.surface) { + surface.viewEmbedder = viewEmbedder; + } /// Creates a new frame from this rasterizer's surface, draws the given /// [LayerTree] into it, and then submits the frame. @@ -30,10 +33,13 @@ class Rasterizer { layerTree.frameSize = frameSize; final SurfaceFrame frame = surface.acquireFrame(layerTree.frameSize); + surface.viewEmbedder.frameSize = layerTree.frameSize; final SkCanvas canvas = frame.skiaCanvas; - final Frame compositorFrame = context.acquireFrame(canvas); + final Frame compositorFrame = context.acquireFrame(canvas, surface.viewEmbedder); compositorFrame.raster(layerTree, ignoreRasterCache: true); + surface.addToScene(); frame.submit(); + surface.viewEmbedder.submitFrame(); } } diff --git a/lib/web_ui/lib/src/engine/compositor/surface.dart b/lib/web_ui/lib/src/engine/compositor/surface.dart index 8e38b678f2fbc..4671a8073df74 100644 --- a/lib/web_ui/lib/src/engine/compositor/surface.dart +++ b/lib/web_ui/lib/src/engine/compositor/surface.dart @@ -34,29 +34,44 @@ class SurfaceFrame { /// created. class Surface { SkSurface _surface; + html.Element htmlElement; + + bool _addedToScene = false; + + /// The default view embedder. Coordinates embedding platform views and + /// overlaying subsequent draw operations on top. + HtmlViewEmbedder viewEmbedder; /// Acquire a frame of the given [size] containing a drawable canvas. /// /// The given [size] is in physical pixels. SurfaceFrame acquireFrame(ui.Size size) { - final SkSurface surface = _acquireRenderSurface(size); + final SkSurface surface = acquireRenderSurface(size); if (surface == null) return null; - SubmitCallback submitCallback = (SurfaceFrame surfaceFrame, SkCanvas canvas) { + SubmitCallback submitCallback = + (SurfaceFrame surfaceFrame, SkCanvas canvas) { _presentSurface(canvas); }; return SurfaceFrame(surface, submitCallback); } - SkSurface _acquireRenderSurface(ui.Size size) { + SkSurface acquireRenderSurface(ui.Size size) { if (!_createOrUpdateSurfaces(size)) { return null; } return _surface; } + void addToScene() { + if (!_addedToScene) { + skiaSceneHost.children.insert(0, htmlElement); + } + _addedToScene = true; + } + bool _createOrUpdateSurfaces(ui.Size size) { if (_surface != null && size == @@ -69,6 +84,9 @@ class Surface { _surface?.dispose(); _surface = null; + htmlElement?.remove(); + htmlElement = null; + _addedToScene = false; if (size.isEmpty) { html.window.console.error('Cannot create surfaces of empty size.'); @@ -86,25 +104,29 @@ class Surface { SkSurface _wrapHtmlCanvas(ui.Size size) { final ui.Size logicalSize = size / ui.window.devicePixelRatio; - final html.CanvasElement htmlCanvas = - html.CanvasElement(width: size.width.ceil(), height: size.height.ceil()) - ..id = 'flt-sk-canvas'; + final html.CanvasElement htmlCanvas = html.CanvasElement( + width: size.width.ceil(), height: size.height.ceil()); htmlCanvas.style ..position = 'absolute' ..width = '${logicalSize.width.ceil()}px' ..height = '${logicalSize.height.ceil()}px'; + final js.JsObject glContext = canvasKit + .callMethod('GetWebGLContext', [htmlCanvas]); + final js.JsObject grContext = + canvasKit.callMethod('MakeGrContext', [glContext]); final js.JsObject skSurface = - canvasKit.callMethod('MakeWebGLCanvasSurface', [ - htmlCanvas, + canvasKit.callMethod('MakeOnScreenGLSurface', [ + grContext, size.width, size.height, ]); + htmlElement = htmlCanvas; + if (skSurface == null) { return null; } else { - domRenderer.renderScene(htmlCanvas); - return SkSurface(skSurface); + return SkSurface(skSurface, glContext); } } @@ -113,6 +135,7 @@ class Surface { return false; } + canvasKit.callMethod('setCurrentContext', [_surface.context]); _surface.getCanvas().flush(); return true; } @@ -121,14 +144,17 @@ class Surface { /// A Dart wrapper around Skia's SkSurface. class SkSurface { final js.JsObject _surface; + final js.JsObject _glContext; - SkSurface(this._surface); + SkSurface(this._surface, this._glContext); SkCanvas getCanvas() { final js.JsObject skCanvas = _surface.callMethod('getCanvas'); return SkCanvas(skCanvas); } + js.JsObject get context => _glContext; + int width() => _surface.callMethod('width'); int height() => _surface.callMethod('height'); diff --git a/lib/web_ui/lib/src/engine/platform_views.dart b/lib/web_ui/lib/src/engine/platform_views.dart index 33980510f3867..1675072eebb7f 100644 --- a/lib/web_ui/lib/src/engine/platform_views.dart +++ b/lib/web_ui/lib/src/engine/platform_views.dart @@ -6,7 +6,7 @@ part of engine; /// A registry for factories that create platform views. class PlatformViewRegistry { - final Map _registeredFactories = + final Map registeredFactories = {}; final Map _createdViews = {}; @@ -16,10 +16,10 @@ class PlatformViewRegistry { /// Register [viewTypeId] as being creating by the given [factory]. bool registerViewFactory(String viewTypeId, PlatformViewFactory factory) { - if (_registeredFactories.containsKey(viewTypeId)) { + if (registeredFactories.containsKey(viewTypeId)) { return false; } - _registeredFactories[viewTypeId] = factory; + registeredFactories[viewTypeId] = factory; return true; } @@ -65,7 +65,7 @@ void _createPlatformView( const MethodCodec codec = StandardMethodCodec(); // TODO(het): Use 'direction', 'width', and 'height'. - if (!platformViewRegistry._registeredFactories.containsKey(viewType)) { + if (!platformViewRegistry.registeredFactories.containsKey(viewType)) { callback(codec.encodeErrorEnvelope( code: 'Unregistered factory', message: "No factory registered for viewtype '$viewType'", @@ -74,7 +74,7 @@ void _createPlatformView( } // TODO(het): Use creation parameters. final html.Element element = - platformViewRegistry._registeredFactories[viewType](id); + platformViewRegistry.registeredFactories[viewType](id); platformViewRegistry._createdViews[id] = element; callback(codec.encodeSuccessEnvelope(null)); diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 96f6409a8d057..69aec0cbeecfe 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -159,7 +159,11 @@ class EngineWindow extends ui.Window { return; case 'flutter/platform_views': - handlePlatformViewCall(data, callback); + if (experimentalUseSkia) { + _rasterizer.viewEmbedder.handlePlatformViewCall(data, callback); + } else { + handlePlatformViewCall(data, callback); + } return; case 'flutter/accessibility': From 12bf95fd49b7456f08a1274078c4b55e675ad018 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 9 Dec 2019 21:58:23 -0500 Subject: [PATCH 364/591] Roll src/third_party/skia 095d2468a075..e56cc054dbae (4 commits) (#14270) https://skia.googlesource.com/skia.git/+log/095d2468a075..e56cc054dbae git log 095d2468a075..e56cc054dbae --date=short --first-parent --format='%ad %ae %s' 2019-12-10 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-10 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-09 jlavrova@google.com Fixing the fix for memory leak 2019-12-09 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@e56cc054dbae If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 9479cc7b6a3d1..a10d6fe96c93e 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '095d2468a075e01bf99ab51c76620f7cc4629f62', + 'skia_revision': 'e56cc054dbaebdacf93716c761f45fda3a6d5817', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index c4bf19ec16575..f0e65d29dd404 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1b07920409af783a5d68de2bc2722f19 +Signature: 0fb191ffcaf6126977f09ce49d0445df UNUSED LICENSES: From 571c9991d0cc576fc3938bf38e8eb479e8e5a4e5 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 01:59:59 -0500 Subject: [PATCH 365/591] Roll src/third_party/skia e56cc054dbae..ab26643258ad (3 commits) (#14273) https://skia.googlesource.com/skia.git/+log/e56cc054dbae..ab26643258ad git log e56cc054dbae..ab26643258ad --date=short --first-parent --format='%ad %ae %s' 2019-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 4e96c2bed379..ffcaa57570c0 (398 commits) 2019-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 27c89d2b5c56..f60da87424a4 (8 commits) 2019-12-10 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). Created with: gclient setdep -r src/third_party/skia@ab26643258ad If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a10d6fe96c93e..5a67707e4c6cd 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e56cc054dbaebdacf93716c761f45fda3a6d5817', + 'skia_revision': 'ab26643258ad78cb100e9d9e5eaf8ad06d8c639d', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f0e65d29dd404..b23634b4e3ed0 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 0fb191ffcaf6126977f09ce49d0445df +Signature: 0400e2940c1c54d6cda0e3950766318b UNUSED LICENSES: From 140818a715321022d20c962275e6ce2ae4f53cff Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 02:38:11 -0500 Subject: [PATCH 366/591] Roll fuchsia/sdk/core/linux-amd64 from Zkpa_... to nqJnP... (#14274) Roll fuchsia/sdk/core/linux-amd64 from Zkpa_... to nqJnP... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 5a67707e4c6cd..24f902eacafcc 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'Zkpa_dlYeNNn7sPFtAsDgjHM0hH8WArOqHxUo7p9GowC' + 'version': 'nqJnPRY-bl_mM90nPzO9iCMgPWLoxXoIERjlqZ_h6gYC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index bac1fabff6aac..ad8cf5009117b 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 27d62b2ba3dbe139698c88787b0db230 +Signature: 30160b123a845921d1986af63f0d28c0 UNUSED LICENSES: From ed6830edf3896d827849f97a397d4b34009a179c Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 10 Dec 2019 06:43:40 -0800 Subject: [PATCH 367/591] Roll src/third_party/dart 8b8894648f..02a8b015ad (26 commits) (#14278) dart-lang/sdk@02a8b015ad Migrate corelib tests starting with "j" to "q" to NNBD. dart-lang/sdk@617933c3c1 [vm, gc] As a debugging aid, remember the previous object during heap iteration. dart-lang/sdk@964c9d8c20 [dart2js] new-rti: debug print of named type variable miss dart-lang/sdk@7974f3f899 [dartdevc] Reduce the number of SDK libraries in the platform dart-lang/sdk@b296d55c55 Issue 39709. Disable implicit 'call' tear-off for nullable objects. dart-lang/sdk@ded753a397 Use 'isNonNullableByDefault' as the flag name everywhere. dart-lang/sdk@86723cd5b1 Remove unused private methods and explicit 'new'. dart-lang/sdk@108a80bd0d NNBD: Mark some good tests as passing; add FailingTests for some GitHub issues dart-lang/sdk@2015c55277 [dartdevc] Remove ability to run analyzer based DDC dart-lang/sdk@2a1fadb66c [dart2js] types_propagation - handle HAsCheckSimple dart-lang/sdk@20ec71d447 [vm/compiler] Fix TypeTestingStub -> SubtypeTestCache fallback code if dst_type = TypeParameter dart-lang/sdk@1aeff32c14 NNBD migrator: record type information in TypeName type arguments dart-lang/sdk@a17884aff0 [vm] Fix late final local variables in AST mode dart-lang/sdk@991e55f6ad NNBD Migrator: Fix generic super initializers dart-lang/sdk@9605cca5a6 Make a method public that is overridden in a subclass dart-lang/sdk@55f86c13cb [dartfuzz] Fix infinite recursion dart-lang/sdk@de0e432ef1 Create an abstract layer to customize querying element types. dart-lang/sdk@5f7177db84 Handle two non-operators as if they were operators to prevent a failure when assertions are enabled dart-lang/sdk@22fef10a59 [nnbd_migration] suppress fewer upstream exact nullable nodes dart-lang/sdk@b37f391f34 [VM/nnbd] Make runtimeType return a non-nullable type when the NNBD experiment is enabled. dart-lang/sdk@bafd4e8dc6 Issue 39668. Use 'read type' to check dead '??='. dart-lang/sdk@8894b88467 [VM/nnbd] Implement syntactic type equality. dart-lang/sdk@cb94390c63 [infra] Improve efficiency of cloning the Flutter repositories dart-lang/sdk@42799b66b6 Add support for unmanaged packages to trial_migration. dart-lang/sdk@304eb2cb7b [test] Remove extra / in dartdevc nndb test suite runner. dart-lang/sdk@16e7647c86 [ VM / dart:typed_data ] Fixed issue where null could be passed for simd types in AOT --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 24f902eacafcc..ac49ddc7ae1f9 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8b8894648f0dbe53b046133caab11fbe8e14dc3f', + 'dart_revision': '02a8b015ada14d556cc5683970d2ccab351b679c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b56dca67acee8..a0bcf16d95413 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 63d3e0cd0caf64c5b89692fc9f875051 +Signature: 387e5572f4d5397cd2535ecdfd59a341 UNUSED LICENSES: From 721fb5bc72f5789194ded062861ea65c6f48d436 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 10:43:17 -0500 Subject: [PATCH 368/591] Roll src/third_party/skia ab26643258ad..732c49739fa5 (1 commits) (#14279) https://skia.googlesource.com/skia.git/+log/ab26643258ad..732c49739fa5 git log ab26643258ad..732c49739fa5 --date=short --first-parent --format='%ad %ae %s' 2019-12-10 nifong@google.com Return image address in image info so it can be shown in dropdown. Created with: gclient setdep -r src/third_party/skia@732c49739fa5 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ac49ddc7ae1f9..e7f74269eeb09 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'ab26643258ad78cb100e9d9e5eaf8ad06d8c639d', + 'skia_revision': '732c49739fa57cfc26fb3cab2ca950bc440f3ac5', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b23634b4e3ed0..e33c3828a80a9 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 0400e2940c1c54d6cda0e3950766318b +Signature: da692beb732b6da3da400f6b172d8b08 UNUSED LICENSES: From b7d4278b4f293b4782f3faabda3568d5da70db6a Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 10 Dec 2019 10:34:50 -0800 Subject: [PATCH 369/591] Create separate objects for isolate state and isolate group state (#14268) Isolate data may need to be deleted on the same thread where it was allocated. In particular, the task observer set up in the UIDartState ctor must be removed from the same message loop where it was added. The engine had been using the same DartIsolate object as the root isolate data and as the isolate group data. This object would be deleted when the isolate group was shut down. However, group shutdown may occur on a thread associated with a secondary isolate. When this happens, cleanup of any state tied to the root isolate's thread will fail. This change adds a DartIsolateGroupData object holding state that is common among all isolates in a group. DartIsolateGroupData can be deleted on any thread. See https://github.com/flutter/flutter/issues/45578 --- ci/licenses_golden/licenses_flutter | 2 + runtime/BUILD.gn | 2 + runtime/dart_isolate.cc | 375 +++++++++++++--------------- runtime/dart_isolate.h | 57 +---- runtime/dart_isolate_group_data.cc | 65 +++++ runtime/dart_isolate_group_data.h | 63 +++++ runtime/dart_isolate_unittests.cc | 17 +- 7 files changed, 321 insertions(+), 260 deletions(-) create mode 100644 runtime/dart_isolate_group_data.cc create mode 100644 runtime/dart_isolate_group_data.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ed92e13fd2a57..bad6d1b797fa5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -491,6 +491,8 @@ FILE: ../../../flutter/lib/web_ui/lib/ui.dart FILE: ../../../flutter/lib/web_ui/tool/unicode_sync_script.dart FILE: ../../../flutter/runtime/dart_isolate.cc FILE: ../../../flutter/runtime/dart_isolate.h +FILE: ../../../flutter/runtime/dart_isolate_group_data.cc +FILE: ../../../flutter/runtime/dart_isolate_group_data.h FILE: ../../../flutter/runtime/dart_isolate_unittests.cc FILE: ../../../flutter/runtime/dart_lifecycle_unittests.cc FILE: ../../../flutter/runtime/dart_service_isolate.cc diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 5e41f19f031e2..edd3f8198bcbf 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -46,6 +46,8 @@ source_set("runtime") { sources = [ "dart_isolate.cc", "dart_isolate.h", + "dart_isolate_group_data.cc", + "dart_isolate_group_data.h", "dart_service_isolate.cc", "dart_service_isolate.h", "dart_snapshot.cc", diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 9fe676aca7b5b..97f55a2507961 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -12,6 +12,7 @@ #include "flutter/lib/io/dart_io.h" #include "flutter/lib/ui/dart_runtime_hooks.h" #include "flutter/lib/ui/dart_ui.h" +#include "flutter/runtime/dart_isolate_group_data.h" #include "flutter/runtime/dart_service_isolate.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" @@ -29,6 +30,27 @@ namespace flutter { +namespace { + +class DartErrorString { + public: + DartErrorString() : str_(nullptr) {} + ~DartErrorString() { + if (str_) { + ::free(str_); + } + } + char** error() { return &str_; } + const char* str() const { return str_; } + operator bool() const { return str_ != nullptr; } + + private: + FML_DISALLOW_COPY_AND_ASSIGN(DartErrorString); + char* str_; +}; + +} // anonymous namespace + std::weak_ptr DartIsolate::CreateRootIsolate( const Settings& settings, fml::RefPtr isolate_snapshot, @@ -44,21 +66,24 @@ std::weak_ptr DartIsolate::CreateRootIsolate( const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback) { TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); - Dart_Isolate vm_isolate = nullptr; - std::weak_ptr embedder_isolate; - - char* error = nullptr; - // Since this is the root isolate, we fake a parent embedder data object. We - // cannot use unique_ptr here because the destructor is private (since the - // isolate lifecycle is entirely managed by the VM). - // // The child isolate preparer is null but will be set when the isolate is // being prepared to run. - auto root_embedder_data = std::make_unique>( + auto isolate_group_data = + std::make_unique>( + std::shared_ptr(new DartIsolateGroupData( + settings, // settings + std::move(isolate_snapshot), // isolate snapshot + advisory_script_uri, // advisory URI + advisory_script_entrypoint, // advisory entrypoint + nullptr, // child isolate preparer + isolate_create_callback, // isolate create callback + isolate_shutdown_callback // isolate shutdown callback + ))); + + auto isolate_data = std::make_unique>( std::shared_ptr(new DartIsolate( settings, // settings - std::move(isolate_snapshot), // isolate snapshot task_runners, // task runners std::move(snapshot_delegate), // snapshot delegate std::move(io_manager), // IO manager @@ -66,46 +91,31 @@ std::weak_ptr DartIsolate::CreateRootIsolate( std::move(image_decoder), // Image Decoder advisory_script_uri, // advisory URI advisory_script_entrypoint, // advisory entrypoint - nullptr, // child isolate preparer - isolate_create_callback, // isolate create callback - isolate_shutdown_callback, // isolate shutdown callback - true, // is_root_isolate - true // is_group_root_isolate + true // is_root_isolate ))); - std::tie(vm_isolate, embedder_isolate) = CreateDartVMAndEmbedderObjectPair( - advisory_script_uri.c_str(), // advisory script URI - advisory_script_entrypoint.c_str(), // advisory script entrypoint - nullptr, // package root - nullptr, // package config - flags, // flags - root_embedder_data.get(), // parent embedder data - true, // is root isolate - &error // error (out) - ); + DartErrorString error; + Dart_Isolate vm_isolate = + CreateDartIsolateGroup(std::move(isolate_group_data), + std::move(isolate_data), flags, error.error()); - if (error != nullptr) { - free(error); + if (error) { + FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << error.str(); } if (vm_isolate == nullptr) { return {}; } - std::shared_ptr shared_embedder_isolate = - embedder_isolate.lock(); - if (shared_embedder_isolate) { - // Only root isolates can interact with windows. - shared_embedder_isolate->SetWindow(std::move(window)); - } + std::shared_ptr* root_isolate_data = + static_cast*>(Dart_IsolateData(vm_isolate)); - root_embedder_data.release(); + (*root_isolate_data)->SetWindow(std::move(window)); - return embedder_isolate; + return (*root_isolate_data)->GetWeakIsolatePtr(); } DartIsolate::DartIsolate(const Settings& settings, - fml::RefPtr isolate_snapshot, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, @@ -113,11 +123,7 @@ DartIsolate::DartIsolate(const Settings& settings, fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - const ChildIsolatePreparer& child_isolate_preparer, - const fml::closure& isolate_create_callback, - const fml::closure& isolate_shutdown_callback, - bool is_root_isolate, - bool is_group_root_isolate) + bool is_root_isolate) : UIDartState(std::move(task_runners), settings.task_observer_add, settings.task_observer_remove, @@ -130,21 +136,14 @@ DartIsolate::DartIsolate(const Settings& settings, settings.log_tag, settings.unhandled_exception_callback, DartVMRef::GetIsolateNameServer()), - settings_(settings), - isolate_snapshot_(std::move(isolate_snapshot)), - child_isolate_preparer_(std::move(child_isolate_preparer)), - isolate_create_callback_(isolate_create_callback), - isolate_shutdown_callback_(isolate_shutdown_callback), - is_root_isolate_(is_root_isolate), - is_group_root_isolate_(is_group_root_isolate) { - FML_DCHECK(isolate_snapshot_) << "Must contain a valid isolate snapshot."; + is_root_isolate_(is_root_isolate) { phase_ = Phase::Uninitialized; } -DartIsolate::~DartIsolate() = default; - -const Settings& DartIsolate::GetSettings() const { - return settings_; +DartIsolate::~DartIsolate() { + if (IsRootIsolate() && GetMessageHandlingTaskRunner()) { + FML_DCHECK(GetMessageHandlingTaskRunner()->RunsTasksOnCurrentThread()); + } } DartIsolate::Phase DartIsolate::GetPhase() const { @@ -295,12 +294,14 @@ bool DartIsolate::PrepareForRunningFromPrecompiledCode() { return false; } - child_isolate_preparer_ = [](DartIsolate* isolate) { + GetIsolateGroupData().SetChildIsolatePreparer([](DartIsolate* isolate) { return isolate->PrepareForRunningFromPrecompiledCode(); - }; + }); - if (isolate_create_callback_) { - isolate_create_callback_(); + const fml::closure& isolate_create_callback = + GetIsolateGroupData().GetIsolateCreateCallback(); + if (isolate_create_callback) { + isolate_create_callback(); } phase_ = Phase::Ready; @@ -376,22 +377,24 @@ bool DartIsolate::PrepareForRunningFromKernel( // Child isolate shares root isolate embedder_isolate (lines 691 and 693 // below). Re-initializing child_isolate_preparer_ lambda while it is being // executed leads to crashes. - if (child_isolate_preparer_ == nullptr) { - child_isolate_preparer_ = [buffers = - kernel_buffers_](DartIsolate* isolate) { - for (unsigned long i = 0; i < buffers.size(); i++) { - bool last_piece = i + 1 == buffers.size(); - const std::shared_ptr& buffer = buffers.at(i); - if (!isolate->PrepareForRunningFromKernel(buffer, last_piece)) { - return false; - } - } - return true; - }; + if (GetIsolateGroupData().GetChildIsolatePreparer() == nullptr) { + GetIsolateGroupData().SetChildIsolatePreparer( + [buffers = kernel_buffers_](DartIsolate* isolate) { + for (unsigned long i = 0; i < buffers.size(); i++) { + bool last_piece = i + 1 == buffers.size(); + const std::shared_ptr& buffer = buffers.at(i); + if (!isolate->PrepareForRunningFromKernel(buffer, last_piece)) { + return false; + } + } + return true; + }); } - if (isolate_create_callback_) { - isolate_create_callback_(); + const fml::closure& isolate_create_callback = + GetIsolateGroupData().GetIsolateCreateCallback(); + if (isolate_create_callback) { + isolate_create_callback(); } phase_ = Phase::Ready; @@ -641,6 +644,13 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( return service_isolate->isolate(); } +DartIsolateGroupData& DartIsolate::GetIsolateGroupData() { + std::shared_ptr* isolate_group_data = + static_cast*>( + Dart_IsolateGroupData(isolate())); + return **isolate_group_data; +} + // |Dart_IsolateGroupCreateCallback| Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( const char* advisory_script_uri, @@ -648,10 +658,10 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( const char* package_root, const char* package_config, Dart_IsolateFlags* flags, - std::shared_ptr* parent_embedder_isolate, + std::shared_ptr* parent_isolate_data, char** error) { TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCreateCallback"); - if (parent_embedder_isolate == nullptr && + if (parent_isolate_data == nullptr && strcmp(advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { // The VM attempts to start the VM service for us on |Dart_Initialize|. In // such a case, the callback data will be null and the script URI will be @@ -665,17 +675,44 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( ); } - return CreateDartVMAndEmbedderObjectPair( - advisory_script_uri, // URI - advisory_script_entrypoint, // entrypoint - package_root, // package root - package_config, // package config - flags, // isolate flags - parent_embedder_isolate, // embedder data - false, // is root isolate - error // error - ) - .first; + DartIsolateGroupData& parent_group_data = + (*parent_isolate_data)->GetIsolateGroupData(); + + auto isolate_group_data = + std::make_unique>( + std::shared_ptr(new DartIsolateGroupData( + parent_group_data.GetSettings(), + parent_group_data.GetIsolateSnapshot(), advisory_script_uri, + advisory_script_entrypoint, + parent_group_data.GetChildIsolatePreparer(), + parent_group_data.GetIsolateCreateCallback(), + parent_group_data.GetIsolateShutdownCallback()))); + + TaskRunners null_task_runners(advisory_script_uri, + /* platform= */ nullptr, /* gpu= */ nullptr, + /* ui= */ nullptr, + /* io= */ nullptr); + + auto isolate_data = std::make_unique>( + std::shared_ptr(new DartIsolate( + (*isolate_group_data)->GetSettings(), // settings + null_task_runners, // task_runners + fml::WeakPtr{}, // snapshot_delegate + fml::WeakPtr{}, // io_manager + fml::RefPtr{}, // unref_queue + fml::WeakPtr{}, // image_decoder + advisory_script_uri, // advisory_script_uri + advisory_script_entrypoint, // advisory_script_entrypoint + false))); // is_root_isolate + + Dart_Isolate vm_isolate = CreateDartIsolateGroup( + std::move(isolate_group_data), std::move(isolate_data), flags, error); + + if (*error) { + FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << error; + } + + return vm_isolate; } // |Dart_IsolateInitializeCallback| @@ -689,36 +726,29 @@ bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, return false; } - auto* root_embedder_isolate = static_cast*>( - Dart_CurrentIsolateGroupData()); + auto* isolate_group_data = + static_cast*>( + Dart_CurrentIsolateGroupData()); - TaskRunners null_task_runners( - (*root_embedder_isolate)->GetAdvisoryScriptURI(), - /* platform= */ nullptr, /* gpu= */ nullptr, - /* ui= */ nullptr, - /* io= */ nullptr); + TaskRunners null_task_runners((*isolate_group_data)->GetAdvisoryScriptURI(), + /* platform= */ nullptr, /* gpu= */ nullptr, + /* ui= */ nullptr, + /* io= */ nullptr); auto embedder_isolate = std::make_unique>( std::shared_ptr(new DartIsolate( - (*root_embedder_isolate)->GetSettings(), // settings - (*root_embedder_isolate)->GetIsolateSnapshot(), // isolate_snapshot - null_task_runners, // task_runners - fml::WeakPtr{}, // snapshot_delegate - fml::WeakPtr{}, // io_manager - fml::RefPtr{}, // unref_queue - fml::WeakPtr{}, // image_decoder - (*root_embedder_isolate) - ->GetAdvisoryScriptURI(), // advisory_script_uri - (*root_embedder_isolate) + (*isolate_group_data)->GetSettings(), // settings + null_task_runners, // task_runners + fml::WeakPtr{}, // snapshot_delegate + fml::WeakPtr{}, // io_manager + fml::RefPtr{}, // unref_queue + fml::WeakPtr{}, // image_decoder + (*isolate_group_data)->GetAdvisoryScriptURI(), // advisory_script_uri + (*isolate_group_data) ->GetAdvisoryScriptEntrypoint(), // advisory_script_entrypoint - (*root_embedder_isolate)->child_isolate_preparer_, // preparer - (*root_embedder_isolate)->isolate_create_callback_, // on create - (*root_embedder_isolate)->isolate_shutdown_callback_, // on shutdown - false, // is_root_isolate - false))); // is_group_root_isolate - - // root isolate should have been created via CreateRootIsolate and - // CreateDartVMAndEmbedderObjectPair + false))); // is_root_isolate + + // root isolate should have been created via CreateRootIsolate if (!InitializeIsolate(*embedder_isolate, isolate, error)) { return false; } @@ -731,84 +761,35 @@ bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, return true; } -std::pair> -DartIsolate::CreateDartVMAndEmbedderObjectPair( - const char* advisory_script_uri, - const char* advisory_script_entrypoint, - const char* package_root, - const char* package_config, +Dart_Isolate DartIsolate::CreateDartIsolateGroup( + std::unique_ptr> isolate_group_data, + std::unique_ptr> isolate_data, Dart_IsolateFlags* flags, - std::shared_ptr* p_parent_embedder_isolate, - bool is_root_isolate, char** error) { - TRACE_EVENT0("flutter", "DartIsolate::CreateDartVMAndEmbedderObjectPair"); - - std::unique_ptr> embedder_isolate( - p_parent_embedder_isolate); - - if (embedder_isolate == nullptr) { - *error = - strdup("Parent isolate did not have embedder specific callback data."); - FML_DLOG(ERROR) << *error; - return {nullptr, {}}; - } - - if (!is_root_isolate) { - auto* raw_embedder_isolate = embedder_isolate.release(); - - TaskRunners null_task_runners(advisory_script_uri, nullptr, nullptr, - nullptr, nullptr); - - // Copy most fields from the parent to the child. - embedder_isolate = std::make_unique>( - std::shared_ptr(new DartIsolate( - (*raw_embedder_isolate)->GetSettings(), // settings - (*raw_embedder_isolate)->GetIsolateSnapshot(), // isolate_snapshot - null_task_runners, // task_runners - fml::WeakPtr{}, // snapshot_delegate - fml::WeakPtr{}, // io_manager - fml::RefPtr{}, // unref_queue - fml::WeakPtr{}, // image_decoder - advisory_script_uri, // advisory_script_uri - advisory_script_entrypoint, // advisory_script_entrypoint - (*raw_embedder_isolate)->child_isolate_preparer_, // preparer - (*raw_embedder_isolate)->isolate_create_callback_, // on create - (*raw_embedder_isolate)->isolate_shutdown_callback_, // on shutdown - is_root_isolate, - true)) // is_root_group_isolate - ); - } + TRACE_EVENT0("flutter", "DartIsolate::CreateDartIsolateGroup"); // Create the Dart VM isolate and give it the embedder object as the baton. Dart_Isolate isolate = Dart_CreateIsolateGroup( - advisory_script_uri, // - advisory_script_entrypoint, // - (*embedder_isolate)->GetIsolateSnapshot()->GetDataMapping(), - (*embedder_isolate)->GetIsolateSnapshot()->GetInstructionsMapping(), - flags, - embedder_isolate.get(), // isolate_group_data - embedder_isolate.get(), // isolate_group - error); + (*isolate_group_data)->GetAdvisoryScriptURI().c_str(), + (*isolate_group_data)->GetAdvisoryScriptEntrypoint().c_str(), + (*isolate_group_data)->GetIsolateSnapshot()->GetDataMapping(), + (*isolate_group_data)->GetIsolateSnapshot()->GetInstructionsMapping(), + flags, isolate_group_data.get(), isolate_data.get(), error); if (isolate == nullptr) { - FML_DLOG(ERROR) << *error; - return {nullptr, {}}; - } - - if (!InitializeIsolate(*embedder_isolate, isolate, error)) { - return {nullptr, {}}; + return nullptr; } - auto* isolate_data = static_cast*>( - Dart_IsolateGroupData(isolate)); - FML_DCHECK(isolate_data->get() == embedder_isolate->get()); + // Ownership of the isolate data objects has been transferred to the Dart VM. + std::shared_ptr embedder_isolate(*isolate_data); + isolate_group_data.release(); + isolate_data.release(); - auto weak_embedder_isolate = (*embedder_isolate)->GetWeakIsolatePtr(); + if (!InitializeIsolate(std::move(embedder_isolate), isolate, error)) { + return nullptr; + } - // The ownership of the embedder object is controlled by the Dart VM. So the - // only reference returned to the caller is weak. - embedder_isolate.release(); - return {isolate, weak_embedder_isolate}; + return isolate; } bool DartIsolate::InitializeIsolate( @@ -833,8 +814,10 @@ bool DartIsolate::InitializeIsolate( // also a root isolate) by the utility routines in the VM. However, secondary // isolates will be run by the VM if they are marked as runnable. if (!embedder_isolate->IsRootIsolate()) { - FML_DCHECK(embedder_isolate->child_isolate_preparer_); - if (!embedder_isolate->child_isolate_preparer_(embedder_isolate.get())) { + const ChildIsolatePreparer& child_isolate_preparer = + embedder_isolate->GetIsolateGroupData().GetChildIsolatePreparer(); + FML_DCHECK(child_isolate_preparer); + if (!child_isolate_preparer(embedder_isolate.get())) { *error = strdup("Could not prepare the child isolate to run."); FML_DLOG(ERROR) << *error; return false; @@ -846,7 +829,7 @@ bool DartIsolate::InitializeIsolate( // |Dart_IsolateShutdownCallback| void DartIsolate::DartIsolateShutdownCallback( - std::shared_ptr* isolate_group_data, + std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data) { TRACE_EVENT0("flutter", "DartIsolate::DartIsolateShutdownCallback"); FML_DLOG(INFO) << "DartIsolateShutdownCallback" @@ -857,7 +840,7 @@ void DartIsolate::DartIsolateShutdownCallback( // |Dart_IsolateGroupCleanupCallback| void DartIsolate::DartIsolateGroupCleanupCallback( - std::shared_ptr* isolate_data) { + std::shared_ptr* isolate_data) { TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCleanupCallback"); FML_DLOG(INFO) << "DartIsolateGroupCleanupCallback isolate_data " << isolate_data; @@ -867,38 +850,15 @@ void DartIsolate::DartIsolateGroupCleanupCallback( // |Dart_IsolateCleanupCallback| void DartIsolate::DartIsolateCleanupCallback( - std::shared_ptr* isolate_group_data, + std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data) { TRACE_EVENT0("flutter", "DartIsolate::DartIsolateCleanupCallback"); - if ((*isolate_data)->IsRootIsolate()) { - // isolate_data will be cleaned up as part of IsolateGroup cleanup - FML_DLOG(INFO) - << "DartIsolateCleanupCallback no-op for root isolate isolate_data " - << isolate_data; - return; - } - if ((*isolate_data)->IsGroupRootIsolate()) { - // Even if isolate was not a root isolate(i.e. was spawned), - // it might have IsolateGroup created for it (when - // --no-enable-isolate-groups dart vm flag is used). - // Then its isolate_data will be cleaned up as part of IsolateGroup - // cleanup as well. - FML_DLOG(INFO) << "DartIsolateCleanupCallback no-op for group root isolate " - "isolate_data " - << isolate_data; - return; - } - FML_DLOG(INFO) << "DartIsolateCleanupCallback cleaned up isolate_data " << isolate_data; delete isolate_data; } -fml::RefPtr DartIsolate::GetIsolateSnapshot() const { - return isolate_snapshot_; -} - std::weak_ptr DartIsolate::GetWeakIsolatePtr() { return std::static_pointer_cast(shared_from_this()); } @@ -917,8 +877,11 @@ void DartIsolate::OnShutdownCallback() { } shutdown_callbacks_.clear(); - if (isolate_shutdown_callback_) { - isolate_shutdown_callback_(); + + const fml::closure& isolate_shutdown_callback = + GetIsolateGroupData().GetIsolateShutdownCallback(); + if (isolate_shutdown_callback) { + isolate_shutdown_callback(); } } diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index 100a16fea9f6a..efbc6bde21755 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -24,6 +24,7 @@ namespace flutter { class DartVM; +class DartIsolateGroupData; //------------------------------------------------------------------------------ /// @brief Represents an instance of a live isolate. An isolate is a @@ -205,13 +206,6 @@ class DartIsolate : public UIDartState { // |UIDartState| ~DartIsolate() override; - //---------------------------------------------------------------------------- - /// @brief Get the settings used to create this isolate instance. - /// - /// @return The settings used in the `CreateRootIsolate` call. - /// - const Settings& GetSettings() const; - //---------------------------------------------------------------------------- /// @brief The current phase of the isolate. The engine represents all /// dart isolates as being in one of the known phases. By invoking @@ -376,14 +370,6 @@ class DartIsolate : public UIDartState { /// void AddIsolateShutdownCallback(const fml::closure& closure); - //---------------------------------------------------------------------------- - /// @brief The snapshot used to launch this isolate. This is referenced - /// by any child isolates launched by the root isolate. - /// - /// @return The isolate snapshot. - /// - fml::RefPtr GetIsolateSnapshot() const; - //---------------------------------------------------------------------------- /// @brief A weak pointer to the Dart isolate instance. This instance may /// only be used on the task runner that created the root isolate. @@ -403,14 +389,8 @@ class DartIsolate : public UIDartState { // Root isolate of the VM application bool IsRootIsolate() const { return is_root_isolate_; } - // Isolate that owns IsolateGroup it lives in. - // When --no-enable-isolate-groups dart vm flag is set, - // all child isolates will have their own IsolateGroups. - bool IsGroupRootIsolate() const { return is_group_root_isolate_; } private: - using ChildIsolatePreparer = std::function; - class AutoFireClosure { public: AutoFireClosure(const fml::closure& closure); @@ -424,19 +404,12 @@ class DartIsolate : public UIDartState { friend class DartVM; Phase phase_ = Phase::Unknown; - const Settings settings_; - const fml::RefPtr isolate_snapshot_; std::vector> kernel_buffers_; std::vector> shutdown_callbacks_; - ChildIsolatePreparer child_isolate_preparer_ = nullptr; fml::RefPtr message_handling_task_runner_; - const fml::closure isolate_create_callback_; - const fml::closure isolate_shutdown_callback_; const bool is_root_isolate_; - const bool is_group_root_isolate_; DartIsolate(const Settings& settings, - fml::RefPtr isolate_snapshot, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, @@ -444,11 +417,7 @@ class DartIsolate : public UIDartState { fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - const ChildIsolatePreparer& child_isolate_preparer, - const fml::closure& isolate_create_callback, - const fml::closure& isolate_shutdown_callback, - bool is_root_isolate, - bool is_group_root_isolate); + bool is_root_isolate); FML_WARN_UNUSED_RESULT bool Initialize(Dart_Isolate isolate); void SetMessageHandlingTaskRunner(fml::RefPtr runner); @@ -465,6 +434,8 @@ class DartIsolate : public UIDartState { void OnShutdownCallback(); + DartIsolateGroupData& GetIsolateGroupData(); + // |Dart_IsolateGroupCreateCallback| static Dart_Isolate DartIsolateGroupCreateCallback( const char* advisory_script_uri, @@ -472,7 +443,7 @@ class DartIsolate : public UIDartState { const char* package_root, const char* package_config, Dart_IsolateFlags* flags, - std::shared_ptr* embedder_isolate, + std::shared_ptr* parent_isolate_group, char** error); // |Dart_IsolateInitializeCallback| @@ -485,16 +456,10 @@ class DartIsolate : public UIDartState { Dart_IsolateFlags* flags, char** error); - static std::pair /* embedder */> - CreateDartVMAndEmbedderObjectPair( - const char* advisory_script_uri, - const char* advisory_script_entrypoint, - const char* package_root, - const char* package_config, + static Dart_Isolate CreateDartIsolateGroup( + std::unique_ptr> isolate_group_data, + std::unique_ptr> isolate_data, Dart_IsolateFlags* flags, - std::shared_ptr* parent_embedder_isolate, - bool is_root_isolate, char** error); static bool InitializeIsolate(std::shared_ptr embedder_isolate, @@ -503,17 +468,17 @@ class DartIsolate : public UIDartState { // |Dart_IsolateShutdownCallback| static void DartIsolateShutdownCallback( - std::shared_ptr* isolate_group_data, + std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data); // |Dart_IsolateCleanupCallback| static void DartIsolateCleanupCallback( - std::shared_ptr* isolate_group_data, + std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data); // |Dart_IsolateGroupCleanupCallback| static void DartIsolateGroupCleanupCallback( - std::shared_ptr* isolate_group_data); + std::shared_ptr* isolate_group_data); FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate); }; diff --git a/runtime/dart_isolate_group_data.cc b/runtime/dart_isolate_group_data.cc new file mode 100644 index 0000000000000..20a61894ff680 --- /dev/null +++ b/runtime/dart_isolate_group_data.cc @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/runtime/dart_isolate_group_data.h" +#include "flutter/runtime/dart_snapshot.h" + +namespace flutter { + +DartIsolateGroupData::DartIsolateGroupData( + const Settings& settings, + fml::RefPtr isolate_snapshot, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + const ChildIsolatePreparer& child_isolate_preparer, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback) + : settings_(settings), + isolate_snapshot_(isolate_snapshot), + advisory_script_uri_(advisory_script_uri), + advisory_script_entrypoint_(advisory_script_entrypoint), + child_isolate_preparer_(child_isolate_preparer), + isolate_create_callback_(isolate_create_callback), + isolate_shutdown_callback_(isolate_shutdown_callback) { + FML_DCHECK(isolate_snapshot_) << "Must contain a valid isolate snapshot."; +} + +DartIsolateGroupData::~DartIsolateGroupData() = default; + +const Settings& DartIsolateGroupData::GetSettings() const { + return settings_; +} + +fml::RefPtr DartIsolateGroupData::GetIsolateSnapshot() + const { + return isolate_snapshot_; +} + +const std::string& DartIsolateGroupData::GetAdvisoryScriptURI() const { + return advisory_script_uri_; +} + +const std::string& DartIsolateGroupData::GetAdvisoryScriptEntrypoint() const { + return advisory_script_entrypoint_; +} + +const ChildIsolatePreparer& DartIsolateGroupData::GetChildIsolatePreparer() + const { + return child_isolate_preparer_; +} + +const fml::closure& DartIsolateGroupData::GetIsolateCreateCallback() const { + return isolate_create_callback_; +} + +const fml::closure& DartIsolateGroupData::GetIsolateShutdownCallback() const { + return isolate_shutdown_callback_; +} + +void DartIsolateGroupData::SetChildIsolatePreparer( + const ChildIsolatePreparer& value) { + child_isolate_preparer_ = value; +} + +} // namespace flutter diff --git a/runtime/dart_isolate_group_data.h b/runtime/dart_isolate_group_data.h new file mode 100644 index 0000000000000..400831aee5ec8 --- /dev/null +++ b/runtime/dart_isolate_group_data.h @@ -0,0 +1,63 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_RUNTIME_DART_ISOLATE_GROUP_DATA_H_ +#define FLUTTER_RUNTIME_DART_ISOLATE_GROUP_DATA_H_ + +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/closure.h" +#include "flutter/fml/memory/ref_ptr.h" + +namespace flutter { + +class DartIsolate; +class DartSnapshot; + +using ChildIsolatePreparer = std::function; + +// Object holding state associated with a Dart isolate group. An instance of +// this class will be provided to Dart_CreateIsolateGroup as the +// isolate_group_data. +// +// This object must be thread safe because the Dart VM can invoke the isolate +// group cleanup callback on any thread. +class DartIsolateGroupData { + public: + DartIsolateGroupData(const Settings& settings, + fml::RefPtr isolate_snapshot, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + const ChildIsolatePreparer& child_isolate_preparer, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback); + + ~DartIsolateGroupData(); + + const Settings& GetSettings() const; + fml::RefPtr GetIsolateSnapshot() const; + const std::string& GetAdvisoryScriptURI() const; + const std::string& GetAdvisoryScriptEntrypoint() const; + const ChildIsolatePreparer& GetChildIsolatePreparer() const; + const fml::closure& GetIsolateCreateCallback() const; + const fml::closure& GetIsolateShutdownCallback() const; + + void SetChildIsolatePreparer(const ChildIsolatePreparer& value); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(DartIsolateGroupData); + + const Settings settings_; + const fml::RefPtr isolate_snapshot_; + const std::string advisory_script_uri_; + const std::string advisory_script_entrypoint_; + ChildIsolatePreparer child_isolate_preparer_; + const fml::closure isolate_create_callback_; + const fml::closure isolate_shutdown_callback_; +}; + +} // namespace flutter + +#endif // FLUTTER_RUNTIME_DART_ISOLATE_GROUP_DATA_H_ diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc index 33b60ae26eefc..79ea7db2aded1 100644 --- a/runtime/dart_isolate_unittests.cc +++ b/runtime/dart_isolate_unittests.cc @@ -109,14 +109,15 @@ class AutoIsolateShutdown { return; } fml::AutoResetWaitableEvent latch; - fml::TaskRunner::RunNowOrPostTask(runner_, [isolate = isolate_, &latch]() { - FML_LOG(INFO) << "Shutting down isolate."; - if (!isolate->Shutdown()) { - FML_LOG(ERROR) << "Could not shutdown isolate."; - FML_CHECK(false); - } - latch.Signal(); - }); + fml::TaskRunner::RunNowOrPostTask( + runner_, [isolate = std::move(isolate_), &latch]() { + FML_LOG(INFO) << "Shutting down isolate."; + if (!isolate->Shutdown()) { + FML_LOG(ERROR) << "Could not shutdown isolate."; + FML_CHECK(false); + } + latch.Signal(); + }); latch.Wait(); } From 57afd8634c4a9f20e0ffdb411deb64c51b60b5c0 Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Tue, 10 Dec 2019 11:02:08 -0800 Subject: [PATCH 370/591] Remove specificity on Android and iOS (#14282) We run on more than two platforms these days. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe2d5b2ac4ee0..97cf4c099c218 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ Flutter Engine [![Build Status - Cirrus][]][Build status] Flutter is Google's mobile app SDK for crafting high-quality native interfaces -on iOS and Android in record time. Flutter works with existing code, is used by -developers and organizations around the world, and is free and open source. +in record time. Flutter works with existing code, is used by developers and +organizations around the world, and is free and open source. The Flutter Engine is a portable runtime for hosting [Flutter](https://flutter.dev) applications. It implements Flutter's core From b7c947df5dc4d62bb61eb82d8c3b3606daa74479 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 10 Dec 2019 13:28:20 -0800 Subject: [PATCH 371/591] Roll src/third_party/dart 02a8b015ad..98c13ba18f (5 commits) (#14280) dart-lang/sdk@98c13ba18f [vm/precomp] Reduce arm-32 code size in BoxInt64Instr dart-lang/sdk@37b6b86b90 [infra] Change the name of the num_chunks attribute sent to pubsub. dart-lang/sdk@a846098c18 [vm] Enable ELF loader to read snapshots from memory without any backing file. dart-lang/sdk@bbd850eac8 Tweak the CHANGELOG for 2.7.0. dart-lang/sdk@9456316b51 ErrorReporter should know isNonNullableByDefault. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e7f74269eeb09..a053f2bc0ae80 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '02a8b015ada14d556cc5683970d2ccab351b679c', + 'dart_revision': '98c13ba18f42b2efd3b9330ee05e1ddad08d816c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index a0bcf16d95413..ea606f79059d3 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 387e5572f4d5397cd2535ecdfd59a341 +Signature: 11786b18ee722ed30adc6af4a31201a6 UNUSED LICENSES: From 76d264ee71f21ec93bd2001deb5b5d28fd3efe33 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 10 Dec 2019 13:40:54 -0800 Subject: [PATCH 372/591] [SkParagraph] Convert the height override flag in text styles (#14283) --- third_party/txt/src/skia/paragraph_builder_skia.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/third_party/txt/src/skia/paragraph_builder_skia.cc b/third_party/txt/src/skia/paragraph_builder_skia.cc index 25ba6bd779b81..cc56504c590a1 100644 --- a/third_party/txt/src/skia/paragraph_builder_skia.cc +++ b/third_party/txt/src/skia/paragraph_builder_skia.cc @@ -49,6 +49,7 @@ skt::ParagraphStyle TxtToSkia(const ParagraphStyle& txt) { text_style.setFontStyle(MakeSkFontStyle(txt.font_weight, txt.font_style)); text_style.setFontSize(SkDoubleToScalar(txt.font_size)); text_style.setHeight(SkDoubleToScalar(txt.height)); + text_style.setHeightOverride(txt.has_height_override); text_style.setFontFamilies({SkString(txt.font_family.c_str())}); text_style.setLocale(SkString(txt.locale.c_str())); skia.setTextStyle(text_style); @@ -103,6 +104,7 @@ skt::TextStyle TxtToSkia(const TextStyle& txt) { skia.setLetterSpacing(SkDoubleToScalar(txt.letter_spacing)); skia.setWordSpacing(SkDoubleToScalar(txt.word_spacing)); skia.setHeight(SkDoubleToScalar(txt.height)); + skia.setHeightOverride(txt.has_height_override); skia.setLocale(SkString(txt.locale.c_str())); if (txt.has_background) { From deb8e57135fe3a9665b078baa2ea71f6456d46f5 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Tue, 10 Dec 2019 14:55:46 -0800 Subject: [PATCH 373/591] Fix for tab not working (#14165) * some logs * Masking moddifier state for lock keys if the key code is not the same as the modifier. this fixes tab issue happening when numlock/capslock is on. * removing modifier state for locks * addresing pr comments' --- lib/web_ui/lib/src/engine/keyboard.dart | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 9b3314ed8f64b..4cf743fda86f1 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -117,9 +117,6 @@ const int _modifierShift = 0x01; const int _modifierAlt = 0x02; const int _modifierControl = 0x04; const int _modifierMeta = 0x08; -const int _modifierNumLock = 0x10; -const int _modifierCapsLock = 0x20; -const int _modifierScrollLock = 0x40; /// Creates a bitmask representing the meta state of the [event]. int _getMetaState(html.KeyboardEvent event) { @@ -136,15 +133,8 @@ int _getMetaState(html.KeyboardEvent event) { if (event.getModifierState('Meta')) { metaState |= _modifierMeta; } - if (event.getModifierState('NumLock')) { - metaState |= _modifierNumLock; - } - if (event.getModifierState('CapsLock')) { - metaState |= _modifierCapsLock; - } - if (event.getModifierState('ScrollLock')) { - metaState |= _modifierScrollLock; - } + // TODO: Re-enable lock key modifiers once there is support on Flutter + // Framework. https://github.com/flutter/flutter/issues/46718 return metaState; } From 2bd5cc2cced2f17b6b56172bf34c04befad7d77c Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Tue, 10 Dec 2019 18:46:55 -0500 Subject: [PATCH 374/591] Roll src/third_party/dart 98c13ba18f..c74a8ec2c4 (14 commits) (#14309) dart-lang/sdk@c74a8ec2c4 [vm/bytecode] Fix handling of contravariant parameters in isUncheckedCall dart-lang/sdk@4ce13659b4 Update server API to allow URLs to be returned to dartfix dart-lang/sdk@e22ddd1372 Update TypeSystemTest to reuse more ElementsTypesMixin. dart-lang/sdk@e1ef70fad4 [vm] Add nnbd-experiment to the snapshot feature string dart-lang/sdk@ec6e8c8dfa Clarify the roles of isolate_data and isolate_group_data in Dart_CreateIsolateGroup dart-lang/sdk@b69596bb1b [vm/compiler] Dead code elimination dart-lang/sdk@1cc5d6927a Deprecate ErrorReporter.reportTypeErrorForNode() dart-lang/sdk@28b0f18397 Add basic git package management to trial_migration. dart-lang/sdk@25def20f5d Revert "[infra] Add failing test to test CI systems and approvals workflow" dart-lang/sdk@620dd7dab5 Use just DartType.getDisplayString() in ErrorReporter. dart-lang/sdk@d03a7686d2 [vm/compiler] Fix inlining of SIMD shuffle operations dart-lang/sdk@6a16a59d08 Additional fixes to analyzer pubspec. dart-lang/sdk@bb8bece3bf Widen the analyzer's pubspec constraint on _fe_analyzer_shared dart-lang/sdk@e578eeb235 [infra] Add failing test to test CI systems and approvals workflow --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a053f2bc0ae80..9602dfee7a213 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '98c13ba18f42b2efd3b9330ee05e1ddad08d816c', + 'dart_revision': 'c74a8ec2c46e71491ae6ff3449b2997a6093bea5', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index ea606f79059d3..d4302d7891231 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 11786b18ee722ed30adc6af4a31201a6 +Signature: 14461554cb71cf23106c5f39a3431fd0 UNUSED LICENSES: From 212fbbaf82501ece966ecc1919596dc03369dd66 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 10 Dec 2019 16:02:02 -0800 Subject: [PATCH 375/591] Cleanup the IO thread GrContext (#14265) Fixes https://github.com/flutter/flutter/issues/19558 This is tested by the devicelab test fast_scroll_large_images__memory --- flow/skia_gpu_object.cc | 12 ++++++++++-- flow/skia_gpu_object.h | 8 +++++++- flow/skia_gpu_object_unittests.cc | 14 ++++++++++++++ shell/common/shell_io_manager.cc | 3 ++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/flow/skia_gpu_object.cc b/flow/skia_gpu_object.cc index dd6c6b8436dff..aadb4e3b72f71 100644 --- a/flow/skia_gpu_object.cc +++ b/flow/skia_gpu_object.cc @@ -5,14 +5,17 @@ #include "flutter/flow/skia_gpu_object.h" #include "flutter/fml/message_loop.h" +#include "flutter/fml/trace_event.h" namespace flutter { SkiaUnrefQueue::SkiaUnrefQueue(fml::RefPtr task_runner, - fml::TimeDelta delay) + fml::TimeDelta delay, + fml::WeakPtr context) : task_runner_(std::move(task_runner)), drain_delay_(delay), - drain_pending_(false) {} + drain_pending_(false), + context_(context) {} SkiaUnrefQueue::~SkiaUnrefQueue() { FML_DCHECK(objects_.empty()); @@ -29,6 +32,7 @@ void SkiaUnrefQueue::Unref(SkRefCnt* object) { } void SkiaUnrefQueue::Drain() { + TRACE_EVENT0("flutter", "SkiaUnrefQueue::Drain"); std::deque skia_objects; { std::scoped_lock lock(mutex_); @@ -39,6 +43,10 @@ void SkiaUnrefQueue::Drain() { for (SkRefCnt* skia_object : skia_objects) { skia_object->unref(); } + + if (context_ && skia_objects.size() > 0) { + context_->performDeferredCleanup(std::chrono::milliseconds(0)); + } } } // namespace flutter diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 2a09f982a4386..37850ce0b6cc2 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -12,6 +12,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/task_runner.h" #include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/gpu/GrContext.h" namespace flutter { @@ -34,9 +35,14 @@ class SkiaUnrefQueue : public fml::RefCountedThreadSafe { std::mutex mutex_; std::deque objects_; bool drain_pending_; + fml::WeakPtr context_; + // The `GrContext* context` is only used for signaling Skia to + // performDeferredCleanup. It can be nullptr when such signaling is not needed + // (e.g., in unit tests). SkiaUnrefQueue(fml::RefPtr task_runner, - fml::TimeDelta delay); + fml::TimeDelta delay, + fml::WeakPtr context = {}); ~SkiaUnrefQueue(); diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index 9df82ad11c312..35737708ac5ca 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -11,6 +11,8 @@ #include "gtest/gtest.h" #include "third_party/skia/include/core/SkRefCnt.h" +#include + namespace flutter { namespace testing { @@ -42,6 +44,18 @@ class SkiaGpuObjectTest : public ThreadTest { delayed_unref_queue_(fml::MakeRefCounted( unref_task_runner(), fml::TimeDelta::FromSeconds(3))) { + // The unref queues must be created in the same thread of the + // unref_task_runner so the queue can access the same-thread-only WeakPtr of + // the GrContext constructed during the creation. + std::promise queuesCreated; + unref_task_runner_->PostTask([this, &queuesCreated]() { + unref_queue_ = fml::MakeRefCounted( + unref_task_runner(), fml::TimeDelta::FromSeconds(0)); + delayed_unref_queue_ = fml::MakeRefCounted( + unref_task_runner(), fml::TimeDelta::FromSeconds(3)); + queuesCreated.set_value(true); + }); + queuesCreated.get_future().wait(); ::testing::FLAGS_gtest_death_test_style = "threadsafe"; } diff --git a/shell/common/shell_io_manager.cc b/shell/common/shell_io_manager.cc index 6c23167bf85d2..97badd40bb25b 100644 --- a/shell/common/shell_io_manager.cc +++ b/shell/common/shell_io_manager.cc @@ -62,7 +62,8 @@ ShellIOManager::ShellIOManager( : nullptr), unref_queue_(fml::MakeRefCounted( std::move(unref_queue_task_runner), - fml::TimeDelta::FromMilliseconds(8))), + fml::TimeDelta::FromMilliseconds(8), + GetResourceContext())), weak_factory_(this), is_gpu_disabled_sync_switch_(is_gpu_disabled_sync_switch) { if (!resource_context_) { From 3e55f64c4fd2a8ee88fc9c79a8df5d414719fb75 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Tue, 10 Dec 2019 16:12:53 -0800 Subject: [PATCH 376/591] [web] Update build_web_compilers to 2.7.1 (#14305) * [web] Update build_web_compilers to 2.7.1 This was causing problems with the dart compiler location for web. The compiler seems to be present in out/host_debug_unopt/dart-sdk/lib/dev_compiler/kernel/amd/dart_sdk.js as opposed to out/host_debug_unopt/dart-sdk/lib/dev_compiler/amd/dart_sdk.js * also update build_runner --- lib/web_ui/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index b74bacbf80933..73ecfb5e625bf 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -14,9 +14,9 @@ dev_dependencies: path: 1.6.4 test: 1.6.5 quiver: 2.0.5 - build_runner: 1.7.0 + build_runner: 1.7.2 build_test: 0.10.8 - build_web_compilers: 2.1.5 + build_web_compilers: 2.7.1 yaml: 2.2.0 watcher: 0.9.7+12 web_engine_tester: From b6d4fd164c419f96a0e0e6fb7b9bcca3a5a5cbc3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 20:20:34 -0500 Subject: [PATCH 377/591] Roll fuchsia/sdk/core/mac-amd64 from 9C6UA... to h4iiT... (#14314) Roll fuchsia/sdk/core/mac-amd64 from 9C6UA... to h4iiT... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 9602dfee7a213..dcac5c929c35c 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '9C6UAGvxO7dHRbo0DCAb4qLfqL8Wfqbht64NfKKGn2sC' + 'version': 'h4iiTcBIYoRoQm3RJHJX2ISBLO3ZfbQ0sRxp2cdjA8EC' } ], 'condition': 'host_os == "mac"', From c0b1dc0c6a160459483bfc9487687e6edb507395 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 20:31:57 -0500 Subject: [PATCH 378/591] Roll src/third_party/skia 732c49739fa5..5afc7b2af854 (16 commits) (#14315) https://skia.googlesource.com/skia.git/+log/732c49739fa5..5afc7b2af854 git log 732c49739fa5..5afc7b2af854 --date=short --first-parent --format='%ad %ae %s' 2019-12-10 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-10 herb@google.com Move SubRun from .h to .cpp 2019-12-10 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-10 mtklein@google.com test for c++17 w/ c++14 stdlib 2019-12-10 scroggo@google.com SkWebpCodec: enable a loop count of 1 2019-12-10 herb@google.com Move luminance calculation to GrTextBlob::Make() 2019-12-10 egdaniel@google.com Add GrSurfaceProxyView to the various draw atlas ops. 2019-12-10 egdaniel@google.com Store GrSurfaceProxyView on shadow ops. 2019-12-10 herb@google.com Put SubRuns in an alloc on GrTextBlob 2019-12-10 fmalita@chromium.org [skottie] Add frame time histogram to SkottieSlide UI 2019-12-10 jlavrova@google.com Font features 2019-12-10 fmalita@chromium.org [skottie] Initial SkottieSlide UI 2019-12-10 halcanary@google.com utils/mac/SkCreateCGImageRef: cleanup 2019-12-10 brianosman@google.com GrSkSLFP: "Kind" is always kPipelineStage 2019-12-10 robertphillips@google.com Switch SkImage::CompressionType to be an enum class 2019-12-10 mtklein@google.com delete unused gyp hack Created with: gclient setdep -r src/third_party/skia@5afc7b2af854 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index dcac5c929c35c..2bdd6095671a0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '732c49739fa57cfc26fb3cab2ca950bc440f3ac5', + 'skia_revision': '5afc7b2af854319bedc5acd067f14bbffac269b4', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e33c3828a80a9..e6fb450584b6d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: da692beb732b6da3da400f6b172d8b08 +Signature: f4fd446862a83218bdd8559ce7845752 UNUSED LICENSES: @@ -1911,7 +1911,6 @@ FILE: ../../../third_party/skia/src/core/SkDistanceFieldGen.h FILE: ../../../third_party/skia/src/core/SkDrawable.cpp FILE: ../../../third_party/skia/src/core/SkFont.cpp FILE: ../../../third_party/skia/src/core/SkFont_serial.cpp -FILE: ../../../third_party/skia/src/core/SkForceCPlusPlusLinking.cpp FILE: ../../../third_party/skia/src/core/SkHalf.cpp FILE: ../../../third_party/skia/src/core/SkImageGenerator.cpp FILE: ../../../third_party/skia/src/core/SkMaskCache.cpp From 5b5206e56da6c3c5389c2eba3462a3fe3a922f68 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 20:37:46 -0500 Subject: [PATCH 379/591] Roll fuchsia/sdk/core/linux-amd64 from nqJnP... to UdfLO... (#14316) Roll fuchsia/sdk/core/linux-amd64 from nqJnP... to UdfLO... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2bdd6095671a0..b1a04e1e295ad 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'nqJnPRY-bl_mM90nPzO9iCMgPWLoxXoIERjlqZ_h6gYC' + 'version': 'UdfLOwiV3CK9deXMATb19JxetGTwtSaTZXX5b8g4Wb4C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index ad8cf5009117b..5c88a32a913c6 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 30160b123a845921d1986af63f0d28c0 +Signature: 485b280b9c5de95a2c846d670269f43b UNUSED LICENSES: From 058b4bc5cc79cf3c371da5f0aa75315b2b8faa8f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 10 Dec 2019 20:40:50 -0500 Subject: [PATCH 380/591] Roll src/third_party/dart c74a8ec2c46e..bcd18e67dcae (9 commits) (#14317) https://dart.googlesource.com/sdk.git/+log/c74a8ec2c46e..bcd18e67dcae git log c74a8ec2c46e..bcd18e67dcae --date=short --first-parent --format='%ad %ae %s' 2019-12-11 fishythefish@google.com [dart2js] Mark native classes as needed if they have new-rti is-tests or named type variables. 2019-12-10 sra@google.com [dart2js] Ensure native lookup always initialized 2019-12-10 kustermann@google.com Revert "[vm/compiler] Fix TypeTestingStub -> SubtypeTestCache fallback code if dst_type = TypeParameter" 2019-12-10 regis@google.com [VM/nnbd] Map nullable Never type to Null type. 2019-12-10 scheglov@google.com Issue 39598. Make UNNECESSARY_NULL_AWARE_CALL and other warnings. 2019-12-10 alexmarkov@google.com Revert "[vm/compiler] Dead code elimination" 2019-12-10 vsm@google.com [dartdevc] add forwarding from old docs to new info 2019-12-10 alexmarkov@google.com [vm/nnbd] Fix nullability of types in inferred types attribute 2019-12-10 jcollins@google.com Fix working directory problem shown in demo. Created with: gclient setdep -r src/third_party/dart@bcd18e67dcae If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b1a04e1e295ad..723412562fe6c 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'c74a8ec2c46e71491ae6ff3449b2997a6093bea5', + 'dart_revision': 'bcd18e67dcaed27e32257659fac6a67617aa8294', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index d4302d7891231..30d2efc37078c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 14461554cb71cf23106c5f39a3431fd0 +Signature: f29b388d37d2ea1b9f4eefc18448e379 UNUSED LICENSES: From 6430ecfd3902c52001c1ee70bd8fee33e143e2a9 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Tue, 10 Dec 2019 18:03:55 -0800 Subject: [PATCH 381/591] [fuchsia] Do not Execute paint tasks when there is no vsync (#14298) This should also reduce the number of OnSurfacesPresented calls made. --- shell/platform/fuchsia/flutter/session_connection.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shell/platform/fuchsia/flutter/session_connection.cc b/shell/platform/fuchsia/flutter/session_connection.cc index e74243fedbd62..f79c5579cf73f 100644 --- a/shell/platform/fuchsia/flutter/session_connection.cc +++ b/shell/platform/fuchsia/flutter/session_connection.cc @@ -63,14 +63,14 @@ void SessionConnection::Present( ToggleSignal(vsync_event_handle_, false); } else { PresentSession(); - } - // Execute paint tasks and signal fences. - auto surfaces_to_submit = scene_update_context_.ExecutePaintTasks(frame); + // Execute paint tasks and signal fences. + auto surfaces_to_submit = scene_update_context_.ExecutePaintTasks(frame); - // Tell the surface producer that a present has occurred so it can perform - // book-keeping on buffer caches. - surface_producer_->OnSurfacesPresented(std::move(surfaces_to_submit)); + // Tell the surface producer that a present has occurred so it can perform + // book-keeping on buffer caches. + surface_producer_->OnSurfacesPresented(std::move(surfaces_to_submit)); + } } void SessionConnection::OnSessionSizeChangeHint(float width_change_factor, From 49d6552e76c0e6a8c385632f6a25c0a67e708bea Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 10 Dec 2019 20:04:15 -0800 Subject: [PATCH 382/591] Verify accounting for loop counts in Gif and WebP assets is consistent. (#14321) Asserts that the Skia fix is pulled into Flutter Engine https://skia-review.googlesource.com/c/skia/+/259161. This should have happened in https://github.com/flutter/engine/pull/14315. Fixes https://github.com/flutter/flutter/issues/46289 Fixes https://github.com/flutter/flutter/issues/45246 --- ci/licenses_golden/licenses_flutter | 2 ++ lib/ui/BUILD.gn | 2 ++ lib/ui/fixtures/hello_loop_2.gif | Bin 0 -> 29328 bytes lib/ui/fixtures/hello_loop_2.webp | Bin 0 -> 18008 bytes lib/ui/painting/image_decoder_unittests.cc | 23 +++++++++++++++++++++ 5 files changed, 27 insertions(+) create mode 100644 lib/ui/fixtures/hello_loop_2.gif create mode 100644 lib/ui/fixtures/hello_loop_2.webp diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index bad6d1b797fa5..681868ba215f5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -269,6 +269,8 @@ FILE: ../../../flutter/lib/ui/dart_ui.h FILE: ../../../flutter/lib/ui/dart_wrapper.h FILE: ../../../flutter/lib/ui/fixtures/DashInNooglerHat.jpg FILE: ../../../flutter/lib/ui/fixtures/Horizontal.jpg +FILE: ../../../flutter/lib/ui/fixtures/hello_loop_2.gif +FILE: ../../../flutter/lib/ui/fixtures/hello_loop_2.webp FILE: ../../../flutter/lib/ui/fixtures/ui_test.dart FILE: ../../../flutter/lib/ui/geometry.dart FILE: ../../../flutter/lib/ui/hash_codes.dart diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index d548d94594184..967f061f1cc58 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -155,6 +155,8 @@ if (current_toolchain == host_toolchain) { fixtures = [ "fixtures/DashInNooglerHat.jpg", "fixtures/Horizontal.jpg", + "fixtures/hello_loop_2.gif", + "fixtures/hello_loop_2.webp", ] } diff --git a/lib/ui/fixtures/hello_loop_2.gif b/lib/ui/fixtures/hello_loop_2.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9ac36d917d22f46e0ff15e102580f76cdb6cbf4 GIT binary patch literal 29328 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 literal 0 HcmV?d00001 diff --git a/lib/ui/fixtures/hello_loop_2.webp b/lib/ui/fixtures/hello_loop_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..cc79d776f8a68a1cf84ddae89fc1a77ae2015141 GIT binary patch literal 18008 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 IqnGj~0Dgjeo&W#< literal 0 HcmV?d00001 diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index 258da2dbeee18..47e7bafe96052 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc @@ -9,6 +9,7 @@ #include "flutter/testing/test_gl_surface.h" #include "flutter/testing/testing.h" #include "flutter/testing/thread_test.h" +#include "third_party/skia/include/codec/SkCodec.h" namespace flutter { namespace testing { @@ -496,5 +497,27 @@ TEST_F(ImageDecoderFixtureTest, CanResizeWithoutDecode) { latch.Wait(); } +// Verifies https://skia-review.googlesource.com/c/skia/+/259161 is present in +// Flutter. +TEST(ImageDecoderTest, + VerifyCodecRepeatCountsForGifAndWebPAreConsistentWithLoopCounts) { + auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif"); + auto webp_mapping = OpenFixtureAsSkData("hello_loop_2.webp"); + + ASSERT_TRUE(gif_mapping); + ASSERT_TRUE(webp_mapping); + + auto gif_codec = SkCodec::MakeFromData(gif_mapping); + auto webp_codec = SkCodec::MakeFromData(webp_mapping); + + ASSERT_TRUE(gif_codec); + ASSERT_TRUE(webp_codec); + + // Both fixtures have a loop count of 2 which should lead to the repeat count + // of 1 + ASSERT_EQ(gif_codec->getRepetitionCount(), 1); + ASSERT_EQ(webp_codec->getRepetitionCount(), 1); +} + } // namespace testing } // namespace flutter From 2ae0d4273b147172e7957413f8dedf2ebb5bd2de Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 11 Dec 2019 00:26:20 -0500 Subject: [PATCH 383/591] Roll src/third_party/dart bcd18e67dcae..bd008dd1e406 (3 commits) (#14322) https://dart.googlesource.com/sdk.git/+log/bcd18e67dcae..bd008dd1e406 git log bcd18e67dcae..bd008dd1e406 --date=short --first-parent --format='%ad %ae %s' 2019-12-11 srawlins@google.com Allow empty URI strings in library directives 2019-12-11 fishythefish@google.com Revert "[dart2js] Mark native classes as needed if they have new-rti is-tests or" 2019-12-11 nshahan@google.com [test_runner] Temporarily remove some packages from the DDC config Created with: gclient setdep -r src/third_party/dart@bd008dd1e406 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 723412562fe6c..58e7edd847d7b 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'bcd18e67dcaed27e32257659fac6a67617aa8294', + 'dart_revision': 'bd008dd1e40625f0dbaadaba12dc4c23394fabb6', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From ac95536a64360061bba9269e2192df8d9e23c42f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 11 Dec 2019 11:07:04 -0500 Subject: [PATCH 384/591] Roll src/third_party/skia 5afc7b2af854..75799967be60 (2 commits) (#14324) https://skia.googlesource.com/skia.git/+log/5afc7b2af854..75799967be60 git log 5afc7b2af854..75799967be60 --date=short --first-parent --format='%ad %ae %s' 2019-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 f60da87424a4..d6053daa6203 (8 commits) 2019-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 31916f494c2e..50211670b4bf (28 commits) Created with: gclient setdep -r src/third_party/skia@75799967be60 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 58e7edd847d7b..0e87f2be69d54 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '5afc7b2af854319bedc5acd067f14bbffac269b4', + 'skia_revision': '75799967be60668214cd3a307203128d0d583095', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e6fb450584b6d..6db219c33cd67 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f4fd446862a83218bdd8559ce7845752 +Signature: d7102792d8fd65d68dec31ed1c1a786d UNUSED LICENSES: From 02fb9c1f94376de5a176f1b795caeaff42ebf4bc Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 11 Dec 2019 11:09:20 -0500 Subject: [PATCH 385/591] Roll src/third_party/dart bd008dd1e406..d9fa37e85d5c (1 commits) (#14325) https://dart.googlesource.com/sdk.git/+log/bd008dd1e406..d9fa37e85d5c git log bd008dd1e406..d9fa37e85d5c --date=short --first-parent --format='%ad %ae %s' 2019-12-11 johnniwinther@google.com [kernel] Use Never as static type for throw/rethrow Created with: gclient setdep -r src/third_party/dart@d9fa37e85d5c If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 0e87f2be69d54..ff1f0657b06a1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'bd008dd1e40625f0dbaadaba12dc4c23394fabb6', + 'dart_revision': 'd9fa37e85d5c55260042c460725de4e98e62a1a9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From db60ebc6325cfea1a8c3722035ca7199a664df47 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 11 Dec 2019 11:13:34 -0500 Subject: [PATCH 386/591] Roll fuchsia/sdk/core/mac-amd64 from h4iiT... to otkJA... (#14327) Roll fuchsia/sdk/core/mac-amd64 from h4iiT... to otkJA... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index ff1f0657b06a1..e3a4740855115 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'h4iiTcBIYoRoQm3RJHJX2ISBLO3ZfbQ0sRxp2cdjA8EC' + 'version': 'otkJAJHPw68CJ4itPbDXj1RSMjEJ0yV3dwaCxgquRXcC' } ], 'condition': 'host_os == "mac"', From 18519e916919439132e5b3db7c40a6cb7082eae3 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 11 Dec 2019 11:47:30 -0800 Subject: [PATCH 387/591] Roll fuchsia/sdk/core/linux-amd64 from UdfLO... to 9wKTl... (#14342) * Roll fuchsia/sdk/core/linux-amd64 from UdfLO... to 9wKTl... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md * async set dispatcher is now from async-default --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 6 +----- shell/platform/fuchsia/flutter/BUILD.gn | 1 + shell/platform/fuchsia/flutter/thread.cc | 1 + 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/DEPS b/DEPS index e3a4740855115..6dcd7dcbb34f4 100644 --- a/DEPS +++ b/DEPS @@ -562,7 +562,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'UdfLOwiV3CK9deXMATb19JxetGTwtSaTZXX5b8g4Wb4C' + 'version': '9wKTl6OgTwPYnwLcHBYA9ok18H58VaFkxF1cP_zA3fAC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 5c88a32a913c6..c188e49c55804 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 485b280b9c5de95a2c846d670269f43b +Signature: f4db1350a58dc05423f556cfae14d815 UNUSED LICENSES: @@ -501,7 +501,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.scenic.scheduling/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.simplecamera/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysinfo/meta.json @@ -1440,7 +1439,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/privacy.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/settings.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/setup.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/system.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.simplecamera/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/types.fidl @@ -2177,7 +2175,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.scenic.scheduling/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.simplecamera/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysinfo/meta.json @@ -2613,7 +2610,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/connectivity.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/net.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.process/launcher.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.process/resolver.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.simplecamera/simple_camera.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/job_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysinfo/sysinfo.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysmem/allocator.fidl diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 2db9b474ee67c..956ccc8a953f7 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -333,6 +333,7 @@ executable("flutter_runner_unittests") { ":aot", ":flutter_runner_fixtures", "//build/fuchsia/fidl:fuchsia.accessibility.semantics", + "//build/fuchsia/pkg:async-default", "//build/fuchsia/pkg:async-loop-cpp", "//build/fuchsia/pkg:async-loop-default", "//build/fuchsia/pkg:scenic_cpp", diff --git a/shell/platform/fuchsia/flutter/thread.cc b/shell/platform/fuchsia/flutter/thread.cc index f15ea309a239d..caddc269c4b26 100644 --- a/shell/platform/fuchsia/flutter/thread.cc +++ b/shell/platform/fuchsia/flutter/thread.cc @@ -10,6 +10,7 @@ #include #include +#include #include "flutter/fml/logging.h" #include "loop.h" From fb9dfe0fb672d9b213b81efd73b5bcb4c4788769 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 11 Dec 2019 13:40:38 -0800 Subject: [PATCH 388/591] [fuchsia] Move async_get_default_dispatcher include to the header (#14351) --- shell/platform/fuchsia/flutter/thread.cc | 1 - shell/platform/fuchsia/flutter/thread.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/fuchsia/flutter/thread.cc b/shell/platform/fuchsia/flutter/thread.cc index caddc269c4b26..f15ea309a239d 100644 --- a/shell/platform/fuchsia/flutter/thread.cc +++ b/shell/platform/fuchsia/flutter/thread.cc @@ -10,7 +10,6 @@ #include #include -#include #include "flutter/fml/logging.h" #include "loop.h" diff --git a/shell/platform/fuchsia/flutter/thread.h b/shell/platform/fuchsia/flutter/thread.h index 1d7587375b4fe..00d1cef9492dd 100644 --- a/shell/platform/fuchsia/flutter/thread.h +++ b/shell/platform/fuchsia/flutter/thread.h @@ -10,6 +10,7 @@ #include #include +#include #include "flutter/fml/macros.h" From 3ebb7b426a7bb318a7847d6214592e6a13f41252 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 11 Dec 2019 17:15:45 -0500 Subject: [PATCH 389/591] Roll src/third_party/skia 75799967be60..3517aa7b14ad (3 commits) (#14345) https://skia.googlesource.com/skia.git/+log/75799967be60..3517aa7b14ad git log 75799967be60..3517aa7b14ad --date=short --first-parent --format='%ad %ae %s' 2019-12-11 bsalomon@google.com Fix stroked arcs for mirror matrices. 2019-12-11 benjaminwagner@google.com Update iOS provisioning profile. 2019-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src ffcaa57570c0..cf2545c5111b (466 commits) Created with: gclient setdep -r src/third_party/skia@3517aa7b14ad If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6dcd7dcbb34f4..6debd190a1bbb 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '75799967be60668214cd3a307203128d0d583095', + 'skia_revision': '3517aa7b14ad52aa662bf38932f2ba358a9f8318', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 6db219c33cd67..d1785a0f4679b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: d7102792d8fd65d68dec31ed1c1a786d +Signature: 6bffa87a7c5c059414f1658dd2762fdf UNUSED LICENSES: From 2713225a6eefe44ccefe379615b144a40845b879 Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Wed, 11 Dec 2019 14:28:00 -0800 Subject: [PATCH 390/591] Remove duplicate and outdated src/third_party/dart/tools/sdks entry from DEPS. (#14340) * Drop duplicate entry --- DEPS | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/DEPS b/DEPS index 6debd190a1bbb..591b1aa5d3335 100644 --- a/DEPS +++ b/DEPS @@ -494,16 +494,6 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/dart/tools/sdks': { - 'packages': [ - { - 'package': 'dart/dart-sdk/${{platform}}', - 'version': 'version:2.4.0' - } - ], - 'dep_type': 'cipd', - }, - 'src/third_party/dart/pkg/analysis_server/language_model': { 'packages': [ { From 80d80ff6e6849e7e131c1f3fd4f56df883814ee1 Mon Sep 17 00:00:00 2001 From: Dwayne Slater Date: Wed, 11 Dec 2019 14:53:50 -0800 Subject: [PATCH 391/591] Add ability to control dithering on Paint (#13868) --- lib/ui/painting.dart | 38 +++++++++++- lib/ui/painting/paint.cc | 7 ++- lib/web_ui/lib/src/ui/painting.dart | 21 +++++++ testing/dart/canvas_test.dart | 58 ++++++++++++++++-- .../canvas_test_dithered_gradient.png | Bin 0 -> 1701 bytes testing/resources/canvas_test_gradient.png | Bin 0 -> 1029 bytes 6 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 testing/resources/canvas_test_dithered_gradient.png create mode 100644 testing/resources/canvas_test_gradient.png diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index b30f3835902c5..a6c96ac929884 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1066,6 +1066,7 @@ class Paint { static const int _kMaskFilterBlurStyleIndex = 10; static const int _kMaskFilterSigmaIndex = 11; static const int _kInvertColorIndex = 12; + static const int _kDitherIndex = 13; static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2; static const int _kColorOffset = _kColorIndex << 2; @@ -1080,8 +1081,9 @@ class Paint { static const int _kMaskFilterBlurStyleOffset = _kMaskFilterBlurStyleIndex << 2; static const int _kMaskFilterSigmaOffset = _kMaskFilterSigmaIndex << 2; static const int _kInvertColorOffset = _kInvertColorIndex << 2; + static const int _kDitherOffset = _kDitherIndex << 2; // If you add more fields, remember to update _kDataByteCount. - static const int _kDataByteCount = 52; + static const int _kDataByteCount = 56; // Binary format must match the deserialization code in paint.cc. List _objects; @@ -1090,6 +1092,14 @@ class Paint { static const int _kImageFilterIndex = 2; static const int _kObjectCount = 3; // Must be one larger than the largest index. + /// Constructs an empty [Paint] object with all fields initialized to + /// their defaults. + Paint() { + if (enableDithering) { + _dither = true; + } + } + /// Whether to apply anti-aliasing to lines and images drawn on the /// canvas. /// @@ -1417,6 +1427,30 @@ class Paint { _data.setInt32(_kInvertColorOffset, value ? 1 : 0, _kFakeHostEndian); } + bool get _dither { + return _data.getInt32(_kDitherOffset, _kFakeHostEndian) == 1; + } + set _dither(bool value) { + _data.setInt32(_kDitherOffset, value ? 1 : 0, _kFakeHostEndian); + } + + /// Whether to dither the output when drawing images. + /// + /// If false, the default value, dithering will be enabled when the input + /// color depth is higher than the output color depth. For example, + /// drawing an RGB8 image onto an RGB565 canvas. + /// + /// This value also controls dithering of [shader]s, which can make + /// gradients appear smoother. + /// + /// Whether or not dithering affects the output is implementation defined. + /// Some implementations may choose to ignore this completely, if they're + /// unable to control dithering. + /// + /// To ensure that dithering is consistently enabled for your entire + /// application, set this to true before invoking any drawing related code. + static bool enableDithering = false; + @override String toString() { final StringBuffer result = StringBuffer(); @@ -1475,6 +1509,8 @@ class Paint { } if (invertColors) result.write('${semicolon}invert: $invertColors'); + if (_dither) + result.write('${semicolon}dither: $_dither'); result.write(')'); return result.toString(); } diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 00006c3f989e9..2648ff2f66f41 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -32,7 +32,8 @@ constexpr int kMaskFilterIndex = 9; constexpr int kMaskFilterBlurStyleIndex = 10; constexpr int kMaskFilterSigmaIndex = 11; constexpr int kInvertColorIndex = 12; -constexpr size_t kDataByteCount = 52; // 4 * (last index + 1) +constexpr int kDitherIndex = 13; +constexpr size_t kDataByteCount = 56; // 4 * (last index + 1) // Indices for objects. constexpr int kShaderIndex = 0; @@ -154,6 +155,10 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { paint_.setColorFilter(invert_filter); } + if (uint_data[kDitherIndex]) { + paint_.setDither(true); + } + switch (uint_data[kMaskFilterIndex]) { case Null: break; diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index 9a26fc9527177..1460e1b08f2cd 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -961,6 +961,10 @@ class PaintData { class Paint { PaintData _paintData = PaintData(); + /// Constructs an empty [Paint] object with all fields initialized to + /// their defaults. + Paint(); + /// A blend mode to apply when a shape is drawn or a layer is composited. /// /// The source colors are from the shape being drawn (e.g. from @@ -1182,6 +1186,23 @@ class Paint { // TODO(flutter/flutter#35156): Implement ImageFilter. } + /// Whether to dither the output when drawing images. + /// + /// If false, the default value, dithering will be enabled when the input + /// color depth is higher than the output color depth. For example, + /// drawing an RGB8 image onto an RGB565 canvas. + /// + /// This value also controls dithering of [shader]s, which can make + /// gradients appear smoother. + /// + /// Whether or not dithering affects the output is implementation defined. + /// Some implementations may choose to ignore this completely, if they're + /// unable to control dithering. + /// + /// To ensure that dithering is consistently enabled for your entire + /// application, set this to true before invoking any drawing related code. + static bool enableDithering = false; + // True if Paint instance has used in RecordingCanvas. bool _frozen = false; diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 32249ae4f0b56..2bfe63d5eca1d 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -109,14 +109,20 @@ Future fuzzyGoldenImageCompare( Image image, String goldenImageName) async { final String imagesPath = path.join('flutter', 'testing', 'resources'); final File file = File(path.join(imagesPath, goldenImageName)); - final Uint8List goldenData = await file.readAsBytes(); - final Codec codec = await instantiateImageCodec(goldenData); - final FrameInfo frame = await codec.getNextFrame(); - expect(frame.image.height, equals(image.width)); - expect(frame.image.width, equals(image.height)); + bool areEqual = false; + + if (file.existsSync()) { + final Uint8List goldenData = await file.readAsBytes(); + + final Codec codec = await instantiateImageCodec(goldenData); + final FrameInfo frame = await codec.getNextFrame(); + expect(frame.image.height, equals(image.width)); + expect(frame.image.width, equals(image.height)); + + areEqual = await fuzzyCompareImages(frame.image, image); + } - final bool areEqual = await fuzzyCompareImages(frame.image, image); if (!areEqual) { final ByteData pngData = await image.toByteData(); final ByteBuffer buffer = pngData.buffer; @@ -151,4 +157,44 @@ void main() { await fuzzyGoldenImageCompare(image, 'canvas_test_toImage.png'); expect(areEqual, true); }); + + Gradient makeGradient() { + return Gradient.linear( + Offset.zero, + const Offset(100, 100), + const [Color(0xFF4C4D52), Color(0xFF202124)], + ); + } + + test('Simple gradient', () async { + Paint.enableDithering = false; + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final Paint paint = Paint()..shader = makeGradient(); + canvas.drawPaint(paint); + final Picture picture = recorder.endRecording(); + final Image image = await picture.toImage(100, 100); + expect(image.width, equals(100)); + expect(image.height, equals(100)); + + final bool areEqual = + await fuzzyGoldenImageCompare(image, 'canvas_test_gradient.png'); + expect(areEqual, true); + }); + + test('Simple dithered gradient', () async { + Paint.enableDithering = true; + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final Paint paint = Paint()..shader = makeGradient(); + canvas.drawPaint(paint); + final Picture picture = recorder.endRecording(); + final Image image = await picture.toImage(100, 100); + expect(image.width, equals(100)); + expect(image.height, equals(100)); + + final bool areEqual = + await fuzzyGoldenImageCompare(image, 'canvas_test_dithered_gradient.png'); + expect(areEqual, true); + }); } diff --git a/testing/resources/canvas_test_dithered_gradient.png b/testing/resources/canvas_test_dithered_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..d8062f2dc1f35424af652e5580097b4c8b1a1d50 GIT binary patch literal 1701 zcmV;W23q-vP)XMhftwzgzDorH#n7nhnuwsrAhbD63C{rP+S`uZx@>$Tj* z%jHs}eZ5{vDbl`{+XL-?e|cVq_P@)%|1Rw!{5HdwqKfdtl4CdNC2{eKHVZ}+B<+V4&uB8_JcZgi zPTH=#65}L|b_&~^T!iOG-&Z=e*BRdYvEASA$o%p?+^@DV9<@z^onc2^L5uy9&ftFS zC>K528MFJ)L6+-}I)QgaZ~kK9w%Aoy=9bLRL42k{JU_<>R+)?u7qQNHMjXuyjK5(B zW5!G}T4l+KX?Bkpm^5qKvmS%ND>h=p95e3E4lv_sU40wlU!#nT*uRGvyfb?1o8Q0R z86%8f@sR$i%otYeYnfpyHfDy;K-N)SC)m!wvwnmbMcI%0I5TkGsw-$ao$*YC2r6U7 z=jx0CHfILvzM49t&kVFZX0XmcyLUWJGvxO$<2`jo8~LkQ#gNxBW41H=F`oamI-_03 znOC173_1aAcD!gmZF8wJVrF2cROTc~#xfRr_MlsIA7Imm^x#e83pWh%)t99PX=bRl(;i6Id+p{%)ohcFrpy6i_Wk`q^~m! z`8~||Or0^nkklD{X5jqmbw+Rg>UV|?;_~P*!#~?oXNr3~@T&Jku{(yV=Mox!e? z@rQSNU&)Mf=!{+*tL==`88RvDzHM}dwY8tBZ%Uovb;@4MpbY7E1}4XDa*P=`j}Ar@ zq)*WqQ8Fk)547ANR7kiz6&$H`OXGEP5>psH}S#e{`Ks$8?>p(p+KfsVy>8Uf&-jf*^|H^{F zD>ik;9?Zb_3#FbqV=XgcKQS}#dhvNk>I`o`J!UY5*!^&33@{{h#*xfGou?QQ1u1n# z5&OsFtY=2RkklEY%!oThT$wI)inf9+@9*<9@<-dsAmu2-OumP89m)c8In377SKbigqzN_yQ|NIKN00000NkvXXu0mjfs6tU# literal 0 HcmV?d00001 diff --git a/testing/resources/canvas_test_gradient.png b/testing/resources/canvas_test_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..89f3f63dc518a1dfd84c9052125fdf6ac079253f GIT binary patch literal 1029 zcmeAS@N?(olHy`uVBq!ia0vp^DImZPyNbG536qR(6c%0y1_uDX$r)6RaP^_KtCs^Z7F}5e1$#N1C)Oos>^Ygrk zjO5{b>?IMgN~5i%(3Iyy1P|w8cAgUfKuVk^W4VNoo4jqO45fGF1`6k zk!QC0?}CX2&l(szj~d8C&C-=Sw4t(vUz{^prY9)Ukf(iGT6eoaj@-{%ZVUY+l;wjz z&QkBG1o}kAN@kA1u>%L*a2^h=TGz~&o6@nxO~O5K$}PT1MdzSqv+dv4uph2UcL8eK ze4rq+?F3t9-h0KK^D%)uoQI!T@;#obxJz-~?P?C?`Mf|MYYZ)CwQEv1@~Ff`ezeoC#x(qD%g_(Tqy zCiZX?WUkWz#*iw{ibWFZ*pu7NbfiP#sz(K6!{MDvcTQE`-?#f{fvv>SXN`@fOC{EE zCNE={ehm_B6H`14*03i(J9EM|5@^#z0|R%UGZrU0)P(}o15?K*C9m0c_>ajl#e7Qn z|2y_pi$T@Ff_EEbUzN8M9zVm8y*`qs^pM44OFm{Tr@02_D$X6ONap|r&@Iij;=>xH z`4Z<=9xbSo_;t6)UgCnA;T-8>S-`;Y1*WUS&s*9*@_l{5(USNjhWW7N)7y(Uo#(%{ zF_W0^V`;+XgBn}acvk!f+Hf Date: Wed, 11 Dec 2019 15:10:55 -0800 Subject: [PATCH 392/591] Conditionally use offscreen root surface only when needed Currently helps primarily on iOS when no BackdropFilter is present by lowering energy usage --- flow/compositor_context.cc | 18 +++++- flow/compositor_context.h | 5 ++ flow/layers/backdrop_filter_layer.cc | 7 +++ flow/layers/backdrop_filter_layer.h | 2 + .../layers/backdrop_filter_layer_unittests.cc | 31 +++++++++ flow/layers/clip_path_layer.cc | 6 +- flow/layers/clip_path_layer.h | 4 ++ flow/layers/clip_path_layer_unittests.cc | 60 ++++++++++++++++++ flow/layers/clip_rect_layer.cc | 6 +- flow/layers/clip_rect_layer.h | 4 ++ flow/layers/clip_rect_layer_unittests.cc | 59 +++++++++++++++++ flow/layers/clip_rrect_layer.cc | 6 +- flow/layers/clip_rrect_layer.h | 4 ++ flow/layers/clip_rrect_layer_unittests.cc | 60 ++++++++++++++++++ flow/layers/color_filter_layer.cc | 7 +++ flow/layers/color_filter_layer.h | 2 + flow/layers/color_filter_layer_unittests.cc | 19 ++++++ flow/layers/layer.cc | 28 +++++++++ flow/layers/layer.h | 27 ++++++++ flow/layers/layer_tree.cc | 7 ++- flow/layers/layer_tree.h | 9 ++- flow/layers/layer_tree_unittests.cc | 1 + flow/layers/opacity_layer.cc | 2 + flow/layers/opacity_layer_unittests.cc | 19 ++++++ flow/layers/physical_shape_layer.cc | 4 +- flow/layers/physical_shape_layer.h | 4 ++ flow/layers/physical_shape_layer_unittests.cc | 63 +++++++++++++++++++ flow/layers/shader_mask_layer.cc | 6 ++ flow/layers/shader_mask_layer.h | 2 + flow/layers/shader_mask_layer_unittests.cc | 22 +++++++ flow/testing/layer_test.h | 1 + flow/testing/mock_layer.cc | 9 ++- flow/testing/mock_layer.h | 4 +- shell/common/rasterizer.cc | 5 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 7 ++- shell/gpu/gpu_surface_gl.cc | 41 ++---------- shell/gpu/gpu_surface_gl.h | 1 - shell/gpu/gpu_surface_gl_delegate.cc | 4 +- shell/gpu/gpu_surface_gl_delegate.h | 5 +- shell/gpu/gpu_surface_metal.mm | 2 +- shell/gpu/gpu_surface_software.cc | 4 +- shell/gpu/gpu_surface_vulkan.cc | 2 +- shell/platform/darwin/ios/ios_surface_gl.h | 2 +- shell/platform/darwin/ios/ios_surface_gl.mm | 12 ++-- .../fuchsia/flutter/compositor_context.cc | 2 + .../fuchsia/flutter/compositor_context.h | 1 + shell/platform/fuchsia/flutter/surface.cc | 6 +- 48 files changed, 536 insertions(+), 72 deletions(-) diff --git a/flow/compositor_context.cc b/flow/compositor_context.cc index 0fafe0f956409..68f308fbf2f57 100644 --- a/flow/compositor_context.cc +++ b/flow/compositor_context.cc @@ -36,10 +36,11 @@ std::unique_ptr CompositorContext::AcquireFrame( ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled, + bool surface_supports_readback, fml::RefPtr gpu_thread_merger) { return std::make_unique( *this, gr_context, canvas, view_embedder, root_surface_transformation, - instrumentation_enabled, gpu_thread_merger); + instrumentation_enabled, surface_supports_readback, gpu_thread_merger); } CompositorContext::ScopedFrame::ScopedFrame( @@ -49,6 +50,7 @@ CompositorContext::ScopedFrame::ScopedFrame( ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled, + bool surface_supports_readback, fml::RefPtr gpu_thread_merger) : context_(context), gr_context_(gr_context), @@ -56,6 +58,7 @@ CompositorContext::ScopedFrame::ScopedFrame( view_embedder_(view_embedder), root_surface_transformation_(root_surface_transformation), instrumentation_enabled_(instrumentation_enabled), + surface_supports_readback_(surface_supports_readback), gpu_thread_merger_(gpu_thread_merger) { context_.BeginFrame(*this, instrumentation_enabled_); } @@ -67,7 +70,8 @@ CompositorContext::ScopedFrame::~ScopedFrame() { RasterStatus CompositorContext::ScopedFrame::Raster( flutter::LayerTree& layer_tree, bool ignore_raster_cache) { - layer_tree.Preroll(*this, ignore_raster_cache); + bool root_needs_readback = layer_tree.Preroll(*this, ignore_raster_cache); + bool needs_save_layer = root_needs_readback && !surface_supports_readback(); PostPrerollResult post_preroll_result = PostPrerollResult::kSuccess; if (view_embedder_ && gpu_thread_merger_) { post_preroll_result = view_embedder_->PostPrerollAction(gpu_thread_merger_); @@ -79,9 +83,19 @@ RasterStatus CompositorContext::ScopedFrame::Raster( // Clearing canvas after preroll reduces one render target switch when preroll // paints some raster cache. if (canvas()) { + if (needs_save_layer) { + FML_LOG(INFO) << "Using SaveLayer to protect non-readback surface"; + SkRect bounds = SkRect::Make(layer_tree.frame_size()); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + canvas()->saveLayer(&bounds, &paint); + } canvas()->clear(SK_ColorTRANSPARENT); } layer_tree.Paint(*this, ignore_raster_cache); + if (canvas() && needs_save_layer) { + canvas()->restore(); + } return RasterStatus::kSuccess; } diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 2872b5aa16180..6698d3f41a2f1 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -45,6 +45,7 @@ class CompositorContext { ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled, + bool surface_supports_readback, fml::RefPtr gpu_thread_merger); virtual ~ScopedFrame(); @@ -59,6 +60,8 @@ class CompositorContext { return root_surface_transformation_; } + bool surface_supports_readback() { return surface_supports_readback_; } + GrContext* gr_context() const { return gr_context_; } virtual RasterStatus Raster(LayerTree& layer_tree, @@ -71,6 +74,7 @@ class CompositorContext { ExternalViewEmbedder* view_embedder_; const SkMatrix& root_surface_transformation_; const bool instrumentation_enabled_; + const bool surface_supports_readback_; fml::RefPtr gpu_thread_merger_; FML_DISALLOW_COPY_AND_ASSIGN(ScopedFrame); @@ -86,6 +90,7 @@ class CompositorContext { ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled, + bool surface_supports_readback, fml::RefPtr gpu_thread_merger); void OnGrContextCreated(); diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index d5799cc4f95dc..ce86db3deaad7 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -9,6 +9,13 @@ namespace flutter { BackdropFilterLayer::BackdropFilterLayer(sk_sp filter) : filter_(std::move(filter)) {} +void BackdropFilterLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_)); + ContainerLayer::Preroll(context, matrix); +} + void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index 732a1ac27e89a..e3f34252b4be1 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -15,6 +15,8 @@ class BackdropFilterLayer : public ContainerLayer { public: BackdropFilterLayer(sk_sp filter); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Paint(PaintContext& context) const override; private: diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc index e02745adcc83a..50afecbdc7824 100644 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -182,5 +182,36 @@ TEST_F(BackdropFilterLayerTest, Nested) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +TEST_F(BackdropFilterLayerTest, Readback) { + sk_sp no_filter; + auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto initial_transform = SkMatrix(); + + // BDF with filter always reads from surface + auto layer1 = std::make_shared(layer_filter); + preroll_context()->surface_needs_readback = false; + layer1->Preroll(preroll_context(), initial_transform); + EXPECT_TRUE(preroll_context()->surface_needs_readback); + + // BDF with no filter does not read from surface itself + auto layer2 = std::make_shared(no_filter); + preroll_context()->surface_needs_readback = false; + layer2->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); + + // BDF with no filter does not block prior readback value + preroll_context()->surface_needs_readback = true; + layer2->Preroll(preroll_context(), initial_transform); + EXPECT_TRUE(preroll_context()->surface_needs_readback); + + // BDF with no filter blocks child with readback + auto mock_layer = + std::make_shared(SkPath(), SkPaint(), false, false, true); + layer2->Add(mock_layer); + preroll_context()->surface_needs_readback = false; + layer2->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 4353debfc095e..3d420657bf079 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -22,6 +22,8 @@ void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect clip_path_bounds = clip_path_.getBounds(); children_inside_clip_ = context->cull_rect.intersect(clip_path_bounds); if (children_inside_clip_) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); context->mutators_stack.PushClipPath(clip_path_); SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_paint_bounds); @@ -57,11 +59,11 @@ void ClipPathLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->clipPath(clip_path_, clip_behavior_ != Clip::hardEdge); - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); } PaintChildren(context); - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { context.internal_nodes_canvas->restore(); } } diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index aac23d6935b76..ac7440625bb62 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -17,6 +17,10 @@ class ClipPathLayer : public ContainerLayer { void Paint(PaintContext& context) const override; + bool UsesSaveLayer() const { + return clip_behavior_ == Clip::antiAliasWithSaveLayer; + } + #if defined(OS_FUCHSIA) void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc index fb91d81f725d4..8c327332baf12 100644 --- a/flow/layers/clip_path_layer_unittests.cc +++ b/flow/layers/clip_path_layer_unittests.cc @@ -192,5 +192,65 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +static bool ReadbackResult(PrerollContext* context, + Clip clip_behavior, + std::shared_ptr child, + bool before) { + const SkMatrix initial_matrix = SkMatrix(); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath layer_path = SkPath().addRect(layer_bounds); + auto layer = std::make_shared(layer_path, clip_behavior); + if (child != nullptr) { + layer->Add(child); + } + context->surface_needs_readback = before; + layer->Preroll(context, initial_matrix); + return context->surface_needs_readback; +} + +TEST_F(ClipPathLayerTest, Readback) { + PrerollContext* context = preroll_context(); + SkPath path; + SkPaint paint; + + const Clip hard = Clip::hardEdge; + const Clip soft = Clip::antiAlias; + const Clip save_layer = Clip::antiAliasWithSaveLayer; + + std::shared_ptr nochild; + auto reader = std::make_shared(path, paint, false, false, true); + auto nonreader = std::make_shared(path, paint); + + // No children, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false)); + + // No children, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true)); + + // Non readback child, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false)); + + // Non readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true)); + + // Readback child, no prior readback -> readback after unless SaveLayer + EXPECT_TRUE(ReadbackResult(context, hard, reader, false)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false)); + + // Readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, reader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true)); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index 9e12839aedcdc..af6c490f27a7d 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -15,6 +15,8 @@ void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect previous_cull_rect = context->cull_rect; children_inside_clip_ = context->cull_rect.intersect(clip_rect_); if (children_inside_clip_) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); context->mutators_stack.PushClipRect(clip_rect_); SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_paint_bounds); @@ -50,11 +52,11 @@ void ClipRectLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->clipRect(clip_rect_, clip_behavior_ != Clip::hardEdge); - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { context.internal_nodes_canvas->saveLayer(clip_rect_, nullptr); } PaintChildren(context); - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { context.internal_nodes_canvas->restore(); } } diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index f503a59336558..adf43915638a9 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -16,6 +16,10 @@ class ClipRectLayer : public ContainerLayer { void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; + bool UsesSaveLayer() const { + return clip_behavior_ == Clip::antiAliasWithSaveLayer; + } + #if defined(OS_FUCHSIA) void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc index 2784f62705b3f..b63ca0c3e9eab 100644 --- a/flow/layers/clip_rect_layer_unittests.cc +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -190,5 +190,64 @@ TEST_F(ClipRectLayerTest, PartiallyContainedChild) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +static bool ReadbackResult(PrerollContext* context, + Clip clip_behavior, + std::shared_ptr child, + bool before) { + const SkMatrix initial_matrix = SkMatrix(); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + auto layer = std::make_shared(layer_bounds, clip_behavior); + if (child != nullptr) { + layer->Add(child); + } + context->surface_needs_readback = before; + layer->Preroll(context, initial_matrix); + return context->surface_needs_readback; +} + +TEST_F(ClipRectLayerTest, Readback) { + PrerollContext* context = preroll_context(); + SkPath path; + SkPaint paint; + + const Clip hard = Clip::hardEdge; + const Clip soft = Clip::antiAlias; + const Clip save_layer = Clip::antiAliasWithSaveLayer; + + std::shared_ptr nochild; + auto reader = std::make_shared(path, paint, false, false, true); + auto nonreader = std::make_shared(path, paint); + + // No children, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false)); + + // No children, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true)); + + // Non readback child, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false)); + + // Non readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true)); + + // Readback child, no prior readback -> readback after unless SaveLayer + EXPECT_TRUE(ReadbackResult(context, hard, reader, false)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false)); + + // Readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, reader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true)); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 4a22151d660d3..d640b8b47171f 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -16,6 +16,8 @@ void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect clip_rrect_bounds = clip_rrect_.getBounds(); children_inside_clip_ = context->cull_rect.intersect(clip_rrect_bounds); if (children_inside_clip_) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); context->mutators_stack.PushClipRRect(clip_rrect_); SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_paint_bounds); @@ -51,11 +53,11 @@ void ClipRRectLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->clipRRect(clip_rrect_, clip_behavior_ != Clip::hardEdge); - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); } PaintChildren(context); - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { context.internal_nodes_canvas->restore(); } } diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index 45daf48ba6e36..c1c83ddbcc60b 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -17,6 +17,10 @@ class ClipRRectLayer : public ContainerLayer { void Paint(PaintContext& context) const override; + bool UsesSaveLayer() const { + return clip_behavior_ == Clip::antiAliasWithSaveLayer; + } + #if defined(OS_FUCHSIA) void UpdateScene(SceneUpdateContext& context) override; #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc index 5c9ed21a3a33f..57db2140ca175 100644 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -195,5 +195,65 @@ TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +static bool ReadbackResult(PrerollContext* context, + Clip clip_behavior, + std::shared_ptr child, + bool before) { + const SkMatrix initial_matrix = SkMatrix(); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + auto layer = std::make_shared(layer_rrect, clip_behavior); + if (child != nullptr) { + layer->Add(child); + } + context->surface_needs_readback = before; + layer->Preroll(context, initial_matrix); + return context->surface_needs_readback; +} + +TEST_F(ClipRRectLayerTest, Readback) { + PrerollContext* context = preroll_context(); + SkPath path; + SkPaint paint; + + const Clip hard = Clip::hardEdge; + const Clip soft = Clip::antiAlias; + const Clip save_layer = Clip::antiAliasWithSaveLayer; + + std::shared_ptr nochild; + auto reader = std::make_shared(path, paint, false, false, true); + auto nonreader = std::make_shared(path, paint); + + // No children, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false)); + + // No children, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true)); + + // Non readback child, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false)); + + // Non readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true)); + + // Readback child, no prior readback -> readback after unless SaveLayer + EXPECT_TRUE(ReadbackResult(context, hard, reader, false)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false)); + + // Readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, reader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true)); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index 8e5ea19ac8484..b212720da581d 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -9,6 +9,13 @@ namespace flutter { ColorFilterLayer::ColorFilterLayer(sk_sp filter) : filter_(std::move(filter)) {} +void ColorFilterLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context); + ContainerLayer::Preroll(context, matrix); +} + void ColorFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ColorFilterLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index 628c5b17f6e1c..989158f704eaf 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -15,6 +15,8 @@ class ColorFilterLayer : public ContainerLayer { public: ColorFilterLayer(sk_sp filter); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Paint(PaintContext& context) const override; private: diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc index 204a02357ad98..42a65f255cd1e 100644 --- a/flow/layers/color_filter_layer_unittests.cc +++ b/flow/layers/color_filter_layer_unittests.cc @@ -195,5 +195,24 @@ TEST_F(ColorFilterLayerTest, Nested) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +TEST_F(ColorFilterLayerTest, Readback) { + auto layer_filter = SkColorFilters::LinearToSRGBGamma(); + auto initial_transform = SkMatrix(); + + // ColorFilterLayer does not read from surface + auto layer = std::make_shared(layer_filter); + preroll_context()->surface_needs_readback = false; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); + + // ColorFilterLayer blocks child with readback + auto mock_layer = + std::make_shared(SkPath(), SkPaint(), false, false, true); + layer->Add(mock_layer); + preroll_context()->surface_needs_readback = false; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index 5ad6a6bee1499..d4bd809d5f2cf 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -27,6 +27,34 @@ uint64_t Layer::NextUniqueID() { void Layer::Preroll(PrerollContext* context, const SkMatrix& matrix) {} +Layer::AutoPrerollSaveLayerState::AutoPrerollSaveLayerState( + PrerollContext* preroll_context, + bool save_layer_is_active, + bool layer_itself_performs_readback) + : preroll_context_(preroll_context), + save_layer_is_active_(save_layer_is_active), + layer_itself_performs_readback_(layer_itself_performs_readback) { + if (save_layer_is_active_) { + prev_surface_needs_readback_ = preroll_context_->surface_needs_readback; + preroll_context_->surface_needs_readback = false; + } +} + +Layer::AutoPrerollSaveLayerState Layer::AutoPrerollSaveLayerState::Create( + PrerollContext* preroll_context, + bool save_layer_is_active, + bool layer_itself_performs_readback) { + return Layer::AutoPrerollSaveLayerState(preroll_context, save_layer_is_active, + layer_itself_performs_readback); +} + +Layer::AutoPrerollSaveLayerState::~AutoPrerollSaveLayerState() { + if (save_layer_is_active_) { + preroll_context_->surface_needs_readback = + (prev_surface_needs_readback_ || layer_itself_performs_readback_); + } +} + #if defined(OS_FUCHSIA) void Layer::UpdateScene(SceneUpdateContext& context) {} #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 731509876b611..63c7cdb316d7e 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -49,6 +49,7 @@ struct PrerollContext { MutatorsStack& mutators_stack; SkColorSpace* dst_color_space; SkRect cull_rect; + bool surface_needs_readback; // These allow us to paint in the end of subtree Preroll. const Stopwatch& raster_time; @@ -76,6 +77,32 @@ class Layer { virtual void Preroll(PrerollContext* context, const SkMatrix& matrix); + // Used during Preroll by layers that employ a saveLayer to manage the + // PrerollContext settings with values affected by the saveLayer mechanism. + // This object must be created before calling Preroll on the children to + // set up the state for the children and then restore the state upon + // destruction. + class AutoPrerollSaveLayerState { + public: + FML_WARN_UNUSED_RESULT static AutoPrerollSaveLayerState Create( + PrerollContext* preroll_context, + bool save_layer_is_active = true, + bool layer_itself_performs_readback = false); + + ~AutoPrerollSaveLayerState(); + + private: + AutoPrerollSaveLayerState(PrerollContext* preroll_context, + bool save_layer_is_active, + bool layer_itself_performs_readback); + + PrerollContext* preroll_context_; + bool save_layer_is_active_; + bool layer_itself_performs_readback_; + + bool prev_surface_needs_readback_; + }; + struct PaintContext { // When splitting the scene into multiple canvases (e.g when embedding // a platform view on iOS) during the paint traversal we apply the non leaf diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index f3fb164136913..dd9e6b4f02b01 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -26,13 +26,13 @@ void LayerTree::RecordBuildTime(fml::TimePoint start) { build_finish_ = fml::TimePoint::Now(); } -void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, +bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) { TRACE_EVENT0("flutter", "LayerTree::Preroll"); if (!root_layer_) { FML_LOG(ERROR) << "The scene did not specify any layers."; - return; + return false; } SkColorSpace* color_space = @@ -47,6 +47,7 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, stack, color_space, kGiantRect, + false, frame.context().raster_time(), frame.context().ui_time(), frame.context().texture_registry(), @@ -55,6 +56,7 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, frame_device_pixel_ratio_}; root_layer_->Preroll(&context, frame.root_surface_transformation()); + return context.surface_needs_readback; } #if defined(OS_FUCHSIA) @@ -152,6 +154,7 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { unused_stack, // mutator stack nullptr, // SkColorSpace* dst_color_space kGiantRect, // SkRect cull_rect + false, // layer reads from surface unused_stopwatch, // frame time (dont care) unused_stopwatch, // engine time (dont care) unused_texture_registry, // texture registry (not supported) diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 10a10d6711ab1..21ba509f09ae6 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -23,7 +23,14 @@ class LayerTree { float frame_physical_depth, float frame_device_pixel_ratio); - void Preroll(CompositorContext::ScopedFrame& frame, + // Perform a preroll pass on the tree and return information about + // the tree that affects rendering this frame. + // + // Returns: + // - a boolean indicating whether or not the top level of the + // layer tree performs any operations that require readback + // from the root surface. + bool Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false); #if defined(OS_FUCHSIA) diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 7d4922ff3be48..9851cebf26d0e 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -26,6 +26,7 @@ class LayerTreeTest : public CanvasTest { nullptr, root_transform_, false, + true, nullptr)) {} LayerTree& layer_tree() { return layer_tree_; } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 5592f40f72f8c..5a5e2d05c6165 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -58,6 +58,8 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { child_matrix.postTranslate(offset_.fX, offset_.fY); context->mutators_stack.PushTransform( SkMatrix::MakeTrans(offset_.fX, offset_.fY)); + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context); OpacityLayerBase::Preroll(context, child_matrix); context->mutators_stack.Pop(); diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 8410d7767c890..f7cc77ffe2ddd 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -308,5 +308,24 @@ TEST_F(OpacityLayerTest, Nested) { EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } +TEST_F(OpacityLayerTest, Readback) { + auto initial_transform = SkMatrix(); + auto layer = std::make_shared(kOpaque_SkAlphaType, SkPoint()); + layer->Add(std::make_shared(SkPath())); + + // OpacityLayer does not read from surface + preroll_context()->surface_needs_readback = false; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); + + // OpacityLayer blocks child with readback + auto mock_layer = + std::make_shared(SkPath(), SkPaint(), false, false, true); + layer->Add(mock_layer); + preroll_context()->surface_needs_readback = false; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index fed7a5fbe7163..017224a08d8d2 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -49,6 +49,8 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); PhysicalShapeLayerBase::Preroll(context, matrix); if (elevation() == 0) { @@ -100,7 +102,7 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { break; } - if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + if (UsesSaveLayer()) { // If we want to avoid the bleeding edge artifact // (https://github.com/flutter/flutter/issues/18057#issue-328003931) // using saveLayer, we have to call drawPaint instead of drawPath as diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index ac54a7bbc6761..3d3a2a74b40b5 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -46,6 +46,10 @@ class PhysicalShapeLayer : public PhysicalShapeLayerBase { void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; + bool UsesSaveLayer() const { + return clip_behavior_ == Clip::antiAliasWithSaveLayer; + } + private: SkColor shadow_color_; SkPath path_; diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index 36107560fa4ed..93c860ba6ac2d 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -202,5 +202,68 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); } +static bool ReadbackResult(PrerollContext* context, + Clip clip_behavior, + std::shared_ptr child, + bool before) { + const SkMatrix initial_matrix = SkMatrix(); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath layer_path = SkPath().addRect(layer_bounds); + auto layer = + std::make_shared(SK_ColorGREEN, SK_ColorBLACK, + 0.0f, // elevation + layer_path, clip_behavior); + if (child != nullptr) { + layer->Add(child); + } + context->surface_needs_readback = before; + layer->Preroll(context, initial_matrix); + return context->surface_needs_readback; +} + +TEST_F(PhysicalShapeLayerTest, Readback) { + PrerollContext* context = preroll_context(); + SkPath path; + SkPaint paint; + + const Clip hard = Clip::hardEdge; + const Clip soft = Clip::antiAlias; + const Clip save_layer = Clip::antiAliasWithSaveLayer; + + std::shared_ptr nochild; + auto reader = std::make_shared(path, paint, false, false, true); + auto nonreader = std::make_shared(path, paint); + + // No children, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nochild, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false)); + + // No children, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nochild, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true)); + + // Non readback child, no prior readback -> no readback after + EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false)); + + // Non readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true)); + + // Readback child, no prior readback -> readback after unless SaveLayer + EXPECT_TRUE(ReadbackResult(context, hard, reader, false)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, false)); + EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false)); + + // Readback child, prior readback -> readback after + EXPECT_TRUE(ReadbackResult(context, hard, reader, true)); + EXPECT_TRUE(ReadbackResult(context, soft, reader, true)); + EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true)); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index 8e681ec725458..157354f7314b4 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -11,6 +11,12 @@ ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, SkBlendMode blend_mode) : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {} +void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context); + ContainerLayer::Preroll(context, matrix); +} + void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); FML_DCHECK(needs_painting()); diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index 7f633c0372d45..03b90e40d0eff 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -17,6 +17,8 @@ class ShaderMaskLayer : public ContainerLayer { const SkRect& mask_rect, SkBlendMode blend_mode); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + void Paint(PaintContext& context) const override; private: diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc index d8997c3e65b2f..895c6a9360c20 100644 --- a/flow/layers/shader_mask_layer_unittests.cc +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -253,5 +253,27 @@ TEST_F(ShaderMaskLayerTest, Nested) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +TEST_F(ShaderMaskLayerTest, Readback) { + auto initial_transform = SkMatrix(); + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); + auto layer_filter = + SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + auto layer = std::make_shared(layer_filter, layer_bounds, + SkBlendMode::kSrc); + + // ShaderMaskLayer does not read from surface + preroll_context()->surface_needs_readback = false; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); + + // ShaderMaskLayer blocks child with readback + auto mock_layer = + std::make_shared(SkPath(), SkPaint(), false, false, true); + layer->Add(mock_layer); + preroll_context()->surface_needs_readback = false; + layer->Preroll(preroll_context(), initial_transform); + EXPECT_FALSE(preroll_context()->surface_needs_readback); +} + } // namespace testing } // namespace flutter diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index ab51af38af563..593dec1836823 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -37,6 +37,7 @@ class LayerTestBase : public CanvasTestBase { nullptr, /* external_view_embedder */ mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), kGiantRect, /* cull_rect */ + false, /* layer reads from surface */ raster_time_, ui_time_, texture_registry_, false, /* checkerboard_offscreen_layers */ 100.0f, /* frame_physical_depth */ diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc index 1065f43054674..5fe1b98088af1 100644 --- a/flow/testing/mock_layer.cc +++ b/flow/testing/mock_layer.cc @@ -10,11 +10,13 @@ namespace testing { MockLayer::MockLayer(SkPath path, SkPaint paint, bool fake_has_platform_view, - bool fake_needs_system_composite) + bool fake_needs_system_composite, + bool fake_reads_surface) : fake_paint_path_(path), fake_paint_(paint), fake_has_platform_view_(fake_has_platform_view), - fake_needs_system_composite_(fake_needs_system_composite) {} + fake_needs_system_composite_(fake_needs_system_composite), + fake_reads_surface_(fake_reads_surface) {} void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { parent_mutators_ = context->mutators_stack; @@ -26,6 +28,9 @@ void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->has_platform_view = fake_has_platform_view_; set_paint_bounds(fake_paint_path_.getBounds()); set_needs_system_composite(fake_needs_system_composite_); + if (fake_reads_surface_) { + context->surface_needs_readback = true; + } } void MockLayer::Paint(PaintContext& context) const { diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h index b55452a37812c..835c3ee9621cc 100644 --- a/flow/testing/mock_layer.h +++ b/flow/testing/mock_layer.h @@ -19,7 +19,8 @@ class MockLayer : public Layer { MockLayer(SkPath path, SkPaint paint = SkPaint(), bool fake_has_platform_view = false, - bool fake_needs_system_composite = false); + bool fake_needs_system_composite = false, + bool fake_reads_surface = false); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; @@ -40,6 +41,7 @@ class MockLayer : public Layer { bool parent_has_platform_view_ = false; bool fake_has_platform_view_ = false; bool fake_needs_system_composite_ = false; + bool fake_reads_surface_ = false; FML_DISALLOW_COPY_AND_ASSIGN(MockLayer); }; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 11fdd3f6ee6ad..7d6a7a74e8a91 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -333,6 +333,7 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { external_view_embedder, // external view embedder root_surface_transformation, // root surface transformation true, // instrumentation enabled + frame->supports_readback(), // surface supports pixel reads gpu_thread_merger_ // thread merger ); @@ -377,7 +378,7 @@ static sk_sp ScreenshotLayerTreeAsPicture( // https://github.com/flutter/flutter/issues/23435 auto frame = compositor_context.AcquireFrame( nullptr, recorder.getRecordingCanvas(), nullptr, - root_surface_transformation, false, nullptr); + root_surface_transformation, false, true, nullptr); frame->Raster(*tree, true); @@ -434,7 +435,7 @@ static sk_sp ScreenshotLayerTreeAsImage( // surface if we don't call the base method. auto frame = compositor_context.flutter::CompositorContext::AcquireFrame( surface_context, canvas, nullptr, root_surface_transformation, false, - nullptr); + true, nullptr); canvas->clear(SK_ColorTRANSPARENT); frame->Raster(*tree, true); canvas->flush(); diff --git a/shell/common/surface.cc b/shell/common/surface.cc index af889885017fc..6a4f992e9137e 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -10,8 +10,12 @@ namespace flutter { SurfaceFrame::SurfaceFrame(sk_sp surface, + bool supports_readback, const SubmitCallback& submit_callback) - : submitted_(false), surface_(surface), submit_callback_(submit_callback) { + : submitted_(false), + surface_(surface), + supports_readback_(supports_readback), + submit_callback_(submit_callback) { FML_DCHECK(submit_callback_); } diff --git a/shell/common/surface.h b/shell/common/surface.h index 14f898fad3fe1..432f6be5e564b 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -21,7 +21,9 @@ class SurfaceFrame { using SubmitCallback = std::function; - SurfaceFrame(sk_sp surface, const SubmitCallback& submit_callback); + SurfaceFrame(sk_sp surface, + bool supports_readback, + const SubmitCallback& submit_callback); ~SurfaceFrame(); @@ -31,9 +33,12 @@ class SurfaceFrame { sk_sp SkiaSurface() const; + bool supports_readback() { return supports_readback_; } + private: bool submitted_; sk_sp surface_; + bool supports_readback_; SubmitCallback submit_callback_; bool PerformSubmit(); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5f30a48375d33..e5fbf00a24744 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -180,19 +180,6 @@ static sk_sp WrapOnscreenSurface(GrContext* context, ); } -static sk_sp CreateOffscreenSurface(GrContext* context, - const SkISize& size) { - const SkImageInfo image_info = SkImageInfo::MakeN32( - size.fWidth, size.fHeight, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB()); - - const SkSurfaceProps surface_props( - SkSurfaceProps::InitType::kLegacyFontHost_InitType); - - return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info, 0, - kBottomLeft_GrSurfaceOrigin, - &surface_props); -} - bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { if (onscreen_surface_ != nullptr && size == SkISize::Make(onscreen_surface_->width(), @@ -206,14 +193,13 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { // Either way, we need to get rid of previous surface. onscreen_surface_ = nullptr; - offscreen_surface_ = nullptr; if (size.isEmpty()) { FML_LOG(ERROR) << "Cannot create surfaces of empty size."; return false; } - sk_sp onscreen_surface, offscreen_surface; + sk_sp onscreen_surface; onscreen_surface = WrapOnscreenSurface(context_.get(), // GL context @@ -228,16 +214,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { return false; } - if (delegate_->UseOffscreenSurface()) { - offscreen_surface = CreateOffscreenSurface(context_.get(), size); - if (offscreen_surface == nullptr) { - FML_LOG(ERROR) << "Could not create offscreen surface."; - return false; - } - } - onscreen_surface_ = std::move(onscreen_surface); - offscreen_surface_ = std::move(offscreen_surface); return true; } @@ -263,7 +240,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { // external view embedder may want to render to the root surface. if (!render_to_surface_) { return std::make_unique( - nullptr, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { + nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { return true; }); } @@ -285,7 +262,8 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - return std::make_unique(surface, submit_callback); + return std::make_unique( + surface, delegate_->SurfaceSupportsReadback(), submit_callback); } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -293,15 +271,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } - if (offscreen_surface_ != nullptr) { - TRACE_EVENT0("flutter", "CopyTextureOnscreen"); - SkPaint paint; - SkCanvas* onscreen_canvas = onscreen_surface_->getCanvas(); - onscreen_canvas->clear(SK_ColorTRANSPARENT); - onscreen_canvas->drawImage(offscreen_surface_->makeImageSnapshot(), 0, 0, - &paint); - } - { TRACE_EVENT0("flutter", "SkCanvas::Flush"); onscreen_surface_->getCanvas()->flush(); @@ -346,7 +315,7 @@ sk_sp GPUSurfaceGL::AcquireRenderSurface( return nullptr; } - return offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_; + return onscreen_surface_; } // |Surface| diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 97325569bfd16..d2e62d7e1fefd 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -50,7 +50,6 @@ class GPUSurfaceGL : public Surface { GPUSurfaceGLDelegate* delegate_; sk_sp context_; sk_sp onscreen_surface_; - sk_sp offscreen_surface_; bool context_owner_; // TODO(38466): Refactor GPU surface APIs take into account the fact that an // external view embedder may want to render to the root surface. This is a diff --git a/shell/gpu/gpu_surface_gl_delegate.cc b/shell/gpu/gpu_surface_gl_delegate.cc index 1ef969fe8acc5..df7ad4ef07323 100644 --- a/shell/gpu/gpu_surface_gl_delegate.cc +++ b/shell/gpu/gpu_surface_gl_delegate.cc @@ -12,8 +12,8 @@ bool GPUSurfaceGLDelegate::GLContextFBOResetAfterPresent() const { return false; } -bool GPUSurfaceGLDelegate::UseOffscreenSurface() const { - return false; +bool GPUSurfaceGLDelegate::SurfaceSupportsReadback() const { + return true; } SkMatrix GPUSurfaceGLDelegate::GLContextSurfaceTransformation() const { diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dfe0ce7f468db..bf51d15c8b79b 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -36,8 +36,9 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // subsequent frames. virtual bool GLContextFBOResetAfterPresent() const; - // Create an offscreen surface to render into before onscreen composition. - virtual bool UseOffscreenSurface() const; + // Indicates whether or not the surface supports pixel readback as used in + // circumstances such as a BackdropFilter. + virtual bool SurfaceSupportsReadback() const; // A transformation applied to the onscreen surface before the canvas is // flushed. diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 81abf740d48f6..bb436d2138768 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -152,7 +152,7 @@ return true; }; - return std::make_unique(std::move(surface), submit_callback); + return std::make_unique(std::move(surface), true, submit_callback); } // |Surface| diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index 346857ac47e6a..8b2f664d18648 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -29,7 +29,7 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( // external view embedder may want to render to the root surface. if (!render_to_surface_) { return std::make_unique( - nullptr, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { + nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { return true; }); } @@ -69,7 +69,7 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( return self->delegate_->PresentBackingStore(surface_frame.SkiaSurface()); }; - return std::make_unique(backing_store, on_submit); + return std::make_unique(backing_store, true, on_submit); } // |Surface| diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index fbc9696b15d52..f7d07f580a523 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -40,7 +40,7 @@ std::unique_ptr GPUSurfaceVulkan::AcquireFrame( } return weak_this->window_.SwapBuffers(); }; - return std::make_unique(std::move(surface), + return std::make_unique(std::move(surface), true, std::move(callback)); } diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index c1019bb442bb0..906f3be92b2d8 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -48,7 +48,7 @@ class IOSSurfaceGL final : public IOSSurface, intptr_t GLContextFBO() const override; - bool UseOffscreenSurface() const override; + bool SurfaceSupportsReadback() const override; // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 48e70e00a4e7a..4f1da5a593a02 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -49,11 +49,13 @@ return IsValid() ? render_target_->framebuffer() : GL_NONE; } -bool IOSSurfaceGL::UseOffscreenSurface() const { - // The onscreen surface wraps a GL renderbuffer, which is extremely slow to read. - // Certain filter effects require making a copy of the current destination, so we - // always render to an offscreen surface, which will be much quicker to read/copy. - return true; +bool IOSSurfaceGL::SurfaceSupportsReadback() const { + // The onscreen surface wraps a GL renderbuffer, which is extremely slow to read on iOS. + // Certain filter effects, in particular BackdropFilter, require making a copy of + // the current destination. For performance, the iOS surface will specify that it + // does not support readback so that the engine compositor can implement a workaround + // such as rendering the scene to an offscreen surface or Skia saveLayer. + return false; } bool IOSSurfaceGL::GLContextMakeCurrent() { diff --git a/shell/platform/fuchsia/flutter/compositor_context.cc b/shell/platform/fuchsia/flutter/compositor_context.cc index 624b237c7a9d8..e47720998138e 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.cc +++ b/shell/platform/fuchsia/flutter/compositor_context.cc @@ -20,6 +20,7 @@ class ScopedFrame final : public flutter::CompositorContext::ScopedFrame { nullptr, root_surface_transformation, instrumentation_enabled, + true, nullptr), session_connection_(session_connection) {} @@ -96,6 +97,7 @@ CompositorContext::AcquireFrame( flutter::ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled, + bool surface_supports_readback, fml::RefPtr gpu_thread_merger) { // TODO: The AcquireFrame interface is too broad and must be refactored to get // rid of the context and canvas arguments as those seem to be only used for diff --git a/shell/platform/fuchsia/flutter/compositor_context.h b/shell/platform/fuchsia/flutter/compositor_context.h index c5e54df952796..ce7d16d47c411 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.h +++ b/shell/platform/fuchsia/flutter/compositor_context.h @@ -45,6 +45,7 @@ class CompositorContext final : public flutter::CompositorContext { flutter::ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled, + bool surface_supports_readback, fml::RefPtr gpu_thread_merger) override; FML_DISALLOW_COPY_AND_ASSIGN(CompositorContext); diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index 29fbbc7294cc1..1dce37fdcebe5 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -26,8 +26,10 @@ bool Surface::IsValid() { std::unique_ptr Surface::AcquireFrame( const SkISize& size) { return std::make_unique( - nullptr, [](const flutter::SurfaceFrame& surface_frame, - SkCanvas* canvas) { return true; }); + nullptr, true, + [](const flutter::SurfaceFrame& surface_frame, SkCanvas* canvas) { + return true; + }); } // |flutter::Surface| From 0a40f3d784f7e671b5b5b26d019654eddad7fc7a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 11 Dec 2019 15:17:34 -0800 Subject: [PATCH 393/591] Assert that arc end caps on canvases with root surface transformations are drawn correctly. (#14359) Verifies that the Skia commit https://skia-review.googlesource.com/c/skia/+/259174 has been pulled into the engine. This should have happened in the roll https://github.com/flutter/engine/pull/14345. Fixes https://github.com/flutter/flutter/issues/46691 Fixes b/142280381 --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/embedder/BUILD.gn | 1 + .../embedder/fixtures/arc_end_caps.png | Bin 0 -> 6006 bytes shell/platform/embedder/fixtures/main.dart | 27 ++++++++++++ .../embedder/tests/embedder_unittests.cc | 41 ++++++++++++++++++ 5 files changed, 70 insertions(+) create mode 100644 shell/platform/embedder/fixtures/arc_end_caps.png diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 681868ba215f5..9a2ccfceda885 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -908,6 +908,7 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.cc FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.h FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.cc FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.h +FILE: ../../../flutter/shell/platform/embedder/fixtures/arc_end_caps.png FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor.png FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_root_surface_xformation.png FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_software.png diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index abb7fe16c4d9f..539b4d48cc045 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -88,6 +88,7 @@ config("embedder_prefix_config") { test_fixtures("fixtures") { dart_main = "fixtures/main.dart" fixtures = [ + "fixtures/arc_end_caps.png", "fixtures/compositor.png", "fixtures/compositor_root_surface_xformation.png", "fixtures/compositor_software.png", diff --git a/shell/platform/embedder/fixtures/arc_end_caps.png b/shell/platform/embedder/fixtures/arc_end_caps.png new file mode 100644 index 0000000000000000000000000000000000000000..149eea084895d2d90a42fa2d6e1af5313966a4cd GIT binary patch literal 6006 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 literal 0 HcmV?d00001 diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index d7dd20d7295ee..b2ac3928b105c 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -620,3 +620,30 @@ void scene_with_no_container() { }; window.scheduleFrame(); } + +Picture CreateArcEndCapsPicture() { + PictureRecorder baseRecorder = PictureRecorder(); + Canvas canvas = Canvas(baseRecorder); + + var style = Paint() + ..strokeWidth = 12.0 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.miter; + + style.color = Color.fromARGB(255, 255, 0, 0); + canvas.drawArc(Rect.fromLTRB(0.0, 0.0, 500.0, 500.0), 1.57, 1.0, false, style); + + return baseRecorder.endRecording(); + +} + +@pragma('vm:entry-point') +void arc_end_caps_correct() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.addPicture(Offset(0.0, 0.0), CreateArcEndCapsPicture()); + window.render(builder.build()); + }; + window.scheduleFrame(); +} diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 3631dee2563ab..f899d3f7b39e0 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -3550,5 +3550,46 @@ TEST_F(EmbedderTest, SceneWithNoRootContainerIsAcceptable) { latch.Wait(); } +// Verifies that https://skia-review.googlesource.com/c/skia/+/259174 is pulled +// into the engine. +TEST_F(EmbedderTest, ArcEndCapsAreDrawnCorrectly) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 1024)); + builder.SetCompositor(); + builder.SetDartEntrypoint("arc_end_caps_correct"); + + const auto root_surface_transformation = SkMatrix() + .preScale(1.0, -1.0) + .preTranslate(1024.0, -800.0) + .preRotate(90.0); + + context.SetRootSurfaceTransformation(root_surface_transformation); + + auto engine = builder.LaunchEngine(); + + fml::AutoResetWaitableEvent latch; + sk_sp scene_image; + context.SetNextSceneCallback([&](sk_sp scene) { + scene_image = std::move(scene); + latch.Signal(); + }); + + ASSERT_TRUE(engine.is_valid()); + + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 1024; + event.height = 800; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + + latch.Wait(); + + ASSERT_TRUE(ImageMatchesFixture("arc_end_caps.png", scene_image)); +} + } // namespace testing } // namespace flutter From d698d96ceae866480dd048c7fc221bba991e4091 Mon Sep 17 00:00:00 2001 From: Gityuan Date: Thu, 12 Dec 2019 07:40:23 +0800 Subject: [PATCH 394/591] Fix missing timeline event of flutter engine's startup time (#14319) Fixes https://github.com/flutter/flutter/issues/46744 --- runtime/dart_vm.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index 652bf2804aca5..f1eb3b0c7afb5 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -417,7 +417,7 @@ DartVM::DartVM(std::shared_ptr vm_data, if (engine_main_enter_ts != 0) { Dart_TimelineEvent("FlutterEngineMainEnter", // label engine_main_enter_ts, // timestamp0 - engine_main_enter_ts, // timestamp1_or_async_id + Dart_TimelineGetMicros(), // timestamp1_or_async_id Dart_Timeline_Event_Duration, // event type 0, // argument_count nullptr, // argument_names From 9dc23b8be83d7f17e0702953481d7222f780a22e Mon Sep 17 00:00:00 2001 From: Gityuan Date: Thu, 12 Dec 2019 07:51:47 +0800 Subject: [PATCH 395/591] Fix missing API stream when record event in systrace (#14323) --- runtime/dart_vm.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index f1eb3b0c7afb5..234e06f87eff7 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -92,7 +92,7 @@ static const char* kDartDisableServiceAuthCodesArgs[]{ }; static const char* kDartTraceStartupArgs[]{ - "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", + "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM,API", }; static const char* kDartEndlessTraceBufferArgs[]{ @@ -108,7 +108,7 @@ static const char* kDartFuchsiaTraceArgs[] FML_ALLOW_UNUSED_TYPE = { }; static const char* kDartTraceStreamsArgs[] = { - "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", + "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM,API", }; constexpr char kFileUriPrefix[] = "file://"; From 9e4c6ad40c9f610389980edba7b7ccee7850f809 Mon Sep 17 00:00:00 2001 From: Wu Zhong Date: Thu, 12 Dec 2019 07:52:29 +0800 Subject: [PATCH 396/591] Fix CGMutablePathRef memory leaks when the path is invalid. (#14275) --- .../ios/framework/Source/FlutterPlatformViews_Internal.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index dc91dde315463..c29c7491641d1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -4,6 +4,7 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" static int kMaxPointsInVerb = 4; @@ -132,15 +133,14 @@ - (void)clipRRect:(const SkRRect&)clipSkRRect { } - (void)clipPath:(const SkPath&)path { - CGMutablePathRef pathRef = CGPathCreateMutable(); if (!path.isValid()) { return; } + fml::CFRef pathRef(CGPathCreateMutable()); if (path.isEmpty()) { CAShapeLayer* clip = [[CAShapeLayer alloc] init]; clip.path = pathRef; self.layer.mask = clip; - CGPathRelease(pathRef); return; } @@ -198,7 +198,6 @@ - (void)clipPath:(const SkPath&)path { CAShapeLayer* clip = [[CAShapeLayer alloc] init]; clip.path = pathRef; self.layer.mask = clip; - CGPathRelease(pathRef); } - (void)setClip:(flutter::MutatorType)type From fc8cafb3c633664a93498537ba119716a02e4c64 Mon Sep 17 00:00:00 2001 From: xster Date: Wed, 11 Dec 2019 16:43:22 -0800 Subject: [PATCH 397/591] objcdoc fix for some ambiguity (#14367) --- .../darwin/ios/framework/Headers/FlutterEngine.h | 6 +++--- .../ios/framework/Headers/FlutterViewController.h | 14 +++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index 344c2b037fc3f..c5fd42b4c8a82 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -122,7 +122,7 @@ FLUTTER_EXPORT * contains `main()`), using `main()` as the entrypoint (the default for Flutter projects). * * The first call to this method will create a new Isolate. Subsequent calls will return - * immediately. + * immediately and have no effect. * * @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise. */ @@ -133,7 +133,7 @@ FLUTTER_EXPORT * contains `main()`). * * The first call to this method will create a new Isolate. Subsequent calls will return - * immediately. + * immediately and have no effect. * * @param entrypoint The name of a top-level function from the same Dart * library that contains the app's main() function. If this is FlutterDefaultDartEntrypoint (or @@ -149,7 +149,7 @@ FLUTTER_EXPORT * which may not be the same as the library containing the Dart program's `main()` function. * * The first call to this method will create a new Isolate. Subsequent calls will return - * immediately. + * immediately and have no effect. * * @param entrypoint The name of a top-level function from a Dart library. If this is * FlutterDefaultDartEntrypoint (or nil); this will default to `main()`. If it is not the app's diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 7b77a7cc5b8b1..b0f7df28614e0 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -105,11 +105,15 @@ FLUTTER_EXPORT - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package; /** - * Sets the first route that the Flutter app shows. The default is "/". - * This method will guarnatee that the initial route is delivered, even if the - * Flutter window hasn't been created yet when called. It cannot be used to update - * the current route being shown in a visible FlutterViewController (see pushRoute - * and popRoute). + * Attempts to set the first route that the Flutter app shows if the Flutter + * runtime hasn't yet started. The default is "/". + * + * This method must be called immediately after `initWithProject` and has no + * effect when using `initWithEngine` if the `FlutterEngine` has already been + * run. + * + * Setting this after the Flutter started running has no effect. See `pushRoute` + * and `popRoute` to change the route after Flutter started running. * * @param route The name of the first route to show. */ From 9bafb3cd3c74b77067501b500605cab55847b488 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 11 Dec 2019 17:23:49 -0800 Subject: [PATCH 398/591] [tests] Use distinct begin and end times (#14361) --- shell/common/shell_test.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 3a0d3e150a207..3066fa0f1c015 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #define FML_USED_ON_EMBEDDER #include "flutter/shell/common/shell_test.h" @@ -159,8 +158,10 @@ void ShellTest::PumpOneFrame(Shell* shell, shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, engine = shell->weak_engine_, viewport_metrics]() { engine->SetViewportMetrics(std::move(viewport_metrics)); - engine->animator_->BeginFrame(fml::TimePoint::Now(), - fml::TimePoint::Now()); + const auto frame_begin_time = fml::TimePoint::Now(); + const auto frame_end_time = + frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0); + engine->animator_->BeginFrame(frame_begin_time, frame_end_time); latch.Signal(); }); latch.Wait(); From 897ce34bc6763bb854168d0a0ac3a9d068024ee0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 11 Dec 2019 23:39:22 -0500 Subject: [PATCH 399/591] Roll src/third_party/skia 3517aa7b14ad..826484f2631f (18 commits) (#14375) https://skia.googlesource.com/skia.git/+log/3517aa7b14ad..826484f2631f git log 3517aa7b14ad..826484f2631f --date=short --first-parent --format='%ad %ae %s' 2019-12-12 egdaniel@google.com Always report SkImage_Gpu subclasses as being texture backed. 2019-12-12 egdaniel@google.com Revert "When converting runtime SkSL to FP SkSL, use default settings" 2019-12-12 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-12-11 brianosman@google.com When converting runtime SkSL to FP SkSL, use default settings 2019-12-11 egdaniel@google.com Update FragmentProcessor TextureSampler to hold an GrSurfaceProxyView. 2019-12-11 jvanverth@google.com Change PerspectiveClip to clip directly to half plane. 2019-12-11 jvanverth@google.com Adjust cache key for Metal persistent cache. 2019-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms ef3043bd8110..64374756e037 (1 commits) 2019-12-11 nifong@google.com Fix audit trail visualization bugs 2019-12-11 fmalita@chromium.org [skottie] Invert effect support 2019-12-11 halcanary@google.com experimental/skottie_ios/SkottieUiView 2019-12-11 csmartdalton@google.com Suppress allocation warnings for tests that induce them intentionally 2019-12-11 mtklein@google.com add SK_CPU_LIMIT_AVX 2019-12-11 halcanary@google.com public.bzl: add metal_objc_srcs() 2019-12-11 fmalita@chromium.org [skottie] Add SkottieSlide frame rate UI option 2019-12-11 halcanary@google.com Roll third_party/externals/libgifcodec ed218be1..38d9c73f (2 commits) 2019-12-11 herb@google.com Remove unused buffer call for SkGlyphRunPaint 2019-12-11 mtklein@google.com roll clang to 9.0 Created with: gclient setdep -r src/third_party/skia@826484f2631f If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 591b1aa5d3335..2c5e2c810f8f0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3517aa7b14ad52aa662bf38932f2ba358a9f8318', + 'skia_revision': '826484f2631ff2249da4779ae60947effebd564a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index d1785a0f4679b..89ef4a056990d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6bffa87a7c5c059414f1658dd2762fdf +Signature: 1e3c9c336a7abcf29e6b685726fa4f0e UNUSED LICENSES: @@ -3101,6 +3101,7 @@ FILE: ../../../third_party/skia/modules/skottie/src/effects/FillEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/GaussianBlurEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/GradientEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/HueSaturationEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/InvertEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/LevelsEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/LinearWipeEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionBlurEffect.cpp @@ -5040,10 +5041,14 @@ FILE: ../../../third_party/skia/docs/examples/Typeface_Methods.cpp FILE: ../../../third_party/skia/docs/examples/Xor.cpp FILE: ../../../third_party/skia/docs/examples/incomplete.cpp FILE: ../../../third_party/skia/experimental/minimal_ios_mtl_skia_app/main.mm +FILE: ../../../third_party/skia/experimental/skottie_ios/SkAnimationDraw.h FILE: ../../../third_party/skia/experimental/skottie_ios/SkMetalViewBridge.h FILE: ../../../third_party/skia/experimental/skottie_ios/SkMetalViewBridge.mm +FILE: ../../../third_party/skia/experimental/skottie_ios/SkTimeKeeper.h FILE: ../../../third_party/skia/experimental/skottie_ios/SkottieMtkView.h FILE: ../../../third_party/skia/experimental/skottie_ios/SkottieMtkView.mm +FILE: ../../../third_party/skia/experimental/skottie_ios/SkottieUIView.h +FILE: ../../../third_party/skia/experimental/skottie_ios/SkottieUIView.mm FILE: ../../../third_party/skia/experimental/skottie_ios/main.mm FILE: ../../../third_party/skia/experimental/xform/SkShape.cpp FILE: ../../../third_party/skia/experimental/xform/SkShape.h From 1ce85bec2f4e8a558ed468f13aa42fb15ed38c9d Mon Sep 17 00:00:00 2001 From: Nathan Rogers Date: Wed, 11 Dec 2019 21:03:12 -0800 Subject: [PATCH 400/591] [flutter_runner] Enable Skia tracing by default on Fuchsia (#13457) Since Flutter tracing is wired up to Fuchsia system level tracing (and that includes Skia tracing within Flutter), it makes more sense to enable Skia tracing by default on Fuchsia, and to control Flutter Skia tracing, rely on whether Fuchsia system tracing is enabled, in progress, and contains the "skia" category. --- shell/platform/fuchsia/flutter/component.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc index 2cbd33586e1d8..e1188f44a99d2 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -316,9 +316,8 @@ Application::Application( settings_.observatory_host = "127.0.0.1"; #endif - // Set this to true to enable category "skia" trace events. - // TODO(PT-145): Explore enabling this by default. - settings_.trace_skia = false; + // Controls whether category "skia" trace events are enabled. + settings_.trace_skia = true; settings_.icu_data_path = ""; From 97634d2efae75fd5f95d5c98090509af18a94960 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 12 Dec 2019 10:51:26 -0500 Subject: [PATCH 401/591] Roll src/third_party/skia 826484f2631f..51b99659ed82 (2 commits) (#14405) https://skia.googlesource.com/skia.git/+log/826484f2631f..51b99659ed82 git log 826484f2631f..51b99659ed82 --date=short --first-parent --format='%ad %ae %s' 2019-12-12 herb@google.com Remove unused fields from GrTextBlobKey 2019-12-12 benjaminwagner@google.com Format public.bzl Created with: gclient setdep -r src/third_party/skia@51b99659ed82 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC djsollen@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: djsollen@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2c5e2c810f8f0..c6a167978c484 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '826484f2631ff2249da4779ae60947effebd564a', + 'skia_revision': '51b99659ed826c5009f9f261a17cf36a61c4702a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 89ef4a056990d..b0e7c52446842 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1e3c9c336a7abcf29e6b685726fa4f0e +Signature: 994037753b8256685e9fb810c0a09a15 UNUSED LICENSES: From a7b6ee58a2a22ecaabf43a07430a927399f70c3e Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Thu, 12 Dec 2019 10:54:31 -0500 Subject: [PATCH 402/591] Smart quote/dash configuration support in iOS (#13863) Support for UITextSmartDashesType and UITextSmartQuotesType in iOS --- .../framework/Source/FlutterTextInputPlugin.mm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index ad39e710eeb50..76d9a8106b5cb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -159,6 +159,8 @@ @interface FlutterTextInputView : UIView @property(nonatomic) UIKeyboardType keyboardType; @property(nonatomic) UIReturnKeyType returnKeyType; @property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; +@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0)); +@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0)); @property(nonatomic, assign) id textInputDelegate; @@ -193,6 +195,10 @@ - (instancetype)init { _keyboardType = UIKeyboardTypeDefault; _returnKeyType = UIReturnKeyDone; _secureTextEntry = NO; + if (@available(iOS 11.0, *)) { + _smartQuotesType = UITextSmartQuotesTypeYes; + _smartDashesType = UITextSmartDashesTypeYes; + } } return self; @@ -764,6 +770,18 @@ - (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configur _activeView.keyboardType = ToUIKeyboardType(inputType); _activeView.returnKeyType = ToUIReturnKeyType(configuration[@"inputAction"]); _activeView.autocapitalizationType = ToUITextAutoCapitalizationType(configuration); + if (@available(iOS 11.0, *)) { + NSString* smartDashesType = configuration[@"smartDashesType"]; + // This index comes from the SmartDashesType enum in the framework. + bool smartDashesIsDisabled = smartDashesType && [smartDashesType isEqualToString:@"0"]; + _activeView.smartDashesType = + smartDashesIsDisabled ? UITextSmartDashesTypeNo : UITextSmartDashesTypeYes; + NSString* smartQuotesType = configuration[@"smartQuotesType"]; + // This index comes from the SmartQuotesType enum in the framework. + bool smartQuotesIsDisabled = smartQuotesType && [smartQuotesType isEqualToString:@"0"]; + _activeView.smartQuotesType = + smartQuotesIsDisabled ? UITextSmartQuotesTypeNo : UITextSmartQuotesTypeYes; + } if ([keyboardAppearance isEqualToString:@"Brightness.dark"]) { _activeView.keyboardAppearance = UIKeyboardAppearanceDark; } else if ([keyboardAppearance isEqualToString:@"Brightness.light"]) { From 48ba39c569c5ec89ce78642fb71c0c26eca4c556 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 12 Dec 2019 10:57:37 -0500 Subject: [PATCH 403/591] Roll fuchsia/sdk/core/mac-amd64 from otkJA... to SlgE8... (#14407) Roll fuchsia/sdk/core/mac-amd64 from otkJA... to SlgE8... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c6a167978c484..04cf13a2d171c 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'otkJAJHPw68CJ4itPbDXj1RSMjEJ0yV3dwaCxgquRXcC' + 'version': 'SlgE80z3O5_qKVaaxQfrvvypC_EzobVucr9UEA1wSxUC' } ], 'condition': 'host_os == "mac"', From 0081e8c51665921b6a5aebd520301cd3c4ba13de Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Thu, 12 Dec 2019 09:43:44 -0800 Subject: [PATCH 404/591] Remove unused _TypeNone enum field. (#14440) * Remove unused field * Similarly remove _TypeNone from web_ui --- lib/ui/painting.dart | 2 -- lib/web_ui/lib/src/engine/color_filter.dart | 2 -- 2 files changed, 4 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index a6c96ac929884..33d209486f6d1 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -2632,8 +2632,6 @@ class ColorFilter { final int _type; // The type of SkColorFilter class to create for Skia. - // These constants must be kept in sync with ColorFilterType in paint.cc. - static const int _TypeNone = 0; // null static const int _TypeMode = 1; // MakeModeFilter static const int _TypeMatrix = 2; // MakeMatrixFilterRowMajor255 static const int _TypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma diff --git a/lib/web_ui/lib/src/engine/color_filter.dart b/lib/web_ui/lib/src/engine/color_filter.dart index 48e4b8bb60863..c6dcf8e1a9088 100644 --- a/lib/web_ui/lib/src/engine/color_filter.dart +++ b/lib/web_ui/lib/src/engine/color_filter.dart @@ -114,8 +114,6 @@ class EngineColorFilter implements ui.ColorFilter { final int _type; // The type of SkColorFilter class to create for Skia. - // These constants must be kept in sync with ColorFilterType in paint.cc. - static const int _TypeNone = 0; // null static const int _TypeMode = 1; // MakeModeFilter static const int _TypeMatrix = 2; // MakeMatrixFilterRowMajor255 static const int _TypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma From d8edfea0339f29a704569d366a15b7c38f7567d7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 12 Dec 2019 14:08:51 -0500 Subject: [PATCH 405/591] Roll src/third_party/dart d9fa37e85d5c..45db29709547 (48 commits) (#14453) https://dart.googlesource.com/sdk.git/+log/d9fa37e85d5c..45db29709547 git log d9fa37e85d5c..45db29709547 --date=short --first-parent --format='%ad %ae %s' 2019-12-12 victor.agnez@hotmail.com [vm/precomp] Reduce arm32 code size for BoxInt64Instr 2019-12-12 sstrickl@google.com [vm] De-obfuscate function and file names in DWARF sections. 2019-12-12 scheglov@google.com Remove Packages from SourceFactory. 2019-12-12 dacoharkes@google.com [vm/ffi] Split up function_callbacks_test 2019-12-12 whesse@google.com [infra] Add script to read builder status from Firestore 2019-12-12 whesse@google.com Reland "Reland "[infra] Add failing test to test CI systems and approvals workflow"" 2019-12-12 sstrickl@google.com [vm/compiler] Add --save-debugging-info flag to gen_snapshot. 2019-12-12 johnniwinther@google.com [cfe] Refactor for-in loops to handle flow analysis 2019-12-12 dacoharkes@google.com [vm/ffi] Fix asan test expectations 2019-12-12 athom@google.com [test] Fix import in exact_selector_test 2019-12-12 dmitryas@google.com [cfe] Implement NNBD-aware lower / upper bound algorithms 2019-12-12 dacoharkes@google.com [samples/ffi] Native resource lifetime management 2019-12-12 netroby@users.noreply.github.com Fixed #39679 make sqlite ffi sample run success 2019-12-12 jensj@google.com [parser] Allow ?? after Type? 2019-12-12 jensj@google.com [parser] Allow '?.' and '?.[' after '!' 2019-12-12 fizaaluthra@google.com [dartfuzz] Add flag for displaying output divergence details 2019-12-12 rmacnak@google.com [vm, arm64] Fix heap corruption in PushArrayOfArguments. 2019-12-12 srawlins@google.com NNBD preview: Rip out static preview; only server preview 2019-12-12 srawlins@google.com Connect g/setters which override fields (w/ implicit g/setters) 2019-12-12 fizaaluthra@google.com [dartfuzz] Re-enabling Int32x4 2019-12-11 sigmund@google.com fix nnbd bots: build step broke when tests were moved 2019-12-11 scheglov@google.com Packages configuration abstraction on top of .packages or package_config.json files. 2019-12-11 fishythefish@google.com [dart2js] Remove bad assert from runtime_types_new. 2019-12-11 gityuan@gmail.com [timeline] Add support for timeline asynchronous events in android platform trace 2019-12-11 alexmarkov@google.com Reland "[vm/compiler] Dead code elimination" 2019-12-11 rnystrom@google.com Move files under language_2 into subdirectories. 2019-12-11 rmacnak@google.com [vm] Initialize the handle vtable table during VM initialization instead of during class registration in each isolate's initialization. 2019-12-11 pquitslund@google.com bump to linter 0.1.106 2019-12-11 rmacnak@google.com [vm] Reduce frame size for the functions with the largest frame sizes. 2019-12-11 rmacnak@google.com [build] Use separate out directories for each sanitizer. 2019-12-11 brianwilkerson@google.com Update dartfix to display urls and wait when run in preview mode 2019-12-11 scheglov@google.com Implement NORM. 2019-12-11 alexmarkov@google.com [vm/test] Fix vm/dart/product_aot_kernel_test for bytecode 2019-12-11 alexmarkov@google.com [vm/ffi_test] Fix ClobberAndCall helper on Windows 2019-12-11 scheglov@google.com Implement parsing for package_config.json files in analyzer. 2019-12-11 scheglov@google.com Extract nullability TypeSystem tests. 2019-12-11 srawlins@google.com Remove duplicate 'DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS' already reported by fasta 2019-12-11 paulberry@google.com Create null-aware companion methods for ElementTypeProvider. 2019-12-11 brianwilkerson@google.com Start sending urls for preview mode from server to dartfix 2019-12-11 brianwilkerson@google.com Prepare to publish analysis_server_client so that dartfix can use the new API 2019-12-11 srawlins@google.com NNBD preview: improve details of where a nullable value is 'assigned' 2019-12-11 athom@google.com [infra] Update checked-in SDKs to 2.8.0-dev.0.0 2019-12-11 jensj@google.com [CFE] Fix bug in DebugStack 2019-12-11 whesse@google.com Revert "Reland "[infra] Add failing test to test CI systems and approvals workflow"" 2019-12-11 athom@google.com [co19] Run co19 nnbd tests on analyzer-nnbd 2019-12-11 sstrickl@google.com [vm] Remove the unchecked entry point offset field from RawInstructions. 2019-12-11 athom@google.com [release] Merge the two 2.7.0 sections in the changelog 2019-12-11 whesse@google.com Reland "[infra] Add failing test to test CI systems and approvals workflow" Created with: gclient setdep -r src/third_party/dart@45db29709547 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 6 +++--- ci/licenses_golden/licenses_third_party | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 04cf13a2d171c..3925e0c1e57fa 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd9fa37e85d5c55260042c460725de4e98e62a1a9', + 'dart_revision': '45db29709547cf48f68344ba7abb8c1e6473e508', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -63,7 +63,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.15.7', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.105+1', + 'dart_linter_tag': '0.1.106', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.3', @@ -381,7 +381,7 @@ deps = { Var('dart_git') + '/package_resolver.git' + '@' + Var('dart_package_resolver_tag'), 'src/third_party/dart/tools/sdks': - {'packages': [{'version': 'version:2.6.0', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, + {'packages': [{'version': 'version:2.8.0-dev.0.0', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, # WARNING: end of dart dependencies list that is cleaned up automatically - see create_updated_flutter_deps.py. diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 30d2efc37078c..9c500ebb546ee 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f29b388d37d2ea1b9f4eefc18448e379 +Signature: b48f64e897d07a9966a0eafc40b79835 UNUSED LICENSES: @@ -7671,6 +7671,12 @@ FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_arm64.cc FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_x64.cc FILE: ../../../third_party/dart/samples/ffi/coordinate.dart FILE: ../../../third_party/dart/samples/ffi/dylib_utils.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/pool.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/pool_sample.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/pool_zoned_sample.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/resource_management_test.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/unmanaged_sample.dart FILE: ../../../third_party/dart/samples/ffi/sample_ffi_bitfield.dart FILE: ../../../third_party/dart/samples/ffi/sample_ffi_data.dart FILE: ../../../third_party/dart/samples/ffi/sample_ffi_dynamic_library.dart From f650bcaf7784923d203b67e24d792e04b4fd0703 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut <50856934+nturgut@users.noreply.github.com> Date: Thu, 12 Dec 2019 11:18:02 -0800 Subject: [PATCH 406/591] Refactoring text editing. Strategy pattern is used to handle different browser/operating system and a11y behavior. (#14131) * adding the default text editing strategy * [DRAFT] Refactoring text editing. Strategy pattern is used to handle different browser/operating system and a11y behaviour. Unit tests are missing. Documentation needs updating. * addressing PR comments * addressing PR comments. Fixing documentation * fixing persistenttextediting element which is used in a11y mode * removing texteditingelement and using texteditingstrategy from hybridtextediting. fixing the unit tests. fixing comments * fix unit tests * add todos for firefox tests * fixing chrome/android a11y issue --- .../lib/src/engine/browser_detection.dart | 15 +- .../src/engine/text_editing/input_type.dart | 2 +- .../src/engine/text_editing/text_editing.dart | 588 +++++++++++------- lib/web_ui/test/text_editing_test.dart | 327 +++++----- 4 files changed, 556 insertions(+), 376 deletions(-) diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index 054b16e6be83b..3a4c1a4cbc305 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -29,10 +29,23 @@ enum BrowserEngine { /// Lazily initialized current browser engine. BrowserEngine _browserEngine; +/// Override the value of [browserEngine]. +/// +/// Setting this to `null` lets [browserEngine] detect the browser that the +/// app is running on. +/// +/// This is intended to be used for testing and debugging only. +BrowserEngine debugBrowserEngineOverride; + /// Returns the [BrowserEngine] used by the current browser. /// /// This is used to implement browser-specific behavior. -BrowserEngine get browserEngine => _browserEngine ??= _detectBrowserEngine(); +BrowserEngine get browserEngine { + if (debugBrowserEngineOverride != null) { + return debugBrowserEngineOverride; + } + return _browserEngine ??= _detectBrowserEngine(); +} BrowserEngine _detectBrowserEngine() { final String vendor = html.window.navigator.vendor; diff --git a/lib/web_ui/lib/src/engine/text_editing/input_type.dart b/lib/web_ui/lib/src/engine/text_editing/input_type.dart index 47c0ff7a09927..d7620c2f62584 100644 --- a/lib/web_ui/lib/src/engine/text_editing/input_type.dart +++ b/lib/web_ui/lib/src/engine/text_editing/input_type.dart @@ -66,7 +66,7 @@ abstract class EngineInputType { html.HtmlElement createDomElement() => html.InputElement(); /// Given a [domElement], set attributes that are specific to this input type. - void configureDomElement(html.HtmlElement domElement) { + void configureInputMode(html.HtmlElement domElement) { if (inputmodeAttribute == null) { return; } diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index edba66651a583..9c99621b78d36 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -12,16 +12,6 @@ const int _kReturnKeyCode = 13; void _emptyCallback(dynamic _) {} -/// Indicates whether virtual keyboard shifts the location of input element. -/// -/// Value decided using the operating system and the browser engine. -/// -/// In iOS, the virtual keyboard might shifts the screen up to make input -/// visible depending on the location of the focused input element. -bool get _doesKeyboardShiftInput => - browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs; - /// These style attributes are constant throughout the life time of an input /// element. /// @@ -222,37 +212,78 @@ class InputConfiguration { typedef _OnChangeCallback = void Function(EditingState editingState); typedef _OnActionCallback = void Function(String inputAction); -/// Wraps the DOM element used to provide text editing capabilities. +/// Interface defining the template for text editing strategies. /// -/// The backing DOM element could be one of: +/// The algorithms will be picked in the runtime depending on the concrete +/// class implementing the interface. /// -/// 1. ``. -/// 2. `

* See {@link PlatformMessageHandler}, which handles messages to Android from Dart diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java index 15600e1c3f46c..925cf8ce8d328 100644 --- a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java +++ b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java @@ -8,8 +8,7 @@ import android.support.annotation.Nullable; /** - * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. - * IF YOU USE IT, WE WILL BREAK YOU. + * Handler that receives messages from Dart code. */ public interface PlatformMessageHandler { void handleMessageFromDart(@NonNull final String channel, @Nullable byte[] message, final int replyId); From 075eae5990ff4dc938af663d4cdae14a1968361b Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 7 Nov 2019 15:37:07 -0800 Subject: [PATCH 051/591] Switch to Cirrus Dockerfile as CI (#13440) --- .cirrus.yml | 14 ++++++++------ ci/docker/build/README.md | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 1f9e1e6b505ee..17dafd5cc2f2b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -3,13 +3,15 @@ gcp_credentials: ENCRYPTED[987a78af29b91ce8489594c9ab3fec21845bbe5ba68294b8f6def # LINUX task: gke_container: - image: gcr.io/flutter-cirrus/build-engine-image:latest - cluster_name: build-32-cluster - zone: us-central1-a - namespace: default - cpu: 30 # can't use all 30-cores; system pods needs cores too - memory: 100Gb # similarly, can't use all 100Gb memory + dockerfile: "ci/docker/build/Dockerfile" + builder_image_name: docker-builder # gce vm image + cluster_name: build-32-cluster + zone: us-central1-a + namespace: default + cpu: 30 # can't use all 30-cores; system pods needs cores too + memory: 100Gb # similarly, can't use all 100Gb memory env: + CIRRUS_DOCKER_CONTEXT: "ci/docker/build" CIRRUS_WORKING_DIR: "/tmp/github_repo" ENGINE_PATH: "/tmp/clean_engine" DEPOT_TOOLS: "/tmp/depot_tools" diff --git a/ci/docker/build/README.md b/ci/docker/build/README.md index a4f12e16ca34e..79f0d92708418 100644 --- a/ci/docker/build/README.md +++ b/ci/docker/build/README.md @@ -4,9 +4,16 @@ building flutter/engine in our CI system (currently [Cirrus](cirrus-ci.org)). In order to run the scripts, you have to setup `docker` and `gcloud`. Please refer to internal doc go/installdocker for how to setup `docker` on gLinux. -After setup, -* edit `Dockerfile` to change how the container image is built. -* run `./build_docker.sh` to build the container image. -* run `./push_docker.sh` to push the image to google cloud registry. This will - affect our CI tests. +Cirrus will build (and cache) a Docker image based on this `Dockerfile` for +Linux tasks using its +[Dockerfile as CI](https://cirrus-ci.org/guide/docker-builder-vm/) feature. +Any change to the `Dockerfile` will cause a new task to be triggered to build +and tag a new version of the Docker image which will be a dependency of the +other Linux tasks. This task will instantiate a new GCP VM based on the image +specified in the `.cirrus.yml` `builder_image_name` field. +To test changes to the Linux `Dockerfile`, create a PR with the changes, and +Cirrus will attempt to build a new image. + +To debug locally, you can build an image with `./build_docker.sh`, but pushing +to the registry is no longer necessary. From 6ad57b0d5788c9ac78146f164e42db0a9c419745 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 7 Nov 2019 16:28:17 -0800 Subject: [PATCH 052/591] Don't run engine tests under vm (causing warnings) (#13737) --- lib/web_ui/build.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/build.yaml b/lib/web_ui/build.yaml index 86391d013bb95..af02fd0fb3390 100644 --- a/lib/web_ui/build.yaml +++ b/lib/web_ui/build.yaml @@ -8,4 +8,8 @@ targets: - --no-minify - --enable-asserts generate_for: - - test/**.dart + include: + - test/**.dart + exclude: + - test/**vm_test.dart + From 14db93afa317268d73d3d562431d289d77587a66 Mon Sep 17 00:00:00 2001 From: George Wright Date: Thu, 7 Nov 2019 17:48:14 -0800 Subject: [PATCH 053/591] Only specify --no-link-platform when not specifying --aot, roll dart-lang sdk (#13742) * Only specify --no-link-platform when --aot isn't specified * [dart_roll] Update to e68ca9b652acdb642668a6acb5f630d5be6c03da --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- tools/fuchsia/dart_kernel.gni | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 089790bcc70b6..45a69e3570118 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '4b9638aaa17203b4e4f9ceb8492faf6a11301999', + 'dart_revision': 'e68ca9b652acdb642668a6acb5f630d5be6c03da', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index e861d5dcdbf5f..05918740e37ab 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: cb6a78a49c674537ebcffa756ccdea6b +Signature: d45bc36638d83a8d56574a59410d78d8 UNUSED LICENSES: diff --git a/tools/fuchsia/dart_kernel.gni b/tools/fuchsia/dart_kernel.gni index ff9929ba7503a..d39db16b2e110 100644 --- a/tools/fuchsia/dart_kernel.gni +++ b/tools/fuchsia/dart_kernel.gni @@ -49,7 +49,6 @@ template("dart_kernel") { "--packages=" + rebase_path(dot_packages), "--target=dart_runner", "--platform=" + rebase_path(platform_dill), - "--no-link-platform", "--output=" + rebase_path(output), ] @@ -64,6 +63,9 @@ template("dart_kernel") { "--aot", "--tfa", ] + } else { + # --no-link-platform is only valid when --aot isn't specified + args += [ "--no-link-platform" ] } if (defined(invoker.product) && invoker.product) { From 6a1f093398c6dc575a7a85ad66d5851316b0c2cb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 7 Nov 2019 21:06:31 -0500 Subject: [PATCH 054/591] Roll src/third_party/skia 345a2735e2c8..76eaab6b8cef (18 commits) (#13745) https://skia.googlesource.com/skia.git/+log/345a2735e2c8..76eaab6b8cef git log 345a2735e2c8..76eaab6b8cef --date=short --no-merges --format='%ad %ae %s' 2019-11-08 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-08 csmartdalton@google.com Implement multisample "disable" in Vulkan 2019-11-08 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-07 mtklein@google.com Reland "hook up float comparisons to x86 JIT" 2019-11-07 herb@google.com Remove unused methods from SkStrike 2019-11-07 mtklein@google.com avoid the JIT on MSAN builds 2019-11-07 halcanary@google.com SkPDF/docs: note that Sfntly subsetter is deprecated 2019-11-07 herb@google.com One glyph() to rule them all!!! 2019-11-07 reed@google.com flesh out blendmodes through Screen 2019-11-07 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-07 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-07 robertphillips@google.com Use GrProgramInfo's primitiveType field 2019-11-07 mtklein@google.com Revert "hook up float comparisons to x86 JIT" 2019-11-07 egdaniel@google.com Add handling of failed framebuffer creation in vulkan. 2019-11-07 mtklein@google.com hook up float comparisons to x86 JIT 2019-11-07 mtklein@google.com gn format BUILD.gn 2019-11-07 robertphillips@google.com Add GrPrimitiveType to GrProgramInfo 2019-11-07 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 1d09b983031b..cc0919dcb64b (8 commits) Created with: gclient setdep -r src/third_party/skia@76eaab6b8cef If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 45a69e3570118..2c072a45e1f58 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '345a2735e2c8e3188f01938cdc62848bf64cf97f', + 'skia_revision': '76eaab6b8cef37e79d5cb1b247e889545ab038aa', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 221f81e677a27..aa801e0c46519 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: aba11c4957d170f239906fde5d332435 +Signature: 74ea915bede8b217a9d54e1233d62ad6 UNUSED LICENSES: From 7590336bbc0b268d38a1a2fb2e61089866d88b69 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 7 Nov 2019 19:53:51 -0800 Subject: [PATCH 055/591] Create a new picture recorder even when the embedder supplied render target is recycled. (#13744) The earlier assumption was that the render target would be re-materialized per frame. The render target needs its own picture recorder to be create per frame as well. When render targets are cached in the registry, an existing target will be reused. But submitting the previous frame would have discarded the recorder already. The layer tree paint would then attempt to dererence a null canvas causing a crash at runtime. Added tests to ensure that this does not happen both with and without a custom compositor specified by the embedder. I am going to rework this code so that the external view embedders thinks of render target access on a per frame basis but that is a larger change. This smaller patchset should unblock broken builds. Fixes b/144093523 --- .../embedder_external_view_embedder.cc | 7 +- shell/platform/embedder/fixtures/main.dart | 17 ++++ .../embedder/tests/embedder_unittests.cc | 80 +++++++++++++++++++ 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index b742bc043b527..615359cf5e97e 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -100,10 +100,11 @@ void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size, if (!root_render_target_) { root_render_target_ = create_render_target_callback_( context, MakeBackingStoreConfig(surface_size)); - root_picture_recorder_ = std::make_unique(); - root_picture_recorder_->beginRecording(pending_frame_size_.width(), - pending_frame_size_.height()); } + + root_picture_recorder_ = std::make_unique(); + root_picture_recorder_->beginRecording(pending_frame_size_.width(), + pending_frame_size_.height()); } // |ExternalViewEmbedder| diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 2d5ae8f535b85..7ba9d55831a6d 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -491,3 +491,20 @@ void verify_b143464703() { }; window.scheduleFrame(); } + +@pragma('vm:entry-point') +void push_frames_over_and_over() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.pushOffset(0.0, 0.0); + builder.addPicture(Offset(0.0, 0.0), CreateColoredBox(Color.fromARGB(255, 128, 128, 128), Size(1024.0, 600.0))); + builder.pushOpacity(128); + builder.addPlatformView(42, width: 1024.0, height: 540.0); + builder.pop(); + builder.pop(); + window.render(builder.build()); + signalNativeTest(); + window.scheduleFrame(); + }; + window.scheduleFrame(); +} diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 13d0453a1603d..c6b0a505f3fa9 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -2952,5 +2952,85 @@ TEST_F(EmbedderTest, VerifyB143464703) { ASSERT_TRUE(ImageMatchesFixture("verifyb143464703.png", renderered_scene)); } +TEST_F(EmbedderTest, + PushingMutlipleFramesSetsUpNewRecordingCanvasWithCustomCompositor) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024)); + builder.SetCompositor(); + builder.SetDartEntrypoint("push_frames_over_and_over"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + const auto root_surface_transformation = + SkMatrix().preTranslate(0, 1024).preRotate(-90, 0, 0); + + context.SetRootSurfaceTransformation(root_surface_transformation); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 1024; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + constexpr size_t frames_expected = 10; + fml::CountDownLatch frame_latch(frames_expected); + size_t frames_seen = 0; + context.AddNativeCallback("SignalNativeTest", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + frames_seen++; + frame_latch.CountDown(); + })); + frame_latch.Wait(); + + ASSERT_EQ(frames_expected, frames_seen); +} + +TEST_F(EmbedderTest, + PushingMutlipleFramesSetsUpNewRecordingCanvasWithoutCustomCompositor) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024)); + builder.SetDartEntrypoint("push_frames_over_and_over"); + + const auto root_surface_transformation = + SkMatrix().preTranslate(0, 1024).preRotate(-90, 0, 0); + + context.SetRootSurfaceTransformation(root_surface_transformation); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 1024; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + constexpr size_t frames_expected = 10; + fml::CountDownLatch frame_latch(frames_expected); + size_t frames_seen = 0; + context.AddNativeCallback("SignalNativeTest", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + frames_seen++; + frame_latch.CountDown(); + })); + frame_latch.Wait(); + + ASSERT_EQ(frames_expected, frames_seen); +} + } // namespace testing } // namespace flutter From 8928c30b30a862d4ee835820484341bd17392e28 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 00:59:22 -0500 Subject: [PATCH 056/591] Roll src/third_party/skia 76eaab6b8cef..3fd426d19df3 (1 commits) (#13746) https://skia.googlesource.com/skia.git/+log/76eaab6b8cef..3fd426d19df3 git log 76eaab6b8cef..3fd426d19df3 --date=short --no-merges --format='%ad %ae %s' 2019-11-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 27a3d31d7a9d..d9ed1c2732ba (1 commits) Created with: gclient setdep -r src/third_party/skia@3fd426d19df3 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2c072a45e1f58..d1e2abf14172a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '76eaab6b8cef37e79d5cb1b247e889545ab038aa', + 'skia_revision': '3fd426d19df339852672b1ac6f023f6adfb7216a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index aa801e0c46519..bd9829c4b618b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 74ea915bede8b217a9d54e1233d62ad6 +Signature: d0f2751db21de83cf13a6bc2e6f7343d UNUSED LICENSES: From 9399645f8af8f30957b71fa25e9d3c0a85026ab5 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 04:51:31 -0500 Subject: [PATCH 057/591] Roll src/third_party/skia 3fd426d19df3..99b558b594a1 (1 commits) (#13749) https://skia.googlesource.com/skia.git/+log/3fd426d19df3..99b558b594a1 git log 3fd426d19df3..99b558b594a1 --date=short --no-merges --format='%ad %ae %s' 2019-11-08 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 cc0919dcb64b..652dbfc63e70 (7 commits) Created with: gclient setdep -r src/third_party/skia@99b558b594a1 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d1e2abf14172a..dd8b2ce610ce4 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3fd426d19df339852672b1ac6f023f6adfb7216a', + 'skia_revision': '99b558b594a12ec1d09172f85a6586b17de75ccc', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index bd9829c4b618b..f55c68faeb441 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: d0f2751db21de83cf13a6bc2e6f7343d +Signature: 58eec51548ec783cf7ab33314c8436c2 UNUSED LICENSES: From b1a1dd6c6c5a6131cb0cc5048cb3f7505bc80809 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 06:04:17 -0500 Subject: [PATCH 058/591] Roll fuchsia/sdk/core/mac-amd64 from H_5HL... to KRali... (#13750) Roll fuchsia/sdk/core/mac-amd64 from H_5HL... to KRali... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index dd8b2ce610ce4..d25a309bb0a14 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'H_5HL34zOj71EeF7wJSU-U66_qIO1F0bmJ9xYAnX-dwC' + 'version': 'KRali1kRjy2d-AU4s8vUOEeBiqRW9rkDXW0Ovc-ac6gC' } ], 'condition': 'host_os == "mac"', From 5f5713e3397164dd895c7ce33e3f8d1ba013784e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 10:04:30 -0500 Subject: [PATCH 059/591] Roll src/third_party/skia 99b558b594a1..8c1e265f6f81 (1 commits) (#13751) https://skia.googlesource.com/skia.git/+log/99b558b594a1..8c1e265f6f81 git log 99b558b594a1..8c1e265f6f81 --date=short --no-merges --format='%ad %ae %s' 2019-11-08 mtklein@google.com remove redundant comparison ops Created with: gclient setdep -r src/third_party/skia@8c1e265f6f81 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d25a309bb0a14..80a0dfa731c91 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '99b558b594a12ec1d09172f85a6586b17de75ccc', + 'skia_revision': '8c1e265f6f8145ec0638fc78d4821bde8e7956c5', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f55c68faeb441..a6b58d2c2e5ea 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 58eec51548ec783cf7ab33314c8436c2 +Signature: eb6c9ef1aad6e6b86a2077d692c5bb42 UNUSED LICENSES: From bec554211b360765c9913236347ca18dc881ebc0 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Nov 2019 10:02:23 -0800 Subject: [PATCH 060/591] Always use `IOSGLContextSwitch` to access EAGLContexts to prevent plugins from polluting Flutter's EAGLContext (#13314) --- ci/licenses_golden/licenses_flutter | 4 + shell/common/BUILD.gn | 2 + shell/common/gl_context_switch_manager.cc | 28 +++++ shell/common/gl_context_switch_manager.h | 116 ++++++++++++++++++ shell/common/shell_test.cc | 11 +- shell/common/shell_test.h | 5 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 +++-- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/platform/android/android_surface_gl.cc | 12 +- shell/platform/android/android_surface_gl.h | 6 +- shell/platform/darwin/ios/BUILD.gn | 2 + .../framework/Source/FlutterPlatformViews.mm | 13 +- .../Source/FlutterPlatformViews_Internal.h | 6 + shell/platform/darwin/ios/ios_gl_context.h | 16 ++- shell/platform/darwin/ios/ios_gl_context.mm | 21 +--- .../ios/ios_gl_context_switch_manager.h | 65 ++++++++++ .../ios/ios_gl_context_switch_manager.mm | 78 ++++++++++++ .../darwin/ios/ios_gl_render_target.h | 15 +-- .../darwin/ios/ios_gl_render_target.mm | 41 ++++--- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 8 +- shell/platform/darwin/ios/ios_surface_gl.mm | 17 ++- .../darwin/ios/ios_surface_software.h | 4 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 +-- .../platform/embedder/embedder_surface_gl.cc | 12 +- shell/platform/embedder/embedder_surface_gl.h | 6 +- .../Scenarios.xcodeproj/project.pbxproj | 16 ++- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ++++ .../Scenarios/Scenarios/GLTestPlatformView.h | 30 +++++ .../Scenarios/Scenarios/GLTestPlatformView.m | 90 ++++++++++++++ .../ScenariosUITests/PlatformViewGLTests.m | 40 ++++++ testing/scenario_app/lib/main.dart | 1 + .../scenario_app/lib/src/platform_view.dart | 36 ++++-- testing/scenario_app/lib/src/texture.dart | 0 39 files changed, 713 insertions(+), 115 deletions(-) create mode 100644 shell/common/gl_context_switch_manager.cc create mode 100644 shell/common/gl_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m create mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4e36d64a55529..e54ec6404291c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -495,6 +495,8 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/fixtures/shell_test.dart +FILE: ../../../flutter/shell/common/gl_context_switch_manager.cc +FILE: ../../../flutter/shell/common/gl_context_switch_manager.h FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h @@ -799,6 +801,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f93bca63478f0..50f1defa57f8d 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -66,6 +66,8 @@ source_set("common") { "canvas_spy.h", "engine.cc", "engine.h", + "gl_context_switch_manager.cc", + "gl_context_switch_manager.h", "isolate_configuration.cc", "isolate_configuration.h", "persistent_cache.cc", diff --git a/shell/common/gl_context_switch_manager.cc b/shell/common/gl_context_switch_manager.cc new file mode 100644 index 0000000000000..37c12e885b3f3 --- /dev/null +++ b/shell/common/gl_context_switch_manager.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gl_context_switch_manager.h" + +namespace flutter { + +GLContextSwitchManager::GLContextSwitchManager() = default; + +GLContextSwitchManager::~GLContextSwitchManager() = default; + +GLContextSwitchManager::GLContextSwitch::GLContextSwitch() = default; + +GLContextSwitchManager::GLContextSwitch::~GLContextSwitch(){}; + +GLContextSwitchManager::GLContextSwitchPureResult::GLContextSwitchPureResult( + bool switch_result) + : switch_result_(switch_result){}; + +GLContextSwitchManager::GLContextSwitchPureResult:: + ~GLContextSwitchPureResult() = default; + +bool GLContextSwitchManager::GLContextSwitchPureResult::GetSwitchResult() { + return switch_result_; +} + +} // namespace flutter diff --git a/shell/common/gl_context_switch_manager.h b/shell/common/gl_context_switch_manager.h new file mode 100644 index 0000000000000..90b27b4232682 --- /dev/null +++ b/shell/common/gl_context_switch_manager.h @@ -0,0 +1,116 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ + +#include +#include "flutter/fml/macros.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// Manages `GLContextSwitch`. +/// +/// Should be subclassed for platforms that uses GL and requires context +/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the +/// `GLContextSwitchManager` to set gl contexts. +/// +class GLContextSwitchManager { + public: + //------------------------------------------------------------------------------ + /// Switches the gl context to the flutter's contexts. + /// + /// Should be subclassed for each platform embedder that uses GL. + /// In construction, it should set the current context to a flutter's context + /// In destruction, it should rest the current context. + /// + class GLContextSwitch { + public: + GLContextSwitch(); + + virtual ~GLContextSwitch(); + + virtual bool GetSwitchResult() = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitch); + }; + + GLContextSwitchManager(); + ~GLContextSwitchManager(); + + //---------------------------------------------------------------------------- + /// @brief Creates a shell instance using the provided settings. The + /// callbacks to create the various shell subcomponents will be + /// called on the appropriate threads before this method returns. + /// If this is the first instance of a shell in the process, this + /// call also bootstraps the Dart VM. + /// + /// @param[in] task_runners The task runners + /// @param[in] settings The settings + /// @param[in] on_create_platform_view The callback that must return a + /// platform view. This will be called on + /// the platform task runner before this + /// method returns. + /// @param[in] on_create_rasterizer That callback that must provide a + /// valid rasterizer. This will be called + /// on the render task runner before this + /// method returns. + /// + /// @return A full initialized shell if the settings and callbacks are + /// valid. The root isolate has been created but not yet launched. + /// It may be launched by obtaining the engine weak pointer and + /// posting a task onto the UI task runner with a valid run + /// configuration to run the isolate. The embedder must always + /// check the validity of the shell (using the IsSetup call) + /// immediately after getting a pointer to it. + /// + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's context as current context. + /// + /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if + /// the setting process is succesful. + virtual std::unique_ptr MakeCurrent() = 0; + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's resources context as current context. + /// + /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if + /// the setting process is succesful. + virtual std::unique_ptr ResourceMakeCurrent() = 0; + + //------------------------------------------------------------------------------ + /// A representation of a `GLContextSwitch` that doesn't require actual + /// context switching. + /// + class GLContextSwitchPureResult final : public GLContextSwitch { + public: + // Constructor that creates an `GLContextSwitchPureResult`. + // The `GetSwitchResult` will return the same value as `switch_result`. + + //---------------------------------------------------------------------------- + /// @brief Constructs a `GLContextSwitchPureResult`. + /// + /// @param[in] switch_result the switch result that will be returned + /// in `GetSwitchResult()` + /// + GLContextSwitchPureResult(bool switch_result); + + ~GLContextSwitchPureResult(); + + bool GetSwitchResult() override; + + private: + bool switch_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchPureResult); + }; + + FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchManager); +}; + +} // namespace flutter + +#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 51370d082862b..7c0785ab9d2e4 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,8 +355,10 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -bool ShellTestPlatformView::GLContextMakeCurrent() { - return gl_surface_.MakeCurrent(); +std::unique_ptr +ShellTestPlatformView::GLContextMakeCurrent() { + return std::make_unique( + gl_surface_.MakeCurrent()); } // |GPUSurfaceGLDelegate| @@ -387,5 +389,10 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } +std::shared_ptr +ShellTestPlatformView::GetGLContextSwitchManager() { + return nullptr; +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index fdee9653b71ce..7d1d442dd8533 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,7 +144,8 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -161,6 +162,8 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + std::shared_ptr GetGLContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index b8a77ca1811d4..392a8cad54e3e 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,8 +60,10 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -bool Surface::MakeRenderContextCurrent() { - return true; +std::unique_ptr +Surface::MakeRenderContextCurrent() { + return std::make_unique( + true); } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index 7bbc16e24c690..d34d4dae93e04 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,6 +10,7 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -58,7 +59,8 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual bool MakeRenderContextCurrent(); + virtual std::unique_ptr + MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5f30a48375d33..b69475aefd6ff 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" @@ -39,7 +40,10 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -87,8 +91,6 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; - - delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -98,7 +100,9 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -114,8 +118,9 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -126,8 +131,6 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; - - delegate_->GLContextClearCurrent(); } // |Surface| @@ -253,7 +256,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -285,7 +290,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - return std::make_unique(surface, submit_callback); + std::unique_ptr result = + std::make_unique(surface, submit_callback); + return result; } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -293,6 +300,8 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -329,7 +338,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } - return true; } @@ -360,7 +368,8 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -bool GPUSurfaceGL::MakeRenderContextCurrent() { +std::unique_ptr +GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 97325569bfd16..953ebf0872161 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,7 +44,8 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - bool MakeRenderContextCurrent() override; + std::unique_ptr + MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dfe0ce7f468db..fe915679e55f5 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,6 +7,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -16,7 +17,8 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual bool GLContextMakeCurrent() = 0; + virtual std::unique_ptr + GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -59,6 +61,9 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; + + virtual std::shared_ptr + GetGLContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 737d9f293a518..f1a5edbe774ad 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,9 +104,11 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -bool AndroidSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr +AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return onscreen_context_->MakeCurrent(); + return std::make_unique( + onscreen_context_->MakeCurrent()); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -130,4 +132,10 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +AndroidSurfaceGL::GetGLContextSwitchManager() { + return nullptr; +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index d59302ad66509..e30300cf91289 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,7 +47,8 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -61,6 +62,9 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetGLContextSwitchManager() override; + private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index a66c3bf48c5ae..3e67beb97e717 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,6 +86,8 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", + "ios_gl_context_switch_manager.h", + "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33ca14d9fabea..5bcab91e01655 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,6 +160,11 @@ frame_size_ = frame_size; } +void FlutterPlatformViewsController::SetGLContextSwitchManager( + std::shared_ptr gl_context_guard_manager) { + gl_context_switch_manager_ = gl_context_guard_manager; +} + void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -368,7 +373,10 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, gl_context, gr_context); + std::unique_ptr contextSwitch = + gl_context_switch_manager_->MakeCurrent(); + + EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -456,6 +464,9 @@ GrContext* gr_context) { FML_DCHECK(flutter_view_); + std::unique_ptr contextSwitch = + gl_context_switch_manager_->MakeCurrent(); + auto overlay_it = overlays_.find(overlay_id); if (!gr_context) { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c8daeaa605946..211f45cc73c25 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,6 +11,7 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -80,6 +81,9 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); + void SetGLContextSwitchManager( + std::shared_ptr gl_context_guard_manager); + void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -204,6 +208,8 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + std::shared_ptr gl_context_switch_manager_; + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 232645d9c8592..136616e8dfc5b 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,16 +26,24 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - bool MakeCurrent(); + std::unique_ptr MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); + + std::shared_ptr GetIOSGLContextSwitchManager() { + return gl_context_switch_manager_; + } sk_sp ColorSpace() const { return color_space_; } + fml::scoped_nsobject GetContext() const { + return gl_context_switch_manager_->GetContext(); + } + private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; sk_sp color_space_; + std::shared_ptr gl_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 52fb85f8f19a9..8436a5a44413f 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,15 +13,7 @@ namespace flutter { IOSGLContext::IOSGLContext() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } + gl_context_switch_manager_ = std::make_shared(); // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -48,16 +40,15 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), context_.get(), - resource_context_.get()); + return std::make_unique(std::move(layer), gl_context_switch_manager_); } -bool IOSGLContext::MakeCurrent() { - return [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr IOSGLContext::MakeCurrent() { + return gl_context_switch_manager_->MakeCurrent(); } -bool IOSGLContext::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr IOSGLContext::ResourceMakeCurrent() { + return gl_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h new file mode 100644 index 0000000000000..5d3714f326fe4 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ + +#define GLES_SILENCE_DEPRECATION + +#import +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/gl_context_switch_manager.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// The iOS implementation of `GLContextSwitchManager`. +/// +/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and +/// sets the flutter's gl context as current. +/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to +/// current. +/// +class IOSGLContextSwitchManager final : public GLContextSwitchManager { + public: + class IOSGLContextSwitch final : public GLContextSwitch { + public: + IOSGLContextSwitch(IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context); + + ~IOSGLContextSwitch(); + + bool GetSwitchResult() override; + + private: + IOSGLContextSwitchManager& manager_; + bool switch_result_; + bool has_pushed_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); + }; + + IOSGLContextSwitchManager(); + + ~IOSGLContextSwitchManager(); + + std::unique_ptr MakeCurrent() override; + std::unique_ptr ResourceMakeCurrent() override; + + fml::scoped_nsobject GetContext(); + + private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; + fml::scoped_nsobject stored_; + + bool PushContext(fml::scoped_nsobject context); + void PopContext(); + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); +}; + +} + +#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm new file mode 100644 index 0000000000000..c6b332f121b46 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios_gl_context_switch_manager.h" + +namespace flutter { + +IOSGLContextSwitchManager::IOSGLContextSwitchManager() { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } +}; + +IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; + +std::unique_ptr IOSGLContextSwitchManager::MakeCurrent() { + return std::make_unique(*this, context_); +} + +std::unique_ptr +IOSGLContextSwitchManager::ResourceMakeCurrent() { + return std::make_unique(*this, resource_context_); +} + +fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { + return context_; +} + +bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { + EAGLContext* current = [EAGLContext currentContext]; + if (current == nil) { + [stored_.get() addObject:[NSNull null]]; + } else { + [stored_.get() addObject:current]; + } + bool result = [EAGLContext setCurrentContext:context.get()]; + return result; +} + +void IOSGLContextSwitchManager::PopContext() { + EAGLContext* last = [stored_.get() lastObject]; + [stored_.get() removeLastObject]; + if ([last isEqual:[NSNull null]]) { + [EAGLContext setCurrentContext:nil]; + return; + } + [EAGLContext setCurrentContext:last]; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( + IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context) + : manager_(manager) { + bool result = manager_.PushContext(context); + has_pushed_context_ = true; + switch_result_ = result; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { + if (!has_pushed_context_) { + return; + } + manager_.PopContext(); +} + +bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { + return switch_result_; +} +} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index b2eafe16e0950..10428896b63a0 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,14 +13,15 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context); + IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager); ~IOSGLRenderTarget(); @@ -32,16 +33,16 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - bool MakeCurrent(); + std::unique_ptr MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; + std::shared_ptr gl_context_switch_manager_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a57ba9c46b414..a3581e77198a8 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,22 +12,20 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context) +IOSGLRenderTarget::IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager) : layer_(std::move(layer)), - context_([context retain]), - resource_context_([resource_context retain]), + gl_context_switch_manager_(gl_context_guard_manager), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - FML_DCHECK(context_ != nullptr); - FML_DCHECK(resource_context_ != nullptr); - - bool context_current = [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + gl_context_switch_manager_->MakeCurrent(); + bool context_current = context_switch->GetSwitchResult(); FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -62,8 +60,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - EAGLContext* context = EAGLContext.currentContext; - [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + gl_context_switch_manager_->MakeCurrent(); FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -71,7 +69,6 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -104,8 +101,9 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - - if (![EAGLContext setCurrentContext:context_]) { + std::unique_ptr context_switch = + gl_context_switch_manager_->MakeCurrent(); + if (!context_switch->GetSwitchResult()) { return false; } @@ -116,7 +114,8 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { + if (![gl_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER + fromDrawable:layer_.get()]) { return false; } @@ -132,12 +131,16 @@ return true; } -bool IOSGLRenderTarget::MakeCurrent() { - return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr IOSGLRenderTarget::MakeCurrent() { + bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); + if (!isUpdateSuccessful) { + return std::make_unique(false); + } + return gl_context_switch_manager_->MakeCurrent(); } -bool IOSGLRenderTarget::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr IOSGLRenderTarget::ResourceMakeCurrent() { + return gl_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 49f40f9eec76a..13253e3eb0bdb 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -27,7 +28,8 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual bool ResourceContextMakeCurrent() = 0; + virtual std::unique_ptr + ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index c1019bb442bb0..8bee87fbf42b1 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,6 +9,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -32,7 +33,7 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -40,7 +41,7 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - bool GLContextMakeCurrent() override; + std::unique_ptr GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -53,6 +54,9 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetGLContextSwitchManager() override; + // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 48e70e00a4e7a..de114d1265278 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,7 +28,8 @@ return render_target_->IsValid(); } -bool IOSSurfaceGL::ResourceContextMakeCurrent() { +std::unique_ptr +IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -56,11 +57,13 @@ return true; } -bool IOSSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return false; + NSLog(@"not valid"); + return std::make_unique(false); } - return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); + NSLog(@"valid"); + return render_target_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -73,6 +76,11 @@ return IsValid() && render_target_->PresentRenderBuffer(); } +// |GPUSurfaceGLDelegate| +std::shared_ptr IOSSurfaceGL::GetGLContextSwitchManager() { + return context_->GetIOSGLContextSwitchManager(); +} + // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -141,6 +149,7 @@ // |ExternalViewEmbedder| bool IOSSurfaceGL::SubmitFrame(GrContext* context) { FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + platform_views_controller->SetGLContextSwitchManager(context_->GetIOSGLContextSwitchManager()); if (platform_views_controller == nullptr) { return true; } diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index daac2ffc77231..5feb1363c1e5c 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" - @class CALayer; namespace flutter { @@ -28,7 +28,7 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index ab5490cf25140..1c87b33e6e92a 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,8 +27,9 @@ return layer_; } -bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return false; +std::unique_ptr +IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return std::make_unique(false); } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index bb37fa9610b2b..1983eb7371383 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,15 +107,18 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + if (gl_context_ != nullptr) { + std::unique_ptr context_switch = + gl_context_->ResourceMakeCurrent(); + if (context_switch->GetSwitchResult()) { + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + } } - - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index d37b03aae8d9e..59566e844d42b 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,8 +34,10 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextMakeCurrent() { - return gl_dispatch_table_.gl_make_current_callback(); +std::unique_ptr +EmbedderSurfaceGL::GLContextMakeCurrent() { + return std::make_unique( + gl_dispatch_table_.gl_make_current_callback()); } // |GPUSurfaceGLDelegate| @@ -79,6 +81,12 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +EmbedderSurfaceGL::GetGLContextSwitchManager() { + return nullptr; +} + // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index a01fa05d4e62c..f810597111f1f 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,7 +50,8 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -73,6 +74,9 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetGLContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index e667c4c88678d..dd2238aba3181 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -136,6 +138,9 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -202,6 +207,8 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -239,6 +246,7 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -398,6 +406,7 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -418,6 +427,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -492,7 +502,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -545,7 +555,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -561,6 +571,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -584,6 +595,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index 87799ad5e6434..cd1c5b7366a89 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - - - - - - - - * registrar = + [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; + [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; + self.window.rootViewController = flutterViewController; +} + @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h new file mode 100644 index 0000000000000..19cefe7025986 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GLTestPlatformView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject*)messenger; + +- (UIView*)view; + +@end + +@interface GLTestPlatformViewFactory : NSObject + +- (instancetype)initWithMessenger:(NSObject*)messenger; + +@end + +@interface GLTestView : UIView + +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m new file mode 100644 index 0000000000000..8142550e52863 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m @@ -0,0 +1,90 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GLTestPlatformView.h" + +#define GLES_SILENCE_DEPRECATION + +@implementation GLTestPlatformView { + int64_t _viewId; + GLTestView* _view; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id)args + binaryMessenger:(NSObject*)messenger { + if ([super init]) { + _viewId = viewId; + _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; + } + return self; +} + +- (UIView*)view { + return _view; +} + +@end + +@implementation GLTestPlatformViewFactory { + NSObject* _messenger; +} + +- (instancetype)initWithMessenger:(NSObject*)messenger { + self = [super init]; + if (self) { + _messenger = messenger; + } + return self; +} + +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger]; + return platformView; +} + +- (NSObject*)createArgsCodec { + return [FlutterStringCodec sharedInstance]; +} + +@end + +@interface GLTestView () + +@property(strong, nonatomic) EAGLContext* context; + +@end + +@implementation GLTestView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + _context.debugLabel = @"platform view context"; + [EAGLContext setCurrentContext:_context]; + self.backgroundColor = [UIColor redColor]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [self checkEAGLContext]; + }); + } + return self; +} + +- (void)checkEAGLContext { + if ([EAGLContext currentContext] != _context) { + self.accessibilityIdentifier = @"gl_platformview_wrong_context"; + } else { + self.accessibilityIdentifier = @"gl_platformview_correct_context"; + } +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m new file mode 100644 index 0000000000000..000a7e578fc90 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface PlatformViewGLTests : XCTestCase + +@property(nonatomic, strong) XCUIApplication* application; + +@end + +@implementation PlatformViewGLTests + +- (void)setUp { + self.continueAfterFailure = NO; + + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--platform-view-gl" ]; + [self.application launch]; +} + +- (void)testExample { + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || + [element.identifier isEqualToString:@"gl_platformview_correct_context"]; + }]; + XCUIElement* firstElement = + [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; + if (![firstElement waitForExistenceWithTimeout:30]) { + NSLog(@"%@", self.application.debugDescription); + XCTFail(@"Failed due to not able to find platform view with 30 seconds"); + } + XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); +} + +@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 7a264b70008f0..82b589a6681fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,6 +25,7 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), + 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 05efe52fa8b25..86fb4df1df449 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -281,6 +281,24 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } +/// Platform view scenario for testing EAGLContext on iOS. +class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Constructs a platform view to test EAGLContext on iOS. + PlatformViewGLScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + finishBuilderByAddingPlatformViewAndPicture(builder, 6); + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -289,7 +307,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id) { + void createPlatformView(Window window, String text, int id, String viewType) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -313,8 +331,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - 'scenarios/textPlatformView'.length, - ...utf8.encode('scenarios/textPlatformView'), + viewType.length, + ...utf8.encode(viewType), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart new file mode 100644 index 0000000000000..e69de29bb2d1d From 2036530c4dd68dd7bd4caf99bbe757fa6c96f8f8 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Nov 2019 10:59:45 -0800 Subject: [PATCH 061/591] Revert "Always use `IOSGLContextSwitch` to access EAGLContexts to prevent plugins from polluting Flutter's EAGLContext (#13314)" (#13753) --- ci/licenses_golden/licenses_flutter | 4 - shell/common/BUILD.gn | 2 - shell/common/gl_context_switch_manager.cc | 28 ----- shell/common/gl_context_switch_manager.h | 116 ------------------ shell/common/shell_test.cc | 11 +- shell/common/shell_test.h | 5 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 ++--- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/platform/android/android_surface_gl.cc | 12 +- shell/platform/android/android_surface_gl.h | 6 +- shell/platform/darwin/ios/BUILD.gn | 2 - .../framework/Source/FlutterPlatformViews.mm | 13 +- .../Source/FlutterPlatformViews_Internal.h | 6 - shell/platform/darwin/ios/ios_gl_context.h | 16 +-- shell/platform/darwin/ios/ios_gl_context.mm | 21 +++- .../ios/ios_gl_context_switch_manager.h | 65 ---------- .../ios/ios_gl_context_switch_manager.mm | 78 ------------ .../darwin/ios/ios_gl_render_target.h | 15 ++- .../darwin/ios/ios_gl_render_target.mm | 41 +++---- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 8 +- shell/platform/darwin/ios/ios_surface_gl.mm | 17 +-- .../darwin/ios/ios_surface_software.h | 4 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 ++- .../platform/embedder/embedder_surface_gl.cc | 12 +- shell/platform/embedder/embedder_surface_gl.h | 6 +- .../Scenarios.xcodeproj/project.pbxproj | 16 +-- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ---- .../Scenarios/Scenarios/GLTestPlatformView.h | 30 ----- .../Scenarios/Scenarios/GLTestPlatformView.m | 90 -------------- .../ScenariosUITests/PlatformViewGLTests.m | 40 ------ testing/scenario_app/lib/main.dart | 1 - .../scenario_app/lib/src/platform_view.dart | 36 ++---- testing/scenario_app/lib/src/texture.dart | 0 39 files changed, 115 insertions(+), 713 deletions(-) delete mode 100644 shell/common/gl_context_switch_manager.cc delete mode 100644 shell/common/gl_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m delete mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e54ec6404291c..4e36d64a55529 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -495,8 +495,6 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/fixtures/shell_test.dart -FILE: ../../../flutter/shell/common/gl_context_switch_manager.cc -FILE: ../../../flutter/shell/common/gl_context_switch_manager.h FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h @@ -801,8 +799,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 50f1defa57f8d..f93bca63478f0 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -66,8 +66,6 @@ source_set("common") { "canvas_spy.h", "engine.cc", "engine.h", - "gl_context_switch_manager.cc", - "gl_context_switch_manager.h", "isolate_configuration.cc", "isolate_configuration.h", "persistent_cache.cc", diff --git a/shell/common/gl_context_switch_manager.cc b/shell/common/gl_context_switch_manager.cc deleted file mode 100644 index 37c12e885b3f3..0000000000000 --- a/shell/common/gl_context_switch_manager.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "gl_context_switch_manager.h" - -namespace flutter { - -GLContextSwitchManager::GLContextSwitchManager() = default; - -GLContextSwitchManager::~GLContextSwitchManager() = default; - -GLContextSwitchManager::GLContextSwitch::GLContextSwitch() = default; - -GLContextSwitchManager::GLContextSwitch::~GLContextSwitch(){}; - -GLContextSwitchManager::GLContextSwitchPureResult::GLContextSwitchPureResult( - bool switch_result) - : switch_result_(switch_result){}; - -GLContextSwitchManager::GLContextSwitchPureResult:: - ~GLContextSwitchPureResult() = default; - -bool GLContextSwitchManager::GLContextSwitchPureResult::GetSwitchResult() { - return switch_result_; -} - -} // namespace flutter diff --git a/shell/common/gl_context_switch_manager.h b/shell/common/gl_context_switch_manager.h deleted file mode 100644 index 90b27b4232682..0000000000000 --- a/shell/common/gl_context_switch_manager.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ - -#include -#include "flutter/fml/macros.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// Manages `GLContextSwitch`. -/// -/// Should be subclassed for platforms that uses GL and requires context -/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the -/// `GLContextSwitchManager` to set gl contexts. -/// -class GLContextSwitchManager { - public: - //------------------------------------------------------------------------------ - /// Switches the gl context to the flutter's contexts. - /// - /// Should be subclassed for each platform embedder that uses GL. - /// In construction, it should set the current context to a flutter's context - /// In destruction, it should rest the current context. - /// - class GLContextSwitch { - public: - GLContextSwitch(); - - virtual ~GLContextSwitch(); - - virtual bool GetSwitchResult() = 0; - - FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitch); - }; - - GLContextSwitchManager(); - ~GLContextSwitchManager(); - - //---------------------------------------------------------------------------- - /// @brief Creates a shell instance using the provided settings. The - /// callbacks to create the various shell subcomponents will be - /// called on the appropriate threads before this method returns. - /// If this is the first instance of a shell in the process, this - /// call also bootstraps the Dart VM. - /// - /// @param[in] task_runners The task runners - /// @param[in] settings The settings - /// @param[in] on_create_platform_view The callback that must return a - /// platform view. This will be called on - /// the platform task runner before this - /// method returns. - /// @param[in] on_create_rasterizer That callback that must provide a - /// valid rasterizer. This will be called - /// on the render task runner before this - /// method returns. - /// - /// @return A full initialized shell if the settings and callbacks are - /// valid. The root isolate has been created but not yet launched. - /// It may be launched by obtaining the engine weak pointer and - /// posting a task onto the UI task runner with a valid run - /// configuration to run the isolate. The embedder must always - /// check the validity of the shell (using the IsSetup call) - /// immediately after getting a pointer to it. - /// - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's context as current context. - /// - /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if - /// the setting process is succesful. - virtual std::unique_ptr MakeCurrent() = 0; - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's resources context as current context. - /// - /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if - /// the setting process is succesful. - virtual std::unique_ptr ResourceMakeCurrent() = 0; - - //------------------------------------------------------------------------------ - /// A representation of a `GLContextSwitch` that doesn't require actual - /// context switching. - /// - class GLContextSwitchPureResult final : public GLContextSwitch { - public: - // Constructor that creates an `GLContextSwitchPureResult`. - // The `GetSwitchResult` will return the same value as `switch_result`. - - //---------------------------------------------------------------------------- - /// @brief Constructs a `GLContextSwitchPureResult`. - /// - /// @param[in] switch_result the switch result that will be returned - /// in `GetSwitchResult()` - /// - GLContextSwitchPureResult(bool switch_result); - - ~GLContextSwitchPureResult(); - - bool GetSwitchResult() override; - - private: - bool switch_result_; - - FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchPureResult); - }; - - FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchManager); -}; - -} // namespace flutter - -#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 7c0785ab9d2e4..51370d082862b 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,10 +355,8 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -std::unique_ptr -ShellTestPlatformView::GLContextMakeCurrent() { - return std::make_unique( - gl_surface_.MakeCurrent()); +bool ShellTestPlatformView::GLContextMakeCurrent() { + return gl_surface_.MakeCurrent(); } // |GPUSurfaceGLDelegate| @@ -389,10 +387,5 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } -std::shared_ptr -ShellTestPlatformView::GetGLContextSwitchManager() { - return nullptr; -} - } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 7d1d442dd8533..fdee9653b71ce 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,8 +144,7 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -162,8 +161,6 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - std::shared_ptr GetGLContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index 392a8cad54e3e..b8a77ca1811d4 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,10 +60,8 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -std::unique_ptr -Surface::MakeRenderContextCurrent() { - return std::make_unique( - true); +bool Surface::MakeRenderContextCurrent() { + return true; } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index d34d4dae93e04..7bbc16e24c690 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,7 +10,6 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -59,8 +58,7 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual std::unique_ptr - MakeRenderContextCurrent(); + virtual bool MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index b69475aefd6ff..5f30a48375d33 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -8,7 +8,6 @@ #include "flutter/fml/logging.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" @@ -40,10 +39,7 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -91,6 +87,8 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; + + delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -100,9 +98,7 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -118,9 +114,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -131,6 +126,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; + + delegate_->GLContextClearCurrent(); } // |Surface| @@ -256,9 +253,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -290,9 +285,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - std::unique_ptr result = - std::make_unique(surface, submit_callback); - return result; + return std::make_unique(surface, submit_callback); } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -300,8 +293,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -338,6 +329,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } + return true; } @@ -368,8 +360,7 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -std::unique_ptr -GPUSurfaceGL::MakeRenderContextCurrent() { +bool GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 953ebf0872161..97325569bfd16 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,8 +44,7 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - std::unique_ptr - MakeRenderContextCurrent() override; + bool MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index fe915679e55f5..dfe0ce7f468db 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,7 +7,6 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -17,8 +16,7 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual std::unique_ptr - GLContextMakeCurrent() = 0; + virtual bool GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -61,9 +59,6 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; - - virtual std::shared_ptr - GetGLContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index f1a5edbe774ad..737d9f293a518 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,11 +104,9 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -std::unique_ptr -AndroidSurfaceGL::GLContextMakeCurrent() { +bool AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return std::make_unique( - onscreen_context_->MakeCurrent()); + return onscreen_context_->MakeCurrent(); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -132,10 +130,4 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -AndroidSurfaceGL::GetGLContextSwitchManager() { - return nullptr; -} - } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index e30300cf91289..d59302ad66509 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,8 +47,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -62,9 +61,6 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetGLContextSwitchManager() override; - private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 3e67beb97e717..a66c3bf48c5ae 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,8 +86,6 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", - "ios_gl_context_switch_manager.h", - "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 5bcab91e01655..33ca14d9fabea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,11 +160,6 @@ frame_size_ = frame_size; } -void FlutterPlatformViewsController::SetGLContextSwitchManager( - std::shared_ptr gl_context_guard_manager) { - gl_context_switch_manager_ = gl_context_guard_manager; -} - void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -373,10 +368,7 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - std::unique_ptr contextSwitch = - gl_context_switch_manager_->MakeCurrent(); - - EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); + EnsureOverlayInitialized(view_id, gl_context, gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -464,9 +456,6 @@ GrContext* gr_context) { FML_DCHECK(flutter_view_); - std::unique_ptr contextSwitch = - gl_context_switch_manager_->MakeCurrent(); - auto overlay_it = overlays_.find(overlay_id); if (!gr_context) { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 211f45cc73c25..c8daeaa605946 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,7 +11,6 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -81,9 +80,6 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); - void SetGLContextSwitchManager( - std::shared_ptr gl_context_guard_manager); - void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -208,8 +204,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - std::shared_ptr gl_context_switch_manager_; - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 136616e8dfc5b..232645d9c8592 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,24 +26,16 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - std::unique_ptr MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); - - std::shared_ptr GetIOSGLContextSwitchManager() { - return gl_context_switch_manager_; - } + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } - fml::scoped_nsobject GetContext() const { - return gl_context_switch_manager_->GetContext(); - } - private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; sk_sp color_space_; - std::shared_ptr gl_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 8436a5a44413f..52fb85f8f19a9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,7 +13,15 @@ namespace flutter { IOSGLContext::IOSGLContext() { - gl_context_switch_manager_ = std::make_shared(); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -40,15 +48,16 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), gl_context_switch_manager_); + return std::make_unique(std::move(layer), context_.get(), + resource_context_.get()); } -std::unique_ptr IOSGLContext::MakeCurrent() { - return gl_context_switch_manager_->MakeCurrent(); +bool IOSGLContext::MakeCurrent() { + return [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr IOSGLContext::ResourceMakeCurrent() { - return gl_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLContext::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h deleted file mode 100644 index 5d3714f326fe4..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ - -#define GLES_SILENCE_DEPRECATION - -#import -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/gl_context_switch_manager.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// The iOS implementation of `GLContextSwitchManager`. -/// -/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and -/// sets the flutter's gl context as current. -/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to -/// current. -/// -class IOSGLContextSwitchManager final : public GLContextSwitchManager { - public: - class IOSGLContextSwitch final : public GLContextSwitch { - public: - IOSGLContextSwitch(IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context); - - ~IOSGLContextSwitch(); - - bool GetSwitchResult() override; - - private: - IOSGLContextSwitchManager& manager_; - bool switch_result_; - bool has_pushed_context_; - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); - }; - - IOSGLContextSwitchManager(); - - ~IOSGLContextSwitchManager(); - - std::unique_ptr MakeCurrent() override; - std::unique_ptr ResourceMakeCurrent() override; - - fml::scoped_nsobject GetContext(); - - private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; - fml::scoped_nsobject stored_; - - bool PushContext(fml::scoped_nsobject context); - void PopContext(); - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); -}; - -} - -#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm deleted file mode 100644 index c6b332f121b46..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ios_gl_context_switch_manager.h" - -namespace flutter { - -IOSGLContextSwitchManager::IOSGLContextSwitchManager() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } -}; - -IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; - -std::unique_ptr IOSGLContextSwitchManager::MakeCurrent() { - return std::make_unique(*this, context_); -} - -std::unique_ptr -IOSGLContextSwitchManager::ResourceMakeCurrent() { - return std::make_unique(*this, resource_context_); -} - -fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { - return context_; -} - -bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { - EAGLContext* current = [EAGLContext currentContext]; - if (current == nil) { - [stored_.get() addObject:[NSNull null]]; - } else { - [stored_.get() addObject:current]; - } - bool result = [EAGLContext setCurrentContext:context.get()]; - return result; -} - -void IOSGLContextSwitchManager::PopContext() { - EAGLContext* last = [stored_.get() lastObject]; - [stored_.get() removeLastObject]; - if ([last isEqual:[NSNull null]]) { - [EAGLContext setCurrentContext:nil]; - return; - } - [EAGLContext setCurrentContext:last]; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( - IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context) - : manager_(manager) { - bool result = manager_.PushContext(context); - has_pushed_context_ = true; - switch_result_ = result; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { - if (!has_pushed_context_) { - return; - } - manager_.PopContext(); -} - -bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { - return switch_result_; -} -} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index 10428896b63a0..b2eafe16e0950 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,15 +13,14 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager); + IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context); ~IOSGLRenderTarget(); @@ -33,16 +32,16 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - std::unique_ptr MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - std::shared_ptr gl_context_switch_manager_; + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a3581e77198a8..a57ba9c46b414 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,20 +12,22 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager) +IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context) : layer_(std::move(layer)), - gl_context_switch_manager_(gl_context_guard_manager), + context_([context retain]), + resource_context_([resource_context retain]), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - std::unique_ptr context_switch = - gl_context_switch_manager_->MakeCurrent(); - bool context_current = context_switch->GetSwitchResult(); + FML_DCHECK(context_ != nullptr); + FML_DCHECK(resource_context_ != nullptr); + + bool context_current = [EAGLContext setCurrentContext:context_]; FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -60,8 +62,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - std::unique_ptr context_switch = - gl_context_switch_manager_->MakeCurrent(); + EAGLContext* context = EAGLContext.currentContext; + [EAGLContext setCurrentContext:context_]; FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -69,6 +71,7 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); + [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -101,9 +104,8 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - std::unique_ptr context_switch = - gl_context_switch_manager_->MakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (![EAGLContext setCurrentContext:context_]) { return false; } @@ -114,8 +116,7 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![gl_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER - fromDrawable:layer_.get()]) { + if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { return false; } @@ -131,16 +132,12 @@ return true; } -std::unique_ptr IOSGLRenderTarget::MakeCurrent() { - bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); - if (!isUpdateSuccessful) { - return std::make_unique(false); - } - return gl_context_switch_manager_->MakeCurrent(); +bool IOSGLRenderTarget::MakeCurrent() { + return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr IOSGLRenderTarget::ResourceMakeCurrent() { - return gl_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLRenderTarget::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 13253e3eb0bdb..49f40f9eec76a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,7 +12,6 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -28,8 +27,7 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual std::unique_ptr - ResourceContextMakeCurrent() = 0; + virtual bool ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 8bee87fbf42b1..c1019bb442bb0 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,7 +9,6 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -33,7 +32,7 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -41,7 +40,7 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - std::unique_ptr GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -54,9 +53,6 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetGLContextSwitchManager() override; - // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index de114d1265278..48e70e00a4e7a 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,8 +28,7 @@ return render_target_->IsValid(); } -std::unique_ptr -IOSSurfaceGL::ResourceContextMakeCurrent() { +bool IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -57,13 +56,11 @@ return true; } -std::unique_ptr IOSSurfaceGL::GLContextMakeCurrent() { +bool IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - NSLog(@"not valid"); - return std::make_unique(false); + return false; } - NSLog(@"valid"); - return render_target_->MakeCurrent(); + return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -76,11 +73,6 @@ return IsValid() && render_target_->PresentRenderBuffer(); } -// |GPUSurfaceGLDelegate| -std::shared_ptr IOSSurfaceGL::GetGLContextSwitchManager() { - return context_->GetIOSGLContextSwitchManager(); -} - // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -149,7 +141,6 @@ // |ExternalViewEmbedder| bool IOSSurfaceGL::SubmitFrame(GrContext* context) { FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); - platform_views_controller->SetGLContextSwitchManager(context_->GetIOSGLContextSwitchManager()); if (platform_views_controller == nullptr) { return true; } diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index 5feb1363c1e5c..daac2ffc77231 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" + @class CALayer; namespace flutter { @@ -28,7 +28,7 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 1c87b33e6e92a..ab5490cf25140 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,9 +27,8 @@ return layer_; } -std::unique_ptr -IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return std::make_unique(false); +bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return false; } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 1983eb7371383..bb37fa9610b2b 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,18 +107,15 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (gl_context_ != nullptr) { - std::unique_ptr context_switch = - gl_context_->ResourceMakeCurrent(); - if (context_switch->GetSwitchResult()) { - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); - } + if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 59566e844d42b..d37b03aae8d9e 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,10 +34,8 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -std::unique_ptr -EmbedderSurfaceGL::GLContextMakeCurrent() { - return std::make_unique( - gl_dispatch_table_.gl_make_current_callback()); +bool EmbedderSurfaceGL::GLContextMakeCurrent() { + return gl_dispatch_table_.gl_make_current_callback(); } // |GPUSurfaceGLDelegate| @@ -81,12 +79,6 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -EmbedderSurfaceGL::GetGLContextSwitchManager() { - return nullptr; -} - // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index f810597111f1f..a01fa05d4e62c 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,8 +50,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -74,9 +73,6 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetGLContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index dd2238aba3181..e667c4c88678d 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,8 +40,6 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -138,9 +136,6 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -207,8 +202,6 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -246,7 +239,6 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -406,7 +398,6 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -427,7 +418,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -502,7 +492,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -555,7 +545,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -571,7 +561,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -595,7 +584,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index cd1c5b7366a89..87799ad5e6434 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,15 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + + + + + * registrar = - [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; - [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; - self.window.rootViewController = flutterViewController; -} - @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h deleted file mode 100644 index 19cefe7025986..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GLTestPlatformView : NSObject - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; - -- (UIView*)view; - -@end - -@interface GLTestPlatformViewFactory : NSObject - -- (instancetype)initWithMessenger:(NSObject*)messenger; - -@end - -@interface GLTestView : UIView - -@end - -NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m deleted file mode 100644 index 8142550e52863..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "GLTestPlatformView.h" - -#define GLES_SILENCE_DEPRECATION - -@implementation GLTestPlatformView { - int64_t _viewId; - GLTestView* _view; -} - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id)args - binaryMessenger:(NSObject*)messenger { - if ([super init]) { - _viewId = viewId; - _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; - } - return self; -} - -- (UIView*)view { - return _view; -} - -@end - -@implementation GLTestPlatformViewFactory { - NSObject* _messenger; -} - -- (instancetype)initWithMessenger:(NSObject*)messenger { - self = [super init]; - if (self) { - _messenger = messenger; - } - return self; -} - -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger]; - return platformView; -} - -- (NSObject*)createArgsCodec { - return [FlutterStringCodec sharedInstance]; -} - -@end - -@interface GLTestView () - -@property(strong, nonatomic) EAGLContext* context; - -@end - -@implementation GLTestView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - _context.debugLabel = @"platform view context"; - [EAGLContext setCurrentContext:_context]; - self.backgroundColor = [UIColor redColor]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self checkEAGLContext]; - }); - } - return self; -} - -- (void)checkEAGLContext { - if ([EAGLContext currentContext] != _context) { - self.accessibilityIdentifier = @"gl_platformview_wrong_context"; - } else { - self.accessibilityIdentifier = @"gl_platformview_correct_context"; - } -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m deleted file mode 100644 index 000a7e578fc90..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface PlatformViewGLTests : XCTestCase - -@property(nonatomic, strong) XCUIApplication* application; - -@end - -@implementation PlatformViewGLTests - -- (void)setUp { - self.continueAfterFailure = NO; - - self.application = [[XCUIApplication alloc] init]; - self.application.launchArguments = @[ @"--platform-view-gl" ]; - [self.application launch]; -} - -- (void)testExample { - NSPredicate* predicateToFindPlatformView = - [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, - NSDictionary* _Nullable bindings) { - XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || - [element.identifier isEqualToString:@"gl_platformview_correct_context"]; - }]; - XCUIElement* firstElement = - [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; - if (![firstElement waitForExistenceWithTimeout:30]) { - NSLog(@"%@", self.application.debugDescription); - XCTFail(@"Failed due to not able to find platform view with 30 seconds"); - } - XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); -} - -@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 82b589a6681fe..7a264b70008f0 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,7 +25,6 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), - 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 86fb4df1df449..05efe52fa8b25 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -281,24 +281,6 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } -/// Platform view scenario for testing EAGLContext on iOS. -class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Constructs a platform view to test EAGLContext on iOS. - PlatformViewGLScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - finishBuilderByAddingPlatformViewAndPicture(builder, 6); - } -} - mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -307,7 +289,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id, String viewType) { + void createPlatformView(Window window, String text, int id) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -331,8 +313,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - viewType.length, - ...utf8.encode(viewType), + 'scenarios/textPlatformView'.length, + ...utf8.encode('scenarios/textPlatformView'), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 9620273f05513813c8cfd4f7b90dc776cc55e327 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 15:16:52 -0500 Subject: [PATCH 062/591] Roll src/third_party/skia 8c1e265f6f81..c88d1774ed50 (7 commits) (#13754) https://skia.googlesource.com/skia.git/+log/8c1e265f6f81..c88d1774ed50 git log 8c1e265f6f81..c88d1774ed50 --date=short --no-merges --format='%ad %ae %s' 2019-11-08 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-08 mtklein@google.com add SkColorTypeIsNormalized() 2019-11-08 jlavrova@google.com Font resolution: all unit tests working 2019-11-08 kjlubick@google.com [canvaskit] Expand SkAnimatedImage 2019-11-08 kjlubick@google.com [canvaskit] Fix null dereference when GrContext cannot be made 2019-11-08 egdaniel@google.com Handle failure to create VkRenderPasses in vulkan backend. 2019-11-08 mtklein@google.com Pre-flight SkTLS_pthread changes for C++17 Created with: gclient setdep -r src/third_party/skia@c88d1774ed50 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 80a0dfa731c91..22f5c060f4022 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8c1e265f6f8145ec0638fc78d4821bde8e7956c5', + 'skia_revision': 'c88d1774ed5074c8cd035a279bb6f599139e7484', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a6b58d2c2e5ea..759edca261f3b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: eb6c9ef1aad6e6b86a2077d692c5bb42 +Signature: 76b5c1ee75cb1ba1b7cf328576dc0703 UNUSED LICENSES: @@ -1724,9 +1724,9 @@ FILE: ../../../third_party/skia/modules/skparagraph/include/TextShadow.h FILE: ../../../third_party/skia/modules/skparagraph/include/TextStyle.h FILE: ../../../third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h FILE: ../../../third_party/skia/modules/skparagraph/src/FontCollection.cpp -FILE: ../../../third_party/skia/modules/skparagraph/src/FontResolver.cpp -FILE: ../../../third_party/skia/modules/skparagraph/src/FontResolver.h FILE: ../../../third_party/skia/modules/skparagraph/src/Iterators.h +FILE: ../../../third_party/skia/modules/skparagraph/src/OneLineShaper.cpp +FILE: ../../../third_party/skia/modules/skparagraph/src/OneLineShaper.h FILE: ../../../third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp FILE: ../../../third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.h FILE: ../../../third_party/skia/modules/skparagraph/src/ParagraphCache.cpp @@ -1742,6 +1742,7 @@ FILE: ../../../third_party/skia/modules/skparagraph/src/TextStyle.cpp FILE: ../../../third_party/skia/modules/skparagraph/src/TextWrapper.cpp FILE: ../../../third_party/skia/modules/skparagraph/src/TextWrapper.h FILE: ../../../third_party/skia/modules/skparagraph/src/TypefaceFontProvider.cpp +FILE: ../../../third_party/skia/modules/skparagraph/test.html FILE: ../../../third_party/skia/modules/skparagraph/utils/TestFontCollection.cpp FILE: ../../../third_party/skia/modules/skparagraph/utils/TestFontCollection.h FILE: ../../../third_party/skia/public.bzl From f7e73b62360be30b87c99041b03ac478f69d8a74 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 8 Nov 2019 12:21:46 -0800 Subject: [PATCH 063/591] Move TextRange from the framework to dart:ui. (#13747) This removes TextRange from the framework and moves it to the engine, in preparation for using it to return text ranges from the text extent APIs, like Paragraph.getWordBoundary instead of a List. Also added new tests for TextRange. --- lib/ui/text.dart | 91 ++++++++++++++++++++++++++++++++- lib/web_ui/lib/src/ui/text.dart | 84 ++++++++++++++++++++++++++++++ lib/web_ui/test/text_test.dart | 55 ++++++++++++++++++++ testing/dart/text_test.dart | 55 ++++++++++++++++++++ 4 files changed, 283 insertions(+), 2 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 50a520ec14e4b..94a88b0cb32c0 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1406,6 +1406,93 @@ class TextPosition { } } +/// A range of characters in a string of text. +class TextRange { + /// Creates a text range. + /// + /// The [start] and [end] arguments must not be null. Both the [start] and + /// [end] must either be greater than or equal to zero or both exactly -1. + /// + /// The text included in the range includes the character at [start], but not + /// the one at [end]. + /// + /// Instead of creating an empty text range, consider using the [empty] + /// constant. + const TextRange({ + this.start, + this.end, + }) : assert(start != null && start >= -1), + assert(end != null && end >= -1); + + /// A text range that starts and ends at offset. + /// + /// The [offset] argument must be non-null and greater than or equal to -1. + const TextRange.collapsed(int offset) + : assert(offset != null && offset >= -1), + start = offset, + end = offset; + + /// A text range that contains nothing and is not in the text. + static const TextRange empty = TextRange(start: -1, end: -1); + + /// The index of the first character in the range. + /// + /// If [start] and [end] are both -1, the text range is empty. + final int start; + + /// The next index after the characters in this range. + /// + /// If [start] and [end] are both -1, the text range is empty. + final int end; + + /// Whether this range represents a valid position in the text. + bool get isValid => start >= 0 && end >= 0; + + /// Whether this range is empty (but still potentially placed inside the text). + bool get isCollapsed => start == end; + + /// Whether the start of this range precedes the end. + bool get isNormalized => end >= start; + + /// The text before this range. + String textBefore(String text) { + assert(isNormalized); + return text.substring(0, start); + } + + /// The text after this range. + String textAfter(String text) { + assert(isNormalized); + return text.substring(end); + } + + /// The text inside this range. + String textInside(String text) { + assert(isNormalized); + return text.substring(start, end); + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other is! TextRange) + return false; + final TextRange typedOther = other; + return typedOther.start == start + && typedOther.end == end; + } + + @override + int get hashCode => hashValues( + start.hashCode, + end.hashCode, + ); + + @override + String toString() => 'TextRange(start: $start, end: $end)'; +} + /// Layout constraints for [Paragraph] objects. /// /// Instances of this class are typically used with [Paragraph.layout]. @@ -1512,8 +1599,8 @@ enum BoxHeightStyle { /// Defines various ways to horizontally bound the boxes returned by /// [Paragraph.getBoxesForRange]. enum BoxWidthStyle { - // Provide tight bounding boxes that fit widths to the runs of each line - // independently. + /// Provide tight bounding boxes that fit widths to the runs of each line + /// independently. tight, /// Adds up to two additional boxes as needed at the beginning and/or end diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index a42b7e7edc3ce..10d9f760c1aba 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -906,6 +906,90 @@ class TextPosition { } } +/// A range of characters in a string of text. +class TextRange { + /// Creates a text range. + /// + /// The [start] and [end] arguments must not be null. Both the [start] and + /// [end] must either be greater than or equal to zero or both exactly -1. + /// + /// Instead of creating an empty text range, consider using the [empty] + /// constant. + const TextRange({ + this.start, + this.end, + }) : assert(start != null && start >= -1), + assert(end != null && end >= -1); + + /// A text range that starts and ends at offset. + /// + /// The [offset] argument must be non-null and greater than or equal to -1. + const TextRange.collapsed(int offset) + : assert(offset != null && offset >= -1), + start = offset, + end = offset; + + /// A text range that contains nothing and is not in the text. + static const TextRange empty = TextRange(start: -1, end: -1); + + /// The index of the first character in the range. + /// + /// If [start] and [end] are both -1, the text range is empty. + final int start; + + /// The next index after the characters in this range. + /// + /// If [start] and [end] are both -1, the text range is empty. + final int end; + + /// Whether this range represents a valid position in the text. + bool get isValid => start >= 0 && end >= 0; + + /// Whether this range is empty (but still potentially placed inside the text). + bool get isCollapsed => start == end; + + /// Whether the start of this range precedes the end. + bool get isNormalized => end >= start; + + /// The text before this range. + String textBefore(String text) { + assert(isNormalized); + return text.substring(0, start); + } + + /// The text after this range. + String textAfter(String text) { + assert(isNormalized); + return text.substring(end); + } + + /// The text inside this range. + String textInside(String text) { + assert(isNormalized); + return text.substring(start, end); + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other is! TextRange) + return false; + final TextRange typedOther = other; + return typedOther.start == start + && typedOther.end == end; + } + + @override + int get hashCode => hashValues( + start.hashCode, + end.hashCode, + ); + + @override + String toString() => 'TextRange(start: $start, end: $end)'; +} + /// Layout constraints for [Paragraph] objects. /// /// Instances of this class are typically used with [Paragraph.layout]. diff --git a/lib/web_ui/test/text_test.dart b/lib/web_ui/test/text_test.dart index 16dfa4c64fd6d..eecebd711ed06 100644 --- a/lib/web_ui/test/text_test.dart +++ b/lib/web_ui/test/text_test.dart @@ -262,4 +262,59 @@ void main() async { debugEmulateFlutterTesterEnvironment = true; }); + group('TextRange', () { + test('empty ranges are correct', () { + const TextRange range = TextRange(start: -1, end: -1); + expect(range, equals(const TextRange.collapsed(-1))); + expect(range, equals(TextRange.empty)); + }); + test('isValid works', () { + expect(TextRange.empty.isValid, isFalse); + expect(const TextRange(start: 0, end: 0).isValid, isTrue); + expect(const TextRange(start: 0, end: 10).isValid, isTrue); + expect(const TextRange(start: 10, end: 10).isValid, isTrue); + expect(const TextRange(start: -1, end: 10).isValid, isFalse); + expect(const TextRange(start: 10, end: 0).isValid, isTrue); + expect(const TextRange(start: 10, end: -1).isValid, isFalse); + }); + test('isCollapsed works', () { + expect(TextRange.empty.isCollapsed, isTrue); + expect(const TextRange(start: 0, end: 0).isCollapsed, isTrue); + expect(const TextRange(start: 0, end: 10).isCollapsed, isFalse); + expect(const TextRange(start: 10, end: 10).isCollapsed, isTrue); + expect(const TextRange(start: -1, end: 10).isCollapsed, isFalse); + expect(const TextRange(start: 10, end: 0).isCollapsed, isFalse); + expect(const TextRange(start: 10, end: -1).isCollapsed, isFalse); + }); + test('isNormalized works', () { + expect(TextRange.empty.isNormalized, isTrue); + expect(const TextRange(start: 0, end: 0).isNormalized, isTrue); + expect(const TextRange(start: 0, end: 10).isNormalized, isTrue); + expect(const TextRange(start: 10, end: 10).isNormalized, isTrue); + expect(const TextRange(start: -1, end: 10).isNormalized, isTrue); + expect(const TextRange(start: 10, end: 0).isNormalized, isFalse); + expect(const TextRange(start: 10, end: -1).isNormalized, isFalse); + }); + test('textBefore works', () { + expect(const TextRange(start: 0, end: 0).textBefore('hello'), isEmpty); + expect(const TextRange(start: 1, end: 1).textBefore('hello'), equals('h')); + expect(const TextRange(start: 1, end: 2).textBefore('hello'), equals('h')); + expect(const TextRange(start: 5, end: 5).textBefore('hello'), equals('hello')); + expect(const TextRange(start: 0, end: 5).textBefore('hello'), isEmpty); + }); + test('textAfter works', () { + expect(const TextRange(start: 0, end: 0).textAfter('hello'), equals('hello')); + expect(const TextRange(start: 1, end: 1).textAfter('hello'), equals('ello')); + expect(const TextRange(start: 1, end: 2).textAfter('hello'), equals('llo')); + expect(const TextRange(start: 5, end: 5).textAfter('hello'), isEmpty); + expect(const TextRange(start: 0, end: 5).textAfter('hello'), isEmpty); + }); + test('textInside works', () { + expect(const TextRange(start: 0, end: 0).textInside('hello'), isEmpty); + expect(const TextRange(start: 1, end: 1).textInside('hello'), isEmpty); + expect(const TextRange(start: 1, end: 2).textInside('hello'), equals('e')); + expect(const TextRange(start: 5, end: 5).textInside('hello'), isEmpty); + expect(const TextRange(start: 0, end: 5).textInside('hello'), equals('hello')); + }); + }); } diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index fb8b13bbbe362..d68bc0033881a 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -24,4 +24,59 @@ void main() { expect(FontWeight.lerp(FontWeight.w400, null, 1), equals(FontWeight.w400)); }); }); + group('TextRange', () { + test('empty ranges are correct', () { + const TextRange range = TextRange(start: -1, end: -1); + expect(range, equals(const TextRange.collapsed(-1))); + expect(range, equals(TextRange.empty)); + }); + test('isValid works', () { + expect(TextRange.empty.isValid, isFalse); + expect(const TextRange(start: 0, end: 0).isValid, isTrue); + expect(const TextRange(start: 0, end: 10).isValid, isTrue); + expect(const TextRange(start: 10, end: 10).isValid, isTrue); + expect(const TextRange(start: -1, end: 10).isValid, isFalse); + expect(const TextRange(start: 10, end: 0).isValid, isTrue); + expect(const TextRange(start: 10, end: -1).isValid, isFalse); + }); + test('isCollapsed works', () { + expect(TextRange.empty.isCollapsed, isTrue); + expect(const TextRange(start: 0, end: 0).isCollapsed, isTrue); + expect(const TextRange(start: 0, end: 10).isCollapsed, isFalse); + expect(const TextRange(start: 10, end: 10).isCollapsed, isTrue); + expect(const TextRange(start: -1, end: 10).isCollapsed, isFalse); + expect(const TextRange(start: 10, end: 0).isCollapsed, isFalse); + expect(const TextRange(start: 10, end: -1).isCollapsed, isFalse); + }); + test('isNormalized works', () { + expect(TextRange.empty.isNormalized, isTrue); + expect(const TextRange(start: 0, end: 0).isNormalized, isTrue); + expect(const TextRange(start: 0, end: 10).isNormalized, isTrue); + expect(const TextRange(start: 10, end: 10).isNormalized, isTrue); + expect(const TextRange(start: -1, end: 10).isNormalized, isTrue); + expect(const TextRange(start: 10, end: 0).isNormalized, isFalse); + expect(const TextRange(start: 10, end: -1).isNormalized, isFalse); + }); + test('textBefore works', () { + expect(const TextRange(start: 0, end: 0).textBefore('hello'), isEmpty); + expect(const TextRange(start: 1, end: 1).textBefore('hello'), equals('h')); + expect(const TextRange(start: 1, end: 2).textBefore('hello'), equals('h')); + expect(const TextRange(start: 5, end: 5).textBefore('hello'), equals('hello')); + expect(const TextRange(start: 0, end: 5).textBefore('hello'), isEmpty); + }); + test('textAfter works', () { + expect(const TextRange(start: 0, end: 0).textAfter('hello'), equals('hello')); + expect(const TextRange(start: 1, end: 1).textAfter('hello'), equals('ello')); + expect(const TextRange(start: 1, end: 2).textAfter('hello'), equals('llo')); + expect(const TextRange(start: 5, end: 5).textAfter('hello'), isEmpty); + expect(const TextRange(start: 0, end: 5).textAfter('hello'), isEmpty); + }); + test('textInside works', () { + expect(const TextRange(start: 0, end: 0).textInside('hello'), isEmpty); + expect(const TextRange(start: 1, end: 1).textInside('hello'), isEmpty); + expect(const TextRange(start: 1, end: 2).textInside('hello'), equals('e')); + expect(const TextRange(start: 5, end: 5).textInside('hello'), isEmpty); + expect(const TextRange(start: 0, end: 5).textInside('hello'), equals('hello')); + }); + }); } From 618e6666ced77bf497311876fbe968c6b9d72041 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Nov 2019 12:26:48 -0800 Subject: [PATCH 064/591] Reland "Guarding EAGLContext used by Flutter #13314" (#13755) --- ci/licenses_golden/licenses_flutter | 4 + shell/common/BUILD.gn | 2 + shell/common/gl_context_switch_manager.cc | 28 +++++ shell/common/gl_context_switch_manager.h | 116 ++++++++++++++++++ shell/common/shell_test.cc | 11 +- shell/common/shell_test.h | 5 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 +++-- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/platform/android/android_surface_gl.cc | 12 +- shell/platform/android/android_surface_gl.h | 6 +- shell/platform/darwin/ios/BUILD.gn | 2 + .../framework/Source/FlutterPlatformViews.mm | 16 ++- .../Source/FlutterPlatformViews_Internal.h | 6 + shell/platform/darwin/ios/ios_gl_context.h | 16 ++- shell/platform/darwin/ios/ios_gl_context.mm | 21 +--- .../ios/ios_gl_context_switch_manager.h | 65 ++++++++++ .../ios/ios_gl_context_switch_manager.mm | 78 ++++++++++++ .../darwin/ios/ios_gl_render_target.h | 15 +-- .../darwin/ios/ios_gl_render_target.mm | 41 ++++--- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 8 +- shell/platform/darwin/ios/ios_surface_gl.mm | 16 ++- .../darwin/ios/ios_surface_software.h | 4 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 +-- .../platform/embedder/embedder_surface_gl.cc | 12 +- shell/platform/embedder/embedder_surface_gl.h | 6 +- .../Scenarios.xcodeproj/project.pbxproj | 16 ++- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ++++ .../Scenarios/Scenarios/GLTestPlatformView.h | 30 +++++ .../Scenarios/Scenarios/GLTestPlatformView.m | 90 ++++++++++++++ .../ScenariosUITests/PlatformViewGLTests.m | 39 ++++++ testing/scenario_app/lib/main.dart | 1 + .../scenario_app/lib/src/platform_view.dart | 36 ++++-- testing/scenario_app/lib/src/texture.dart | 0 39 files changed, 713 insertions(+), 116 deletions(-) create mode 100644 shell/common/gl_context_switch_manager.cc create mode 100644 shell/common/gl_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h create mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m create mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4e36d64a55529..e54ec6404291c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -495,6 +495,8 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/fixtures/shell_test.dart +FILE: ../../../flutter/shell/common/gl_context_switch_manager.cc +FILE: ../../../flutter/shell/common/gl_context_switch_manager.h FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h @@ -799,6 +801,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f93bca63478f0..50f1defa57f8d 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -66,6 +66,8 @@ source_set("common") { "canvas_spy.h", "engine.cc", "engine.h", + "gl_context_switch_manager.cc", + "gl_context_switch_manager.h", "isolate_configuration.cc", "isolate_configuration.h", "persistent_cache.cc", diff --git a/shell/common/gl_context_switch_manager.cc b/shell/common/gl_context_switch_manager.cc new file mode 100644 index 0000000000000..37c12e885b3f3 --- /dev/null +++ b/shell/common/gl_context_switch_manager.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gl_context_switch_manager.h" + +namespace flutter { + +GLContextSwitchManager::GLContextSwitchManager() = default; + +GLContextSwitchManager::~GLContextSwitchManager() = default; + +GLContextSwitchManager::GLContextSwitch::GLContextSwitch() = default; + +GLContextSwitchManager::GLContextSwitch::~GLContextSwitch(){}; + +GLContextSwitchManager::GLContextSwitchPureResult::GLContextSwitchPureResult( + bool switch_result) + : switch_result_(switch_result){}; + +GLContextSwitchManager::GLContextSwitchPureResult:: + ~GLContextSwitchPureResult() = default; + +bool GLContextSwitchManager::GLContextSwitchPureResult::GetSwitchResult() { + return switch_result_; +} + +} // namespace flutter diff --git a/shell/common/gl_context_switch_manager.h b/shell/common/gl_context_switch_manager.h new file mode 100644 index 0000000000000..90b27b4232682 --- /dev/null +++ b/shell/common/gl_context_switch_manager.h @@ -0,0 +1,116 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ + +#include +#include "flutter/fml/macros.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// Manages `GLContextSwitch`. +/// +/// Should be subclassed for platforms that uses GL and requires context +/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the +/// `GLContextSwitchManager` to set gl contexts. +/// +class GLContextSwitchManager { + public: + //------------------------------------------------------------------------------ + /// Switches the gl context to the flutter's contexts. + /// + /// Should be subclassed for each platform embedder that uses GL. + /// In construction, it should set the current context to a flutter's context + /// In destruction, it should rest the current context. + /// + class GLContextSwitch { + public: + GLContextSwitch(); + + virtual ~GLContextSwitch(); + + virtual bool GetSwitchResult() = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitch); + }; + + GLContextSwitchManager(); + ~GLContextSwitchManager(); + + //---------------------------------------------------------------------------- + /// @brief Creates a shell instance using the provided settings. The + /// callbacks to create the various shell subcomponents will be + /// called on the appropriate threads before this method returns. + /// If this is the first instance of a shell in the process, this + /// call also bootstraps the Dart VM. + /// + /// @param[in] task_runners The task runners + /// @param[in] settings The settings + /// @param[in] on_create_platform_view The callback that must return a + /// platform view. This will be called on + /// the platform task runner before this + /// method returns. + /// @param[in] on_create_rasterizer That callback that must provide a + /// valid rasterizer. This will be called + /// on the render task runner before this + /// method returns. + /// + /// @return A full initialized shell if the settings and callbacks are + /// valid. The root isolate has been created but not yet launched. + /// It may be launched by obtaining the engine weak pointer and + /// posting a task onto the UI task runner with a valid run + /// configuration to run the isolate. The embedder must always + /// check the validity of the shell (using the IsSetup call) + /// immediately after getting a pointer to it. + /// + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's context as current context. + /// + /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if + /// the setting process is succesful. + virtual std::unique_ptr MakeCurrent() = 0; + + //---------------------------------------------------------------------------- + /// @brief Make the flutter's resources context as current context. + /// + /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if + /// the setting process is succesful. + virtual std::unique_ptr ResourceMakeCurrent() = 0; + + //------------------------------------------------------------------------------ + /// A representation of a `GLContextSwitch` that doesn't require actual + /// context switching. + /// + class GLContextSwitchPureResult final : public GLContextSwitch { + public: + // Constructor that creates an `GLContextSwitchPureResult`. + // The `GetSwitchResult` will return the same value as `switch_result`. + + //---------------------------------------------------------------------------- + /// @brief Constructs a `GLContextSwitchPureResult`. + /// + /// @param[in] switch_result the switch result that will be returned + /// in `GetSwitchResult()` + /// + GLContextSwitchPureResult(bool switch_result); + + ~GLContextSwitchPureResult(); + + bool GetSwitchResult() override; + + private: + bool switch_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchPureResult); + }; + + FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchManager); +}; + +} // namespace flutter + +#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 51370d082862b..7c0785ab9d2e4 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,8 +355,10 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -bool ShellTestPlatformView::GLContextMakeCurrent() { - return gl_surface_.MakeCurrent(); +std::unique_ptr +ShellTestPlatformView::GLContextMakeCurrent() { + return std::make_unique( + gl_surface_.MakeCurrent()); } // |GPUSurfaceGLDelegate| @@ -387,5 +389,10 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } +std::shared_ptr +ShellTestPlatformView::GetGLContextSwitchManager() { + return nullptr; +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index fdee9653b71ce..7d1d442dd8533 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,7 +144,8 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -161,6 +162,8 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + std::shared_ptr GetGLContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index b8a77ca1811d4..392a8cad54e3e 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,8 +60,10 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -bool Surface::MakeRenderContextCurrent() { - return true; +std::unique_ptr +Surface::MakeRenderContextCurrent() { + return std::make_unique( + true); } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index 7bbc16e24c690..d34d4dae93e04 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,6 +10,7 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -58,7 +59,8 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual bool MakeRenderContextCurrent(); + virtual std::unique_ptr + MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5f30a48375d33..b69475aefd6ff 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" @@ -39,7 +40,10 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -87,8 +91,6 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; - - delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -98,7 +100,9 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -114,8 +118,9 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -126,8 +131,6 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; - - delegate_->GLContextClearCurrent(); } // |Surface| @@ -253,7 +256,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - if (!delegate_->GLContextMakeCurrent()) { + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); + if (!context_switch->GetSwitchResult()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -285,7 +290,9 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - return std::make_unique(surface, submit_callback); + std::unique_ptr result = + std::make_unique(surface, submit_callback); + return result; } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -293,6 +300,8 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } + std::unique_ptr context_switch = + delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -329,7 +338,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } - return true; } @@ -360,7 +368,8 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -bool GPUSurfaceGL::MakeRenderContextCurrent() { +std::unique_ptr +GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 97325569bfd16..953ebf0872161 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,7 +44,8 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - bool MakeRenderContextCurrent() override; + std::unique_ptr + MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index dfe0ce7f468db..fe915679e55f5 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,6 +7,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -16,7 +17,8 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual bool GLContextMakeCurrent() = 0; + virtual std::unique_ptr + GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -59,6 +61,9 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; + + virtual std::shared_ptr + GetGLContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 737d9f293a518..f1a5edbe774ad 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,9 +104,11 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -bool AndroidSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr +AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return onscreen_context_->MakeCurrent(); + return std::make_unique( + onscreen_context_->MakeCurrent()); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -130,4 +132,10 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +AndroidSurfaceGL::GetGLContextSwitchManager() { + return nullptr; +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index d59302ad66509..e30300cf91289 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,7 +47,8 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -61,6 +62,9 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetGLContextSwitchManager() override; + private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index a66c3bf48c5ae..3e67beb97e717 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,6 +86,8 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", + "ios_gl_context_switch_manager.h", + "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33ca14d9fabea..057ee69d50520 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,6 +160,11 @@ frame_size_ = frame_size; } +void FlutterPlatformViewsController::SetGLContextSwitchManager( + std::shared_ptr gl_context_guard_manager) { + gl_context_switch_manager_ = gl_context_guard_manager; +} + void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -368,7 +373,12 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, gl_context, gr_context); + if (gl_context_switch_manager_ != nullptr) { + std::unique_ptr contextSwitch = + gl_context_switch_manager_->MakeCurrent(); + } + + EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -455,6 +465,10 @@ std::shared_ptr gl_context, GrContext* gr_context) { FML_DCHECK(flutter_view_); + if (gl_context_switch_manager_ != nullptr) { + std::unique_ptr contextSwitch = + gl_context_switch_manager_->MakeCurrent(); + } auto overlay_it = overlays_.find(overlay_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c8daeaa605946..211f45cc73c25 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,6 +11,7 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -80,6 +81,9 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); + void SetGLContextSwitchManager( + std::shared_ptr gl_context_guard_manager); + void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -204,6 +208,8 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + std::shared_ptr gl_context_switch_manager_; + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 232645d9c8592..136616e8dfc5b 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,16 +26,24 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - bool MakeCurrent(); + std::unique_ptr MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); + + std::shared_ptr GetIOSGLContextSwitchManager() { + return gl_context_switch_manager_; + } sk_sp ColorSpace() const { return color_space_; } + fml::scoped_nsobject GetContext() const { + return gl_context_switch_manager_->GetContext(); + } + private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; sk_sp color_space_; + std::shared_ptr gl_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 52fb85f8f19a9..8436a5a44413f 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,15 +13,7 @@ namespace flutter { IOSGLContext::IOSGLContext() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } + gl_context_switch_manager_ = std::make_shared(); // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -48,16 +40,15 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), context_.get(), - resource_context_.get()); + return std::make_unique(std::move(layer), gl_context_switch_manager_); } -bool IOSGLContext::MakeCurrent() { - return [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr IOSGLContext::MakeCurrent() { + return gl_context_switch_manager_->MakeCurrent(); } -bool IOSGLContext::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr IOSGLContext::ResourceMakeCurrent() { + return gl_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h new file mode 100644 index 0000000000000..5d3714f326fe4 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ + +#define GLES_SILENCE_DEPRECATION + +#import +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/gl_context_switch_manager.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// The iOS implementation of `GLContextSwitchManager`. +/// +/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and +/// sets the flutter's gl context as current. +/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to +/// current. +/// +class IOSGLContextSwitchManager final : public GLContextSwitchManager { + public: + class IOSGLContextSwitch final : public GLContextSwitch { + public: + IOSGLContextSwitch(IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context); + + ~IOSGLContextSwitch(); + + bool GetSwitchResult() override; + + private: + IOSGLContextSwitchManager& manager_; + bool switch_result_; + bool has_pushed_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); + }; + + IOSGLContextSwitchManager(); + + ~IOSGLContextSwitchManager(); + + std::unique_ptr MakeCurrent() override; + std::unique_ptr ResourceMakeCurrent() override; + + fml::scoped_nsobject GetContext(); + + private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; + fml::scoped_nsobject stored_; + + bool PushContext(fml::scoped_nsobject context); + void PopContext(); + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); +}; + +} + +#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm new file mode 100644 index 0000000000000..c6b332f121b46 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios_gl_context_switch_manager.h" + +namespace flutter { + +IOSGLContextSwitchManager::IOSGLContextSwitchManager() { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } +}; + +IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; + +std::unique_ptr IOSGLContextSwitchManager::MakeCurrent() { + return std::make_unique(*this, context_); +} + +std::unique_ptr +IOSGLContextSwitchManager::ResourceMakeCurrent() { + return std::make_unique(*this, resource_context_); +} + +fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { + return context_; +} + +bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { + EAGLContext* current = [EAGLContext currentContext]; + if (current == nil) { + [stored_.get() addObject:[NSNull null]]; + } else { + [stored_.get() addObject:current]; + } + bool result = [EAGLContext setCurrentContext:context.get()]; + return result; +} + +void IOSGLContextSwitchManager::PopContext() { + EAGLContext* last = [stored_.get() lastObject]; + [stored_.get() removeLastObject]; + if ([last isEqual:[NSNull null]]) { + [EAGLContext setCurrentContext:nil]; + return; + } + [EAGLContext setCurrentContext:last]; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( + IOSGLContextSwitchManager& manager, + fml::scoped_nsobject context) + : manager_(manager) { + bool result = manager_.PushContext(context); + has_pushed_context_ = true; + switch_result_ = result; +} + +IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { + if (!has_pushed_context_) { + return; + } + manager_.PopContext(); +} + +bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { + return switch_result_; +} +} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index b2eafe16e0950..10428896b63a0 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,14 +13,15 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context); + IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager); ~IOSGLRenderTarget(); @@ -32,16 +33,16 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - bool MakeCurrent(); + std::unique_ptr MakeCurrent(); - bool ResourceMakeCurrent(); + std::unique_ptr + ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; + std::shared_ptr gl_context_switch_manager_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a57ba9c46b414..a3581e77198a8 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,22 +12,20 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, - EAGLContext* context, - EAGLContext* resource_context) +IOSGLRenderTarget::IOSGLRenderTarget( + fml::scoped_nsobject layer, + std::shared_ptr gl_context_guard_manager) : layer_(std::move(layer)), - context_([context retain]), - resource_context_([resource_context retain]), + gl_context_switch_manager_(gl_context_guard_manager), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - FML_DCHECK(context_ != nullptr); - FML_DCHECK(resource_context_ != nullptr); - - bool context_current = [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + gl_context_switch_manager_->MakeCurrent(); + bool context_current = context_switch->GetSwitchResult(); FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -62,8 +60,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - EAGLContext* context = EAGLContext.currentContext; - [EAGLContext setCurrentContext:context_]; + std::unique_ptr context_switch = + gl_context_switch_manager_->MakeCurrent(); FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -71,7 +69,6 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -104,8 +101,9 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - - if (![EAGLContext setCurrentContext:context_]) { + std::unique_ptr context_switch = + gl_context_switch_manager_->MakeCurrent(); + if (!context_switch->GetSwitchResult()) { return false; } @@ -116,7 +114,8 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { + if (![gl_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER + fromDrawable:layer_.get()]) { return false; } @@ -132,12 +131,16 @@ return true; } -bool IOSGLRenderTarget::MakeCurrent() { - return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; +std::unique_ptr IOSGLRenderTarget::MakeCurrent() { + bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); + if (!isUpdateSuccessful) { + return std::make_unique(false); + } + return gl_context_switch_manager_->MakeCurrent(); } -bool IOSGLRenderTarget::ResourceMakeCurrent() { - return [EAGLContext setCurrentContext:resource_context_.get()]; +std::unique_ptr IOSGLRenderTarget::ResourceMakeCurrent() { + return gl_context_switch_manager_->ResourceMakeCurrent(); } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 49f40f9eec76a..13253e3eb0bdb 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -27,7 +28,8 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual bool ResourceContextMakeCurrent() = 0; + virtual std::unique_ptr + ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index c1019bb442bb0..8bee87fbf42b1 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,6 +9,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -32,7 +33,7 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -40,7 +41,7 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - bool GLContextMakeCurrent() override; + std::unique_ptr GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -53,6 +54,9 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetGLContextSwitchManager() override; + // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 48e70e00a4e7a..ca3836744d29b 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,7 +28,8 @@ return render_target_->IsValid(); } -bool IOSSurfaceGL::ResourceContextMakeCurrent() { +std::unique_ptr +IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -56,11 +57,11 @@ return true; } -bool IOSSurfaceGL::GLContextMakeCurrent() { +std::unique_ptr IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return false; + return std::make_unique(false); } - return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); + return render_target_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -73,6 +74,11 @@ return IsValid() && render_target_->PresentRenderBuffer(); } +// |GPUSurfaceGLDelegate| +std::shared_ptr IOSSurfaceGL::GetGLContextSwitchManager() { + return context_->GetIOSGLContextSwitchManager(); +} + // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -144,7 +150,7 @@ if (platform_views_controller == nullptr) { return true; } - + platform_views_controller->SetGLContextSwitchManager(context_->GetIOSGLContextSwitchManager()); bool submitted = platform_views_controller->SubmitFrame(std::move(context), context_); [CATransaction commit]; return submitted; diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index daac2ffc77231..5feb1363c1e5c 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" - @class CALayer; namespace flutter { @@ -28,7 +28,7 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - bool ResourceContextMakeCurrent() override; + std::unique_ptr ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index ab5490cf25140..1c87b33e6e92a 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,8 +27,9 @@ return layer_; } -bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return false; +std::unique_ptr +IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return std::make_unique(false); } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index bb37fa9610b2b..1983eb7371383 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,15 +107,18 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + if (gl_context_ != nullptr) { + std::unique_ptr context_switch = + gl_context_->ResourceMakeCurrent(); + if (context_switch->GetSwitchResult()) { + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + } } - - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index d37b03aae8d9e..59566e844d42b 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,8 +34,10 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextMakeCurrent() { - return gl_dispatch_table_.gl_make_current_callback(); +std::unique_ptr +EmbedderSurfaceGL::GLContextMakeCurrent() { + return std::make_unique( + gl_dispatch_table_.gl_make_current_callback()); } // |GPUSurfaceGLDelegate| @@ -79,6 +81,12 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } +// |GPUSurfaceGLDelegate| +std::shared_ptr +EmbedderSurfaceGL::GetGLContextSwitchManager() { + return nullptr; +} + // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index a01fa05d4e62c..f810597111f1f 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,7 +50,8 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - bool GLContextMakeCurrent() override; + std::unique_ptr + GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -73,6 +74,9 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; + // |GPUSurfaceGLDelegate| + std::shared_ptr GetGLContextSwitchManager() override; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index e667c4c88678d..dd2238aba3181 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -136,6 +138,9 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -202,6 +207,8 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, + 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, + 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -239,6 +246,7 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, + 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -398,6 +406,7 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, + 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -418,6 +427,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -492,7 +502,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -545,7 +555,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -561,6 +571,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -584,6 +595,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index 87799ad5e6434..cd1c5b7366a89 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - - - - - - - - * registrar = + [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; + [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; + self.window.rootViewController = flutterViewController; +} + @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h new file mode 100644 index 0000000000000..19cefe7025986 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GLTestPlatformView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject*)messenger; + +- (UIView*)view; + +@end + +@interface GLTestPlatformViewFactory : NSObject + +- (instancetype)initWithMessenger:(NSObject*)messenger; + +@end + +@interface GLTestView : UIView + +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m new file mode 100644 index 0000000000000..8142550e52863 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m @@ -0,0 +1,90 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GLTestPlatformView.h" + +#define GLES_SILENCE_DEPRECATION + +@implementation GLTestPlatformView { + int64_t _viewId; + GLTestView* _view; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id)args + binaryMessenger:(NSObject*)messenger { + if ([super init]) { + _viewId = viewId; + _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; + } + return self; +} + +- (UIView*)view { + return _view; +} + +@end + +@implementation GLTestPlatformViewFactory { + NSObject* _messenger; +} + +- (instancetype)initWithMessenger:(NSObject*)messenger { + self = [super init]; + if (self) { + _messenger = messenger; + } + return self; +} + +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger]; + return platformView; +} + +- (NSObject*)createArgsCodec { + return [FlutterStringCodec sharedInstance]; +} + +@end + +@interface GLTestView () + +@property(strong, nonatomic) EAGLContext* context; + +@end + +@implementation GLTestView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + _context.debugLabel = @"platform view context"; + [EAGLContext setCurrentContext:_context]; + self.backgroundColor = [UIColor redColor]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [self checkEAGLContext]; + }); + } + return self; +} + +- (void)checkEAGLContext { + if ([EAGLContext currentContext] != _context) { + self.accessibilityIdentifier = @"gl_platformview_wrong_context"; + } else { + self.accessibilityIdentifier = @"gl_platformview_correct_context"; + } +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m new file mode 100644 index 0000000000000..d581926ae5a6a --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface PlatformViewGLTests : XCTestCase + +@property(nonatomic, strong) XCUIApplication* application; + +@end + +@implementation PlatformViewGLTests + +- (void)setUp { + self.continueAfterFailure = NO; + + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--platform-view-gl" ]; + [self.application launch]; +} + +- (void)testExample { + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || + [element.identifier isEqualToString:@"gl_platformview_correct_context"]; + }]; + XCUIElement* firstElement = + [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; + if (![firstElement waitForExistenceWithTimeout:30]) { + XCTFail(@"Failed due to not able to find platform view with 30 seconds"); + } + XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); +} + +@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 7a264b70008f0..82b589a6681fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,6 +25,7 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), + 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 05efe52fa8b25..86fb4df1df449 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id); + createPlatformView(window, text, id, 'scenarios/textPlatformView'); } @override @@ -281,6 +281,24 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } +/// Platform view scenario for testing EAGLContext on iOS. +class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Constructs a platform view to test EAGLContext on iOS. + PlatformViewGLScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + finishBuilderByAddingPlatformViewAndPicture(builder, 6); + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -289,7 +307,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id) { + void createPlatformView(Window window, String text, int id, String viewType) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -313,8 +331,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - 'scenarios/textPlatformView'.length, - ...utf8.encode('scenarios/textPlatformView'), + viewType.length, + ...utf8.encode(viewType), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart new file mode 100644 index 0000000000000..e69de29bb2d1d From 7413304c37dca275ce11fe8f4120f16feb63b613 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Fri, 8 Nov 2019 12:52:01 -0800 Subject: [PATCH 065/591] [web] Support gif/webp animations, Speed up image drawing in BitmapCanvas. (#13748) * Add draw image test * Optimize drawImageScaled * optimize cloning in HtmlImage, implement drawImageRect using image tag --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 100 ++++++++++--- .../lib/src/engine/html_image_codec.dart | 14 +- .../lib/src/engine/recording_canvas.dart | 5 +- .../engine/canvas_draw_image_golden_test.dart | 136 ++++++++++++++++++ 5 files changed, 234 insertions(+), 23 deletions(-) create mode 100644 lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index e82417f92861e..e8b87138a0033 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 7935a97f89a6af5ae5182b2b5e59debda0189984 \ No newline at end of file +revision: 009fbdd595aeec364eaff6b8f337f8ceb3c44ab9 diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index 65de6c351296d..b9daedc91f644 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -72,6 +72,17 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { Object _prevFillStyle; Object _prevStrokeStyle; + // Indicates the instructions following drawImage or drawParagraph that + // a child element was created to paint. + // TODO(flutter_web): When childElements are created by + // drawImage/drawParagraph commands, compositing order is not correctly + // handled when we interleave these with other paint commands. + // To solve this, recording canvas will have to check the paint queue + // and send a hint to EngineCanvas that additional canvas layers need + // to be used to composite correctly. In practice this is very rare + // with Widgets but CustomPainter(s) can hit this code path. + bool _childOverdraw = false; + /// Allocates a canvas with enough memory to paint a picture within the given /// [bounds]. /// @@ -568,30 +579,81 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { void drawImage(ui.Image image, ui.Offset p, ui.PaintData paint) { _applyPaint(paint); final HtmlImage htmlImage = image; - final html.Element imgElement = htmlImage.imgElement.clone(true); - imgElement.style - ..position = 'absolute' - ..transform = 'translate(${p.dx}px, ${p.dy}px)'; - rootElement.append(imgElement); + final html.Element imgElement = htmlImage.cloneImageElement(); + _drawImage(imgElement, p); + _childOverdraw = true; + } + + void _drawImage(html.ImageElement imgElement, ui.Offset p) { + if (isClipped) { + final List clipElements = + _clipContent(_clipStack, imgElement, p, currentTransform); + for (html.Element clipElement in clipElements) { + rootElement.append(clipElement); + _children.add(clipElement); + } + } else { + final String cssTransform = + matrix4ToCssTransform(transformWithOffset(currentTransform, p)); + imgElement.style + ..transformOrigin = '0 0 0' + ..transform = cssTransform; + rootElement.append(imgElement); + _children.add(imgElement); + } } @override void drawImageRect( ui.Image image, ui.Rect src, ui.Rect dst, ui.PaintData paint) { - // TODO(het): Check if the src rect is the entire image, and if so just - // append the imgElement and set it's height and width. final HtmlImage htmlImage = image; - ctx.drawImageScaledFromSource( - htmlImage.imgElement, - src.left, - src.top, - src.width, - src.height, - dst.left, - dst.top, - dst.width, - dst.height, - ); + final bool requiresClipping = src.left != 0 || + src.top != 0 || + src.width != image.width || + src.height != image.height; + if (dst.width == image.width && + dst.height == image.height && + !requiresClipping) { + drawImage(image, dst.topLeft, paint); + } else { + _applyPaint(paint); + final html.Element imgElement = htmlImage.cloneImageElement(); + if (requiresClipping) { + save(); + clipRect(dst); + } + double targetLeft = dst.left; + double targetTop = dst.top; + if (requiresClipping) { + if (src.width != image.width) { + double leftMargin = -src.left * (dst.width / src.width); + targetLeft += leftMargin; + } + if (src.height != image.height) { + double topMargin = -src.top * (dst.height / src.height); + targetTop += topMargin; + } + } + _drawImage(imgElement, ui.Offset(targetLeft, targetTop)); + // To scale set width / height on destination image. + // For clipping we need to scale according to + // clipped-width/full image width and shift it according to left/top of + // source rectangle. + double targetWidth = dst.width; + double targetHeight = dst.height; + if (requiresClipping) { + targetWidth *= image.width / src.width; + targetHeight *= image.height / src.height; + } + final html.CssStyleDeclaration imageStyle = imgElement.style; + imageStyle + ..width = '${targetWidth.toStringAsFixed(2)}px' + ..height = '${targetHeight.toStringAsFixed(2)}px'; + if (requiresClipping) { + restore(); + } + } + _childOverdraw = true; } void _drawTextLine( @@ -625,7 +687,7 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking { final ParagraphGeometricStyle style = paragraph._geometricStyle; - if (paragraph._drawOnCanvas) { + if (paragraph._drawOnCanvas && _childOverdraw == false) { final List lines = paragraph._lines ?? [paragraph._plainText]; diff --git a/lib/web_ui/lib/src/engine/html_image_codec.dart b/lib/web_ui/lib/src/engine/html_image_codec.dart index 23d422f467483..3ccd1faf11cb9 100644 --- a/lib/web_ui/lib/src/engine/html_image_codec.dart +++ b/lib/web_ui/lib/src/engine/html_image_codec.dart @@ -92,7 +92,7 @@ class SingleFrameInfo implements ui.FrameInfo { class HtmlImage implements ui.Image { final html.ImageElement imgElement; - + bool _requiresClone = false; HtmlImage(this.imgElement, this.width, this.height); @override @@ -117,6 +117,18 @@ class HtmlImage implements ui.Image { }); } + // Returns absolutely positioned actual image element on first call and + // clones on subsequent calls. + html.ImageElement cloneImageElement() { + if (_requiresClone) { + return imgElement.clone(true); + } else { + _requiresClone = true; + imgElement.style..position = 'absolute'; + return imgElement; + } + } + /// Returns an error message on failure, null on success. String _toByteData(int format, Callback callback) => null; } diff --git a/lib/web_ui/lib/src/engine/recording_canvas.dart b/lib/web_ui/lib/src/engine/recording_canvas.dart index 067fe397eb881..891390fca4464 100644 --- a/lib/web_ui/lib/src/engine/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/recording_canvas.dart @@ -73,8 +73,9 @@ class RecordingCanvas { print(debugBuf); } else { try { - for (int i = 0; i < _commands.length; i++) { - _commands[i].apply(engineCanvas); + for (int i = 0, len = _commands.length; i < len; i++) { + PaintCommand command = _commands[i]; + command.apply(engineCanvas); } } catch (e) { // commands should never fail, but... diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart new file mode 100644 index 0000000000000..9601d71afa93f --- /dev/null +++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart @@ -0,0 +1,136 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:js_util' as js_util; + +import 'package:ui/ui.dart' hide TextStyle; +import 'package:ui/src/engine.dart'; +import 'package:test/test.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + final Paint testPaint = Paint()..color = const Color(0xFFFF0000); + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(RecordingCanvas rc, String fileName, + { Rect region = const Rect.fromLTWH(0, 0, 500, 500) }) async { + + final EngineCanvas engineCanvas = BitmapCanvas(screenRect); + + rc.apply(engineCanvas); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body.append(sceneElement); + await matchGoldenFile('$fileName.png', region: region, maxDiffRate: 0.02); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Paints image', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.drawImage(createTestImage(), Offset(0, 0), new Paint()); + await _checkScreenshot(rc, 'draw_image'); + }); + + test('Paints image with transform', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 4.0); + rc.drawImage(createTestImage(), Offset(0, 0), new Paint()); + await _checkScreenshot(rc, 'draw_image_with_transform'); + }); + + test('Paints image with transform and offset', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 4.0); + rc.drawImage(createTestImage(), Offset(30, 20), new Paint()); + await _checkScreenshot(rc, 'draw_image_with_transform_and_offset'); + }); + + test('Paints image with transform using destination', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 4.0); + Image testImage = createTestImage(); + double testWidth = testImage.width.toDouble(); + double testHeight = testImage.height.toDouble(); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); + await _checkScreenshot(rc, 'draw_image_rect_with_transform'); + }); + + test('Paints image with source and destination', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + Image testImage = createTestImage(); + double testWidth = testImage.width.toDouble(); + double testHeight = testImage.height.toDouble(); + rc.drawImageRect(testImage, Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); + await _checkScreenshot(rc, 'draw_image_rect_with_source'); + }); + + test('Paints image with transform using source and destination', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 6.0); + Image testImage = createTestImage(); + double testWidth = testImage.width.toDouble(); + double testHeight = testImage.height.toDouble(); + rc.drawImageRect(testImage, Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); + await _checkScreenshot(rc, 'draw_image_rect_with_transform_source'); + }); +} + +HtmlImage createTestImage() { + const int width = 100; + const int height = 50; + html.CanvasElement canvas = new html.CanvasElement(width: width, height: height); + html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(33, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(66, 0, 33, 50); + ctx.fill(); + html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); + return HtmlImage(imageElement, width, height); +} + From f5754357b666d0b3645bae83cfebd214790a30ea Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Nov 2019 13:44:29 -0800 Subject: [PATCH 066/591] Revert "Reland "Guarding EAGLContext used by Flutter #13314" (#13755)" (#13757) This reverts commit 618e6666ced77bf497311876fbe968c6b9d72041. --- ci/licenses_golden/licenses_flutter | 4 - shell/common/BUILD.gn | 2 - shell/common/gl_context_switch_manager.cc | 28 ----- shell/common/gl_context_switch_manager.h | 116 ------------------ shell/common/shell_test.cc | 11 +- shell/common/shell_test.h | 5 +- shell/common/surface.cc | 6 +- shell/common/surface.h | 4 +- shell/gpu/gpu_surface_gl.cc | 33 ++--- shell/gpu/gpu_surface_gl.h | 3 +- shell/gpu/gpu_surface_gl_delegate.h | 7 +- shell/platform/android/android_surface_gl.cc | 12 +- shell/platform/android/android_surface_gl.h | 6 +- shell/platform/darwin/ios/BUILD.gn | 2 - .../framework/Source/FlutterPlatformViews.mm | 16 +-- .../Source/FlutterPlatformViews_Internal.h | 6 - shell/platform/darwin/ios/ios_gl_context.h | 16 +-- shell/platform/darwin/ios/ios_gl_context.mm | 21 +++- .../ios/ios_gl_context_switch_manager.h | 65 ---------- .../ios/ios_gl_context_switch_manager.mm | 78 ------------ .../darwin/ios/ios_gl_render_target.h | 15 ++- .../darwin/ios/ios_gl_render_target.mm | 41 +++---- shell/platform/darwin/ios/ios_surface.h | 4 +- shell/platform/darwin/ios/ios_surface_gl.h | 8 +- shell/platform/darwin/ios/ios_surface_gl.mm | 16 +-- .../darwin/ios/ios_surface_software.h | 4 +- .../darwin/ios/ios_surface_software.mm | 5 +- .../platform/darwin/ios/platform_view_ios.mm | 19 ++- .../platform/embedder/embedder_surface_gl.cc | 12 +- shell/platform/embedder/embedder_surface_gl.h | 6 +- .../Scenarios.xcodeproj/project.pbxproj | 16 +-- .../xcshareddata/xcschemes/Scenarios.xcscheme | 22 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 24 ---- .../Scenarios/Scenarios/GLTestPlatformView.h | 30 ----- .../Scenarios/Scenarios/GLTestPlatformView.m | 90 -------------- .../ScenariosUITests/PlatformViewGLTests.m | 39 ------ testing/scenario_app/lib/main.dart | 1 - .../scenario_app/lib/src/platform_view.dart | 36 ++---- testing/scenario_app/lib/src/texture.dart | 0 39 files changed, 116 insertions(+), 713 deletions(-) delete mode 100644 shell/common/gl_context_switch_manager.cc delete mode 100644 shell/common/gl_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.h delete mode 100644 shell/platform/darwin/ios/ios_gl_context_switch_manager.mm delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h delete mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m delete mode 100644 testing/scenario_app/lib/src/texture.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e54ec6404291c..4e36d64a55529 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -495,8 +495,6 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/fixtures/shell_test.dart -FILE: ../../../flutter/shell/common/gl_context_switch_manager.cc -FILE: ../../../flutter/shell/common/gl_context_switch_manager.h FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h @@ -801,8 +799,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 50f1defa57f8d..f93bca63478f0 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -66,8 +66,6 @@ source_set("common") { "canvas_spy.h", "engine.cc", "engine.h", - "gl_context_switch_manager.cc", - "gl_context_switch_manager.h", "isolate_configuration.cc", "isolate_configuration.h", "persistent_cache.cc", diff --git a/shell/common/gl_context_switch_manager.cc b/shell/common/gl_context_switch_manager.cc deleted file mode 100644 index 37c12e885b3f3..0000000000000 --- a/shell/common/gl_context_switch_manager.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "gl_context_switch_manager.h" - -namespace flutter { - -GLContextSwitchManager::GLContextSwitchManager() = default; - -GLContextSwitchManager::~GLContextSwitchManager() = default; - -GLContextSwitchManager::GLContextSwitch::GLContextSwitch() = default; - -GLContextSwitchManager::GLContextSwitch::~GLContextSwitch(){}; - -GLContextSwitchManager::GLContextSwitchPureResult::GLContextSwitchPureResult( - bool switch_result) - : switch_result_(switch_result){}; - -GLContextSwitchManager::GLContextSwitchPureResult:: - ~GLContextSwitchPureResult() = default; - -bool GLContextSwitchManager::GLContextSwitchPureResult::GetSwitchResult() { - return switch_result_; -} - -} // namespace flutter diff --git a/shell/common/gl_context_switch_manager.h b/shell/common/gl_context_switch_manager.h deleted file mode 100644 index 90b27b4232682..0000000000000 --- a/shell/common/gl_context_switch_manager.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_COMMON_GL_CONTEXT_SWITCH_MANAGER_H_ - -#include -#include "flutter/fml/macros.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// Manages `GLContextSwitch`. -/// -/// Should be subclassed for platforms that uses GL and requires context -/// switching. Always use `MakeCurrent` and `ResourceMakeCurrent` in the -/// `GLContextSwitchManager` to set gl contexts. -/// -class GLContextSwitchManager { - public: - //------------------------------------------------------------------------------ - /// Switches the gl context to the flutter's contexts. - /// - /// Should be subclassed for each platform embedder that uses GL. - /// In construction, it should set the current context to a flutter's context - /// In destruction, it should rest the current context. - /// - class GLContextSwitch { - public: - GLContextSwitch(); - - virtual ~GLContextSwitch(); - - virtual bool GetSwitchResult() = 0; - - FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitch); - }; - - GLContextSwitchManager(); - ~GLContextSwitchManager(); - - //---------------------------------------------------------------------------- - /// @brief Creates a shell instance using the provided settings. The - /// callbacks to create the various shell subcomponents will be - /// called on the appropriate threads before this method returns. - /// If this is the first instance of a shell in the process, this - /// call also bootstraps the Dart VM. - /// - /// @param[in] task_runners The task runners - /// @param[in] settings The settings - /// @param[in] on_create_platform_view The callback that must return a - /// platform view. This will be called on - /// the platform task runner before this - /// method returns. - /// @param[in] on_create_rasterizer That callback that must provide a - /// valid rasterizer. This will be called - /// on the render task runner before this - /// method returns. - /// - /// @return A full initialized shell if the settings and callbacks are - /// valid. The root isolate has been created but not yet launched. - /// It may be launched by obtaining the engine weak pointer and - /// posting a task onto the UI task runner with a valid run - /// configuration to run the isolate. The embedder must always - /// check the validity of the shell (using the IsSetup call) - /// immediately after getting a pointer to it. - /// - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's context as current context. - /// - /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if - /// the setting process is succesful. - virtual std::unique_ptr MakeCurrent() = 0; - - //---------------------------------------------------------------------------- - /// @brief Make the flutter's resources context as current context. - /// - /// @return A `GLContextSwitch` with `GetSwitchResult` returning true if - /// the setting process is succesful. - virtual std::unique_ptr ResourceMakeCurrent() = 0; - - //------------------------------------------------------------------------------ - /// A representation of a `GLContextSwitch` that doesn't require actual - /// context switching. - /// - class GLContextSwitchPureResult final : public GLContextSwitch { - public: - // Constructor that creates an `GLContextSwitchPureResult`. - // The `GetSwitchResult` will return the same value as `switch_result`. - - //---------------------------------------------------------------------------- - /// @brief Constructs a `GLContextSwitchPureResult`. - /// - /// @param[in] switch_result the switch result that will be returned - /// in `GetSwitchResult()` - /// - GLContextSwitchPureResult(bool switch_result); - - ~GLContextSwitchPureResult(); - - bool GetSwitchResult() override; - - private: - bool switch_result_; - - FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchPureResult); - }; - - FML_DISALLOW_COPY_AND_ASSIGN(GLContextSwitchManager); -}; - -} // namespace flutter - -#endif diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 7c0785ab9d2e4..51370d082862b 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,10 +355,8 @@ PointerDataDispatcherMaker ShellTestPlatformView::GetDispatcherMaker() { } // |GPUSurfaceGLDelegate| -std::unique_ptr -ShellTestPlatformView::GLContextMakeCurrent() { - return std::make_unique( - gl_surface_.MakeCurrent()); +bool ShellTestPlatformView::GLContextMakeCurrent() { + return gl_surface_.MakeCurrent(); } // |GPUSurfaceGLDelegate| @@ -389,10 +387,5 @@ ExternalViewEmbedder* ShellTestPlatformView::GetExternalViewEmbedder() { return nullptr; } -std::shared_ptr -ShellTestPlatformView::GetGLContextSwitchManager() { - return nullptr; -} - } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 7d1d442dd8533..fdee9653b71ce 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -144,8 +144,7 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { PointerDataDispatcherMaker GetDispatcherMaker() override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -162,8 +161,6 @@ class ShellTestPlatformView : public PlatformView, public GPUSurfaceGLDelegate { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - std::shared_ptr GetGLContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformView); }; diff --git a/shell/common/surface.cc b/shell/common/surface.cc index 392a8cad54e3e..b8a77ca1811d4 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -60,10 +60,8 @@ flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { return nullptr; } -std::unique_ptr -Surface::MakeRenderContextCurrent() { - return std::make_unique( - true); +bool Surface::MakeRenderContextCurrent() { + return true; } } // namespace flutter diff --git a/shell/common/surface.h b/shell/common/surface.h index d34d4dae93e04..7bbc16e24c690 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -10,7 +10,6 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flutter { @@ -59,8 +58,7 @@ class Surface { virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual std::unique_ptr - MakeRenderContextCurrent(); + virtual bool MakeRenderContextCurrent(); private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index b69475aefd6ff..5f30a48375d33 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -8,7 +8,6 @@ #include "flutter/fml/logging.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" @@ -40,10 +39,7 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, : delegate_(delegate), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -91,6 +87,8 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, } FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " << compiled_count; + + delegate_->GLContextClearCurrent(); } GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, @@ -100,9 +98,7 @@ GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, context_(gr_context), render_to_surface_(render_to_surface), weak_factory_(this) { - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to setup the gr context."; return; @@ -118,9 +114,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { if (!valid_) { return; } - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to destroy the " "GrContext resources."; return; @@ -131,6 +126,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { context_->releaseResourcesAndAbandonContext(); } context_ = nullptr; + + delegate_->GLContextClearCurrent(); } // |Surface| @@ -256,9 +253,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); - if (!context_switch->GetSwitchResult()) { + if (!delegate_->GLContextMakeCurrent()) { FML_LOG(ERROR) << "Could not make the context current to acquire the frame."; return nullptr; @@ -290,9 +285,7 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return weak ? weak->PresentSurface(canvas) : false; }; - std::unique_ptr result = - std::make_unique(surface, submit_callback); - return result; + return std::make_unique(surface, submit_callback); } bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { @@ -300,8 +293,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { return false; } - std::unique_ptr context_switch = - delegate_->GLContextMakeCurrent(); if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; @@ -338,6 +329,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_ = std::move(new_onscreen_surface); } + return true; } @@ -368,8 +360,7 @@ flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { } // |Surface| -std::unique_ptr -GPUSurfaceGL::MakeRenderContextCurrent() { +bool GPUSurfaceGL::MakeRenderContextCurrent() { return delegate_->GLContextMakeCurrent(); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 953ebf0872161..97325569bfd16 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -44,8 +44,7 @@ class GPUSurfaceGL : public Surface { flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; // |Surface| - std::unique_ptr - MakeRenderContextCurrent() override; + bool MakeRenderContextCurrent() override; private: GPUSurfaceGLDelegate* delegate_; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index fe915679e55f5..dfe0ce7f468db 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -7,7 +7,6 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_delegate.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" @@ -17,8 +16,7 @@ namespace flutter { class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: // Called to make the main GL context current on the current thread. - virtual std::unique_ptr - GLContextMakeCurrent() = 0; + virtual bool GLContextMakeCurrent() = 0; // Called to clear the current GL context on the thread. This may be called on // either the GPU or IO threads. @@ -61,9 +59,6 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // instrumentation to specific GL calls can specify custom GL functions // here. virtual GLProcResolver GetGLProcResolver() const; - - virtual std::shared_ptr - GetGLContextSwitchManager() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index f1a5edbe774ad..737d9f293a518 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -104,11 +104,9 @@ bool AndroidSurfaceGL::SetNativeWindow( return true; } -std::unique_ptr -AndroidSurfaceGL::GLContextMakeCurrent() { +bool AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return std::make_unique( - onscreen_context_->MakeCurrent()); + return onscreen_context_->MakeCurrent(); } bool AndroidSurfaceGL::GLContextClearCurrent() { @@ -132,10 +130,4 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return nullptr; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -AndroidSurfaceGL::GetGLContextSwitchManager() { - return nullptr; -} - } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index e30300cf91289..d59302ad66509 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -47,8 +47,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -62,9 +61,6 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetGLContextSwitchManager() override; - private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 3e67beb97e717..a66c3bf48c5ae 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -86,8 +86,6 @@ shared_library("create_flutter_framework_dylib") { "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", - "ios_gl_context_switch_manager.h", - "ios_gl_context_switch_manager.mm", "ios_gl_render_target.h", "ios_gl_render_target.mm", "ios_surface.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 057ee69d50520..33ca14d9fabea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -160,11 +160,6 @@ frame_size_ = frame_size; } -void FlutterPlatformViewsController::SetGLContextSwitchManager( - std::shared_ptr gl_context_guard_manager) { - gl_context_switch_manager_ = gl_context_guard_manager; -} - void FlutterPlatformViewsController::CancelFrame() { composition_order_.clear(); } @@ -373,12 +368,7 @@ bool did_submit = true; for (int64_t view_id : composition_order_) { - if (gl_context_switch_manager_ != nullptr) { - std::unique_ptr contextSwitch = - gl_context_switch_manager_->MakeCurrent(); - } - - EnsureOverlayInitialized(view_id, std::move(gl_context), gr_context); + EnsureOverlayInitialized(view_id, gl_context, gr_context); auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); SkCanvas* canvas = frame->SkiaCanvas(); canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); @@ -465,10 +455,6 @@ std::shared_ptr gl_context, GrContext* gr_context) { FML_DCHECK(flutter_view_); - if (gl_context_switch_manager_ != nullptr) { - std::unique_ptr contextSwitch = - gl_context_switch_manager_->MakeCurrent(); - } auto overlay_it = overlays_.find(overlay_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 211f45cc73c25..c8daeaa605946 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,7 +11,6 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" // A UIView that is used as the parent for embedded UIViews. // @@ -81,9 +80,6 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); - void SetGLContextSwitchManager( - std::shared_ptr gl_context_guard_manager); - void RegisterViewFactory(NSObject* factory, NSString* factoryId); void SetFrameSize(SkISize frame_size); @@ -208,8 +204,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - std::shared_ptr gl_context_switch_manager_; - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 136616e8dfc5b..232645d9c8592 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -26,24 +26,16 @@ class IOSGLContext { std::unique_ptr CreateRenderTarget( fml::scoped_nsobject layer); - std::unique_ptr MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); - - std::shared_ptr GetIOSGLContextSwitchManager() { - return gl_context_switch_manager_; - } + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } - fml::scoped_nsobject GetContext() const { - return gl_context_switch_manager_->GetContext(); - } - private: + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; sk_sp color_space_; - std::shared_ptr gl_context_switch_manager_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 8436a5a44413f..52fb85f8f19a9 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -13,7 +13,15 @@ namespace flutter { IOSGLContext::IOSGLContext() { - gl_context_switch_manager_ = std::make_shared(); + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); + if (resource_context_ != nullptr) { + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:resource_context_.get().sharegroup]); + } else { + resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); + context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:resource_context_.get().sharegroup]); + } // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -40,15 +48,16 @@ std::unique_ptr IOSGLContext::CreateRenderTarget( fml::scoped_nsobject layer) { - return std::make_unique(std::move(layer), gl_context_switch_manager_); + return std::make_unique(std::move(layer), context_.get(), + resource_context_.get()); } -std::unique_ptr IOSGLContext::MakeCurrent() { - return gl_context_switch_manager_->MakeCurrent(); +bool IOSGLContext::MakeCurrent() { + return [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr IOSGLContext::ResourceMakeCurrent() { - return gl_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLContext::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h b/shell/platform/darwin/ios/ios_gl_context_switch_manager.h deleted file mode 100644 index 5d3714f326fe4..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_CONTEXT_SWITCH_MANAGER_H_ - -#define GLES_SILENCE_DEPRECATION - -#import -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/gl_context_switch_manager.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// The iOS implementation of `GLContextSwitchManager`. -/// -/// On `IOSGLContextSwitch`'s construction, it pushes the current EAGLContext to a stack and -/// sets the flutter's gl context as current. -/// On `IOSGLContextSwitch`'s desstruction, it pops a EAGLContext from the stack and set it to -/// current. -/// -class IOSGLContextSwitchManager final : public GLContextSwitchManager { - public: - class IOSGLContextSwitch final : public GLContextSwitch { - public: - IOSGLContextSwitch(IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context); - - ~IOSGLContextSwitch(); - - bool GetSwitchResult() override; - - private: - IOSGLContextSwitchManager& manager_; - bool switch_result_; - bool has_pushed_context_; - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitch); - }; - - IOSGLContextSwitchManager(); - - ~IOSGLContextSwitchManager(); - - std::unique_ptr MakeCurrent() override; - std::unique_ptr ResourceMakeCurrent() override; - - fml::scoped_nsobject GetContext(); - - private: - fml::scoped_nsobject context_; - fml::scoped_nsobject resource_context_; - fml::scoped_nsobject stored_; - - bool PushContext(fml::scoped_nsobject context); - void PopContext(); - - FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContextSwitchManager); -}; - -} - -#endif diff --git a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm b/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm deleted file mode 100644 index c6b332f121b46..0000000000000 --- a/shell/platform/darwin/ios/ios_gl_context_switch_manager.mm +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ios_gl_context_switch_manager.h" - -namespace flutter { - -IOSGLContextSwitchManager::IOSGLContextSwitchManager() { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - stored_ = fml::scoped_nsobject([[NSMutableArray new] retain]); - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); - if (resource_context_ != nullptr) { - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 - sharegroup:resource_context_.get().sharegroup]); - } else { - resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]); - context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:resource_context_.get().sharegroup]); - } -}; - -IOSGLContextSwitchManager::~IOSGLContextSwitchManager() = default; - -std::unique_ptr IOSGLContextSwitchManager::MakeCurrent() { - return std::make_unique(*this, context_); -} - -std::unique_ptr -IOSGLContextSwitchManager::ResourceMakeCurrent() { - return std::make_unique(*this, resource_context_); -} - -fml::scoped_nsobject IOSGLContextSwitchManager::GetContext() { - return context_; -} - -bool IOSGLContextSwitchManager::PushContext(fml::scoped_nsobject context) { - EAGLContext* current = [EAGLContext currentContext]; - if (current == nil) { - [stored_.get() addObject:[NSNull null]]; - } else { - [stored_.get() addObject:current]; - } - bool result = [EAGLContext setCurrentContext:context.get()]; - return result; -} - -void IOSGLContextSwitchManager::PopContext() { - EAGLContext* last = [stored_.get() lastObject]; - [stored_.get() removeLastObject]; - if ([last isEqual:[NSNull null]]) { - [EAGLContext setCurrentContext:nil]; - return; - } - [EAGLContext setCurrentContext:last]; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::IOSGLContextSwitch( - IOSGLContextSwitchManager& manager, - fml::scoped_nsobject context) - : manager_(manager) { - bool result = manager_.PushContext(context); - has_pushed_context_ = true; - switch_result_ = result; -} - -IOSGLContextSwitchManager::IOSGLContextSwitch::~IOSGLContextSwitch() { - if (!has_pushed_context_) { - return; - } - manager_.PopContext(); -} - -bool IOSGLContextSwitchManager::IOSGLContextSwitch::GetSwitchResult() { - return switch_result_; -} -} diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h index 10428896b63a0..b2eafe16e0950 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.h +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -13,15 +13,14 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { class IOSGLRenderTarget { public: - IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager); + IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context); ~IOSGLRenderTarget(); @@ -33,16 +32,16 @@ class IOSGLRenderTarget { bool UpdateStorageSizeIfNecessary(); - std::unique_ptr MakeCurrent(); + bool MakeCurrent(); - std::unique_ptr - ResourceMakeCurrent(); + bool ResourceMakeCurrent(); sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; - std::shared_ptr gl_context_switch_manager_; + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; GLuint framebuffer_; GLuint colorbuffer_; GLint storage_size_width_; diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm index a3581e77198a8..a57ba9c46b414 100644 --- a/shell/platform/darwin/ios/ios_gl_render_target.mm +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -12,20 +12,22 @@ namespace flutter { -IOSGLRenderTarget::IOSGLRenderTarget( - fml::scoped_nsobject layer, - std::shared_ptr gl_context_guard_manager) +IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context) : layer_(std::move(layer)), - gl_context_switch_manager_(gl_context_guard_manager), + context_([context retain]), + resource_context_([resource_context retain]), framebuffer_(GL_NONE), colorbuffer_(GL_NONE), storage_size_width_(0), storage_size_height_(0), valid_(false) { FML_DCHECK(layer_ != nullptr); - std::unique_ptr context_switch = - gl_context_switch_manager_->MakeCurrent(); - bool context_current = context_switch->GetSwitchResult(); + FML_DCHECK(context_ != nullptr); + FML_DCHECK(resource_context_ != nullptr); + + bool context_current = [EAGLContext setCurrentContext:context_]; FML_DCHECK(context_current); FML_DCHECK(glGetError() == GL_NO_ERROR); @@ -60,8 +62,8 @@ } IOSGLRenderTarget::~IOSGLRenderTarget() { - std::unique_ptr context_switch = - gl_context_switch_manager_->MakeCurrent(); + EAGLContext* context = EAGLContext.currentContext; + [EAGLContext setCurrentContext:context_]; FML_DCHECK(glGetError() == GL_NO_ERROR); // Deletes on GL_NONEs are ignored @@ -69,6 +71,7 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); + [EAGLContext setCurrentContext:context]; } bool IOSGLRenderTarget::IsValid() const { @@ -101,9 +104,8 @@ FML_DLOG(INFO) << "Updating render buffer storage size."; FML_DCHECK(glGetError() == GL_NO_ERROR); - std::unique_ptr context_switch = - gl_context_switch_manager_->MakeCurrent(); - if (!context_switch->GetSwitchResult()) { + + if (![EAGLContext setCurrentContext:context_]) { return false; } @@ -114,8 +116,7 @@ glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - if (![gl_context_switch_manager_->GetContext().get() renderbufferStorage:GL_RENDERBUFFER - fromDrawable:layer_.get()]) { + if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { return false; } @@ -131,16 +132,12 @@ return true; } -std::unique_ptr IOSGLRenderTarget::MakeCurrent() { - bool isUpdateSuccessful = UpdateStorageSizeIfNecessary(); - if (!isUpdateSuccessful) { - return std::make_unique(false); - } - return gl_context_switch_manager_->MakeCurrent(); +bool IOSGLRenderTarget::MakeCurrent() { + return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; } -std::unique_ptr IOSGLRenderTarget::ResourceMakeCurrent() { - return gl_context_switch_manager_->ResourceMakeCurrent(); +bool IOSGLRenderTarget::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; } } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 13253e3eb0bdb..49f40f9eec76a 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -12,7 +12,6 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/surface.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" namespace flutter { @@ -28,8 +27,7 @@ class IOSSurface { virtual bool IsValid() const = 0; - virtual std::unique_ptr - ResourceContextMakeCurrent() = 0; + virtual bool ResourceContextMakeCurrent() = 0; virtual void UpdateStorageSizeIfNecessary() = 0; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 8bee87fbf42b1..c1019bb442bb0 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -9,7 +9,6 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" -#include "flutter/shell/platform/darwin/ios/ios_gl_context_switch_manager.h" #include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -33,7 +32,7 @@ class IOSSurfaceGL final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; @@ -41,7 +40,7 @@ class IOSSurfaceGL final : public IOSSurface, // |IOSSurface| std::unique_ptr CreateGPUSurface(GrContext* gr_context = nullptr) override; - std::unique_ptr GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -54,9 +53,6 @@ class IOSSurfaceGL final : public IOSSurface, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetGLContextSwitchManager() override; - // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index ca3836744d29b..48e70e00a4e7a 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -28,8 +28,7 @@ return render_target_->IsValid(); } -std::unique_ptr -IOSSurfaceGL::ResourceContextMakeCurrent() { +bool IOSSurfaceGL::ResourceContextMakeCurrent() { return context_->ResourceMakeCurrent(); } @@ -57,11 +56,11 @@ return true; } -std::unique_ptr IOSSurfaceGL::GLContextMakeCurrent() { +bool IOSSurfaceGL::GLContextMakeCurrent() { if (!IsValid()) { - return std::make_unique(false); + return false; } - return render_target_->MakeCurrent(); + return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -74,11 +73,6 @@ return IsValid() && render_target_->PresentRenderBuffer(); } -// |GPUSurfaceGLDelegate| -std::shared_ptr IOSSurfaceGL::GetGLContextSwitchManager() { - return context_->GetIOSGLContextSwitchManager(); -} - // |ExternalViewEmbedder| SkCanvas* IOSSurfaceGL::GetRootCanvas() { // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the @@ -150,7 +144,7 @@ if (platform_views_controller == nullptr) { return true; } - platform_views_controller->SetGLContextSwitchManager(context_->GetIOSGLContextSwitchManager()); + bool submitted = platform_views_controller->SubmitFrame(std::move(context), context_); [CATransaction commit]; return submitted; diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index 5feb1363c1e5c..daac2ffc77231 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -8,9 +8,9 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/gl_context_switch_manager.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" + @class CALayer; namespace flutter { @@ -28,7 +28,7 @@ class IOSSurfaceSoftware final : public IOSSurface, bool IsValid() const override; // |IOSSurface| - std::unique_ptr ResourceContextMakeCurrent() override; + bool ResourceContextMakeCurrent() override; // |IOSSurface| void UpdateStorageSizeIfNecessary() override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 1c87b33e6e92a..ab5490cf25140 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -27,9 +27,8 @@ return layer_; } -std::unique_ptr -IOSSurfaceSoftware::ResourceContextMakeCurrent() { - return std::make_unique(false); +bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { + return false; } void IOSSurfaceSoftware::UpdateStorageSizeIfNecessary() { diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 1983eb7371383..bb37fa9610b2b 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -107,18 +107,15 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); - if (gl_context_ != nullptr) { - std::unique_ptr context_switch = - gl_context_->ResourceMakeCurrent(); - if (context_switch->GetSwitchResult()) { - return ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); - } + if (!gl_context_ || !gl_context_->ResourceMakeCurrent()) { + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads will be disabled. On Simulators, " + "this is expected."; + return nullptr; } - FML_DLOG(INFO) << "Could not make resource context current on IO thread. " - "Async texture uploads will be disabled. On Simulators, " - "this is expected."; - return nullptr; + + return ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } // |PlatformView| diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 59566e844d42b..d37b03aae8d9e 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -34,10 +34,8 @@ bool EmbedderSurfaceGL::IsValid() const { } // |GPUSurfaceGLDelegate| -std::unique_ptr -EmbedderSurfaceGL::GLContextMakeCurrent() { - return std::make_unique( - gl_dispatch_table_.gl_make_current_callback()); +bool EmbedderSurfaceGL::GLContextMakeCurrent() { + return gl_dispatch_table_.gl_make_current_callback(); } // |GPUSurfaceGLDelegate| @@ -81,12 +79,6 @@ EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { return gl_dispatch_table_.gl_proc_resolver; } -// |GPUSurfaceGLDelegate| -std::shared_ptr -EmbedderSurfaceGL::GetGLContextSwitchManager() { - return nullptr; -} - // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index f810597111f1f..a01fa05d4e62c 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -50,8 +50,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, sk_sp CreateResourceContext() const override; // |GPUSurfaceGLDelegate| - std::unique_ptr - GLContextMakeCurrent() override; + bool GLContextMakeCurrent() override; // |GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; @@ -74,9 +73,6 @@ class EmbedderSurfaceGL final : public EmbedderSurface, // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; - // |GPUSurfaceGLDelegate| - std::shared_ptr GetGLContextSwitchManager() override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index dd2238aba3181..e667c4c88678d 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -40,8 +40,6 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */; }; - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -138,9 +136,6 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLTestPlatformView.h; sourceTree = ""; }; - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLTestPlatformView.m; sourceTree = ""; }; - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGLTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -207,8 +202,6 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, - 68396B26235FA0D700D5E655 /* GLTestPlatformView.h */, - 68396B27235FA0D700D5E655 /* GLTestPlatformView.m */, ); path = Scenarios; sourceTree = ""; @@ -246,7 +239,6 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, - 68396B29235FBEA600D5E655 /* PlatformViewGLTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -406,7 +398,6 @@ files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, - 68396B28235FA0D700D5E655 /* GLTestPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, 0A57B3BD2323C4BD00DD9521 /* ScreenBeforeFlutter.m in Sources */, @@ -427,7 +418,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 68396B2A235FBEA600D5E655 /* PlatformViewGLTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, @@ -502,7 +492,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -555,7 +545,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -571,7 +561,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -595,7 +584,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme index cd1c5b7366a89..87799ad5e6434 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/xcshareddata/xcschemes/Scenarios.xcscheme @@ -27,15 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + + + + + * registrar = - [flutterViewController.engine registrarForPlugin:@"scenarios/glTestPlatformViewPlugin"]; - [registrar registerViewFactory:platformViewFactory withId:@"scenarios/glTestPlatformView"]; - self.window.rootViewController = flutterViewController; -} - @end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h deleted file mode 100644 index 19cefe7025986..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GLTestPlatformView : NSObject - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; - -- (UIView*)view; - -@end - -@interface GLTestPlatformViewFactory : NSObject - -- (instancetype)initWithMessenger:(NSObject*)messenger; - -@end - -@interface GLTestView : UIView - -@end - -NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m deleted file mode 100644 index 8142550e52863..0000000000000 --- a/testing/scenario_app/ios/Scenarios/Scenarios/GLTestPlatformView.m +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "GLTestPlatformView.h" - -#define GLES_SILENCE_DEPRECATION - -@implementation GLTestPlatformView { - int64_t _viewId; - GLTestView* _view; -} - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id)args - binaryMessenger:(NSObject*)messenger { - if ([super init]) { - _viewId = viewId; - _view = [[GLTestView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; - } - return self; -} - -- (UIView*)view { - return _view; -} - -@end - -@implementation GLTestPlatformViewFactory { - NSObject* _messenger; -} - -- (instancetype)initWithMessenger:(NSObject*)messenger { - self = [super init]; - if (self) { - _messenger = messenger; - } - return self; -} - -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - GLTestPlatformView* platformView = [[GLTestPlatformView alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger]; - return platformView; -} - -- (NSObject*)createArgsCodec { - return [FlutterStringCodec sharedInstance]; -} - -@end - -@interface GLTestView () - -@property(strong, nonatomic) EAGLContext* context; - -@end - -@implementation GLTestView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - _context.debugLabel = @"platform view context"; - [EAGLContext setCurrentContext:_context]; - self.backgroundColor = [UIColor redColor]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self checkEAGLContext]; - }); - } - return self; -} - -- (void)checkEAGLContext { - if ([EAGLContext currentContext] != _context) { - self.accessibilityIdentifier = @"gl_platformview_wrong_context"; - } else { - self.accessibilityIdentifier = @"gl_platformview_correct_context"; - } -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m deleted file mode 100644 index d581926ae5a6a..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGLTests.m +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface PlatformViewGLTests : XCTestCase - -@property(nonatomic, strong) XCUIApplication* application; - -@end - -@implementation PlatformViewGLTests - -- (void)setUp { - self.continueAfterFailure = NO; - - self.application = [[XCUIApplication alloc] init]; - self.application.launchArguments = @[ @"--platform-view-gl" ]; - [self.application launch]; -} - -- (void)testExample { - NSPredicate* predicateToFindPlatformView = - [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, - NSDictionary* _Nullable bindings) { - XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"gl_platformview_wrong_context"] || - [element.identifier isEqualToString:@"gl_platformview_correct_context"]; - }]; - XCUIElement* firstElement = - [self.application.otherElements elementMatchingPredicate:predicateToFindPlatformView]; - if (![firstElement waitForExistenceWithTimeout:30]) { - XCTFail(@"Failed due to not able to find platform view with 30 seconds"); - } - XCTAssertEqualObjects(firstElement.identifier, @"gl_platformview_correct_context"); -} - -@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 82b589a6681fe..7a264b70008f0 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,7 +25,6 @@ Map _scenarios = { 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), - 'platform_view_eaglcontext': PlatformViewGLScenario(window, 'null', id:6), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 86fb4df1df449..05efe52fa8b25 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -34,7 +34,7 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin PlatformViewScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -55,8 +55,8 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); } /// The platform view identifier to use for the first platform view. @@ -91,8 +91,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { - createPlatformView(window, 'platform view 1', firstId, 'scenarios/textPlatformView'); - createPlatformView(window, 'platform view 2', secondId, 'scenarios/textPlatformView'); + createPlatformView(window, 'platform view 1', firstId); + createPlatformView(window, 'platform view 2', secondId); _nextFrame = _firstFrame; } @@ -177,7 +177,7 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), super(window) { - createPlatformView(window, text, id, 'scenarios/textPlatformView'); + createPlatformView(window, text, id); } @override @@ -281,24 +281,6 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } -/// Platform view scenario for testing EAGLContext on iOS. -class PlatformViewGLScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Constructs a platform view to test EAGLContext on iOS. - PlatformViewGLScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id, 'scenarios/glTestPlatformView'); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - finishBuilderByAddingPlatformViewAndPicture(builder, 6); - } -} - mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -307,7 +289,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id, String viewType) { + void createPlatformView(Window window, String text, int id) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -331,8 +313,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - viewType.length, - ...utf8.encode(viewType), + 'scenarios/textPlatformView'.length, + ...utf8.encode('scenarios/textPlatformView'), if (Platform.isAndroid) ...[ _valueString, 'width'.length, diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart deleted file mode 100644 index e69de29bb2d1d..0000000000000 From af04338413c3ed73316350f64248a152433073b6 Mon Sep 17 00:00:00 2001 From: Siva Date: Sat, 9 Nov 2019 00:33:47 +0200 Subject: [PATCH 067/591] Manual roll of Dart e68ca9b652acdb642668a6acb5f630d5be6c03da...fa4379946109467c8a48f20f19d83d7c72968a3e (#13756) dart-lang/sdk@fa43799461 [vm/bytecode] Check number of type arguments in non-generic closures dart-lang/sdk@a01c3b464e Use TypeParameterElement(s) as free variables in FunctionType.toString(). dart-lang/sdk@9c4c49d9ee [vm/benchmarks] Reduce number of isolate spawn runs from 5 to 3. dart-lang/sdk@e3aac9e25d Check more in LegacyTypeAsserter. dart-lang/sdk@0957c13054 Check for null before dereferencing constraint text. dart-lang/sdk@fd9f79c25d [cfe] Handle deferred explicit extension access dart-lang/sdk@7c78ab75a5 Migration: allow already-migrated libraries to be opted in. dart-lang/sdk@6317779f9b Simplify TestTypeProvider constructor. dart-lang/sdk@7e8e66016a Migration: do not use `always` or `never` in already-migrated types. dart-lang/sdk@e4a60912a7 [cfe] Use library's NNBD opt-in status while serializing supertypes dart-lang/sdk@ee2efe8e58 [CFE] Add more incremental compiler tests, prepare for better invalidation strategy dart-lang/sdk@5eaccc20ba [CFE] Incremental test suite now has expect files dart-lang/sdk@ec0d042955 [infra] Support multiple named configurations in test infrastructure dart-lang/sdk@b334ea8320 [dart2js] new-rti: experiment - fault in '$is' test dart-lang/sdk@7a45228584 Use ElementsTypesMixin in TypeSystem tests. dart-lang/sdk@ec519c3d16 [vm/aot] Disable bytecode in AOT compilation pipeline dart-lang/sdk@f4780a6450 [vm] Late modifier for non-static final fields. dart-lang/sdk@23770fcc6c [vm, gc] Increase HeapPage size to 512k; increase max heap size to 32GB. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 22f5c060f4022..37859c4f5149e 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'e68ca9b652acdb642668a6acb5f630d5be6c03da', + 'dart_revision': 'fa4379946109467c8a48f20f19d83d7c72968a3e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 05918740e37ab..0fd991a4d44a2 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: d45bc36638d83a8d56574a59410d78d8 +Signature: 2c3233e5e7d7eec7f5757567f2cd61a7 UNUSED LICENSES: From bf99efe65b1d31f91c337271dac8e85904995323 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Fri, 8 Nov 2019 15:18:06 -0800 Subject: [PATCH 068/591] libtxt: pass an RTL bool flag instead of a bidiFlags enum to measureText (#13736) Fixes https://github.com/flutter/flutter/issues/41086 --- third_party/txt/BUILD.gn | 1 + third_party/txt/src/minikin/LineBreaker.cpp | 9 +++-- third_party/txt/src/txt/paragraph_txt.h | 1 + third_party/txt/tests/paragraph_unittests.cc | 33 +++++++++++++++++- .../fonts/NotoSansKhmer-Regular.ttf | Bin 0 -> 64648 bytes 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 third_party/txt/third_party/fonts/NotoSansKhmer-Regular.ttf diff --git a/third_party/txt/BUILD.gn b/third_party/txt/BUILD.gn index 93d90e45bf3a8..7111bf9fefcac 100644 --- a/third_party/txt/BUILD.gn +++ b/third_party/txt/BUILD.gn @@ -218,6 +218,7 @@ test_fixtures("txt_fixtures") { "third_party/fonts/NotoColorEmoji.ttf", "third_party/fonts/NotoNaskhArabic-Regular.ttf", "third_party/fonts/NotoSansCJK-Regular.ttc", + "third_party/fonts/NotoSansKhmer-Regular.ttf", "third_party/fonts/Regular.ttf", "third_party/fonts/Regular.ttx", "third_party/fonts/Roboto-Black.ttf", diff --git a/third_party/txt/src/minikin/LineBreaker.cpp b/third_party/txt/src/minikin/LineBreaker.cpp index 52417d28e35df..094e405bd7386 100644 --- a/third_party/txt/src/minikin/LineBreaker.cpp +++ b/third_party/txt/src/minikin/LineBreaker.cpp @@ -122,13 +122,12 @@ float LineBreaker::addStyleRun(MinikinPaint* paint, size_t end, bool isRtl) { float width = 0.0f; - int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; float hyphenPenalty = 0.0; if (paint != nullptr) { width = Layout::measureText(mTextBuf.data(), start, end - start, - mTextBuf.size(), bidiFlags, style, *paint, - typeface, mCharWidths.data() + start); + mTextBuf.size(), isRtl, style, *paint, typeface, + mCharWidths.data() + start); // a heuristic that seems to perform well hyphenPenalty = @@ -195,12 +194,12 @@ float LineBreaker::addStyleRun(MinikinPaint* paint, paint->hyphenEdit = HyphenEdit::editForThisLine(hyph); const float firstPartWidth = Layout::measureText( mTextBuf.data(), lastBreak, j - lastBreak, mTextBuf.size(), - bidiFlags, style, *paint, typeface, nullptr); + isRtl, style, *paint, typeface, nullptr); ParaWidth hyphPostBreak = lastBreakWidth + firstPartWidth; paint->hyphenEdit = HyphenEdit::editForNextLine(hyph); const float secondPartWidth = Layout::measureText( - mTextBuf.data(), j, afterWord - j, mTextBuf.size(), bidiFlags, + mTextBuf.data(), j, afterWord - j, mTextBuf.size(), isRtl, style, *paint, typeface, nullptr); ParaWidth hyphPreBreak = postBreak - secondPartWidth; diff --git a/third_party/txt/src/txt/paragraph_txt.h b/third_party/txt/src/txt/paragraph_txt.h index 896d17f9eeac8..9a0e2681f2039 100644 --- a/third_party/txt/src/txt/paragraph_txt.h +++ b/third_party/txt/src/txt/paragraph_txt.h @@ -161,6 +161,7 @@ class ParagraphTxt : public Paragraph { FRIEND_TEST(ParagraphTest, InlinePlaceholder0xFFFCParagraph); FRIEND_TEST(ParagraphTest, FontFeaturesParagraph); FRIEND_TEST(ParagraphTest, GetGlyphPositionAtCoordinateSegfault); + FRIEND_TEST(ParagraphTest, KhmerLineBreaker); // Starting data to layout. std::vector text_; diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 9ebac3fdeee8e..c6ffe5a20f03c 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -354,7 +354,7 @@ TEST_F(ParagraphTest, DISABLE_ON_MAC(LineMetricsParagraph2)) { ASSERT_EQ(paragraph->GetLineMetrics()[0].hard_break, false); ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].ascent, 27.84); ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].descent, 7.6799998); - ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].width, 349.26953); + ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].width, 348.61328); ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].left, 0.0); ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].baseline, 28.32); ASSERT_EQ(paragraph->GetLineMetrics()[0].line_number, 0ull); @@ -5815,4 +5815,35 @@ TEST_F(ParagraphTest, FontFeaturesParagraph) { ASSERT_TRUE(Snapshot()); } +TEST_F(ParagraphTest, KhmerLineBreaker) { + const char* text = "និងក្មេងចង់ផ្ទៃសមុទ្រសែនខៀវស្រងាត់"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Noto Sans Khmer"); + text_style.font_size = 24; + text_style.color = SK_ColorBLACK; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + paragraph->Layout(200); + paragraph->Paint(GetCanvas(), 0, 0); + + ASSERT_EQ(paragraph->glyph_lines_.size(), 3ull); + EXPECT_EQ(paragraph->glyph_lines_[0].positions.size(), 7ul); + EXPECT_EQ(paragraph->glyph_lines_[1].positions.size(), 12ul); + EXPECT_EQ(paragraph->glyph_lines_[2].positions.size(), 7ul); + + ASSERT_TRUE(Snapshot()); +} + } // namespace txt diff --git a/third_party/txt/third_party/fonts/NotoSansKhmer-Regular.ttf b/third_party/txt/third_party/fonts/NotoSansKhmer-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9aaea35610f1fb4d139afb004d3c757d3a831f3e GIT binary patch literal 64648 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 literal 0 HcmV?d00001 From 2f69e38160a301f4070a8be25dcb66120e4548ac Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Fri, 8 Nov 2019 15:53:42 -0800 Subject: [PATCH 069/591] Implement Path.computeMetrics in the CanvasKit backend (#13760) * Implement Path.computeMetrics in the CanvasKit backend * Add license for path_metrics.dart --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 3 +- .../lib/src/engine/compositor/path.dart | 2 +- .../src/engine/compositor/path_metrics.dart | 124 ++++++++++++++++++ lib/web_ui/lib/src/ui/canvas.dart | 6 +- 5 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/compositor/path_metrics.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4e36d64a55529..d144243bbc9e1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -366,6 +366,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer_scene_builder.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/layer_tree.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/path.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/path_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/picture.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/picture_recorder.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/platform_message.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 0579928cfdd9d..d44e33dd9d176 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -5,7 +5,7 @@ library engine; import 'dart:async'; -import 'dart:collection' show ListBase; +import 'dart:collection' show ListBase, IterableBase; import 'dart:convert' hide Codec; import 'dart:developer' as developer; import 'dart:html' as html; @@ -34,6 +34,7 @@ part 'engine/compositor/layer.dart'; part 'engine/compositor/layer_scene_builder.dart'; part 'engine/compositor/layer_tree.dart'; part 'engine/compositor/path.dart'; +part 'engine/compositor/path_metrics.dart'; part 'engine/compositor/picture.dart'; part 'engine/compositor/picture_recorder.dart'; part 'engine/compositor/platform_message.dart'; diff --git a/lib/web_ui/lib/src/engine/compositor/path.dart b/lib/web_ui/lib/src/engine/compositor/path.dart index 43932a0fdbcac..44f07bb671e50 100644 --- a/lib/web_ui/lib/src/engine/compositor/path.dart +++ b/lib/web_ui/lib/src/engine/compositor/path.dart @@ -156,7 +156,7 @@ class SkPath implements ui.Path { @override ui.PathMetrics computeMetrics({bool forceClosed = false}) { - throw 'computeMetrics'; + return SkPathMetrics(this, forceClosed); } @override diff --git a/lib/web_ui/lib/src/engine/compositor/path_metrics.dart b/lib/web_ui/lib/src/engine/compositor/path_metrics.dart new file mode 100644 index 0000000000000..71f73205283aa --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/path_metrics.dart @@ -0,0 +1,124 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +class SkPathMetrics extends IterableBase + implements ui.PathMetrics { + SkPathMetrics(SkPath path, bool forceClosed) + : _iterator = SkPathMetricIterator._(_SkPathMeasure(path, forceClosed)); + + final Iterator _iterator; + + @override + Iterator get iterator => _iterator; +} + +class SkPathMetricIterator implements Iterator { + SkPathMetricIterator._(this._pathMeasure) : assert(_pathMeasure != null); + + _SkPathMetric _pathMetric; + _SkPathMeasure _pathMeasure; + + @override + ui.PathMetric get current => _pathMetric; + + @override + bool moveNext() { + if (_pathMeasure._nextContour()) { + _pathMetric = _SkPathMetric._(_pathMeasure); + return true; + } + _pathMetric = null; + return false; + } +} + +class _SkPathMetric implements ui.PathMetric { + _SkPathMetric._(this._measure) + : assert(_measure != null), + length = _measure.length(_measure.currentContourIndex), + isClosed = _measure.isClosed(_measure.currentContourIndex), + contourIndex = _measure.currentContourIndex; + + @override + final double length; + + @override + final bool isClosed; + + @override + final int contourIndex; + + final _SkPathMeasure _measure; + + @override + ui.Tangent getTangentForOffset(double distance) { + return _measure.getTangentForOffset(contourIndex, distance); + } + + @override + ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { + return _measure.extractPath(contourIndex, start, end, + startWithMoveTo: startWithMoveTo); + } + + @override + String toString() => 'PathMetric{length: $length, isClosed: $isClosed, ' + 'contourIndex: $contourIndex}'; +} + +class _SkPathMeasure { + _SkPathMeasure(SkPath path, bool forceClosed) { + currentContourIndex = -1; + pathMeasure = js.JsObject(canvasKit['SkPathMeasure'], [ + path._skPath, + forceClosed, + 1, + ]); + } + + js.JsObject pathMeasure; + + double length(int contourIndex) { + assert(contourIndex == currentContourIndex, + 'PathMetrics are invalid if it is not the current contour.'); + return pathMeasure.callMethod('getLength'); + } + + ui.Tangent getTangentForOffset(int contourIndex, double distance) { + assert(contourIndex == currentContourIndex, + 'PathMetrics are invalid if it is not the current contour.'); + final js.JsObject posTan = + pathMeasure.callMethod('getPosTan', [distance]); + return ui.Tangent( + ui.Offset(posTan[0], posTan[1]), + ui.Offset(posTan[2], posTan[3]), + ); + } + + ui.Path extractPath(int contourIndex, double start, double end, + {bool startWithMoveTo = true}) { + assert(contourIndex == currentContourIndex, + 'PathMetrics are invalid if it is not the current contour.'); + // TODO(het): Add this once `getSegment` is added to CanvasKit. + throw UnimplementedError('extractPath'); + } + + bool isClosed(int contourIndex) { + assert(contourIndex == currentContourIndex, + 'PathMetrics are invalid if it is not the current contour.'); + return pathMeasure.callMethod('isClosed'); + } + + bool _nextContour() { + final bool next = pathMeasure.callMethod('nextContour'); + if (next) { + currentContourIndex++; + } + return next; + } + + int currentContourIndex; +} diff --git a/lib/web_ui/lib/src/ui/canvas.dart b/lib/web_ui/lib/src/ui/canvas.dart index 7c861fd0f14f4..941786abd5947 100644 --- a/lib/web_ui/lib/src/ui/canvas.dart +++ b/lib/web_ui/lib/src/ui/canvas.dart @@ -2343,11 +2343,11 @@ class PathMetricIterator implements Iterator { /// contour. When the next contour's [PathMetric] is obtained, this object /// becomes invalid. class PathMetric { - final Path path; - final bool forceClosed; + final Path _path; + final bool _forceClosed; /// Create a new empty [Path] object. - PathMetric._(this.path, this.forceClosed); + PathMetric._(this._path, this._forceClosed); /// Return the total length of the current contour. double get length => throw UnimplementedError(); From 0fce6905f37367f973c9825385861f56a77e3621 Mon Sep 17 00:00:00 2001 From: Siva Date: Sat, 9 Nov 2019 01:55:03 +0200 Subject: [PATCH 070/591] Manual Dart roll fa4379946109467c8a48f20f19d83d7c72968a3e...d45c3d15cb3cea0104a87697c085259666eec528 (#13761) dart-lang/sdk@d45c3d15cb [VM/nnbd] Make Nullability and NNBDMode class enums to avoid name conflicts. dart-lang/sdk@cfec969eff Update dartdoc version to 0.29.1. dart-lang/sdk@e3e0ef8548 Make explanations for unchanged types optional and disabled by default dart-lang/sdk@e39b0f8d99 Disallow a deferred import of a library with extensions if any extensions are visible dart-lang/sdk@9c983d2ee3 [cfe] Support extensions via prefix dart-lang/sdk@5f5ce5d119 Include type parameter bounds into string expections for subtyping. dart-lang/sdk@dc69edcdb3 NullabilityEliminator for removing nullabilities in legacy libraries. dart-lang/sdk@7ad7e6202d Allow extensions imported with a prefix to be accessible dart-lang/sdk@ea57b1e62c [VM/nnbd] Pass nullability when creating Class::DeclarationType. --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 37859c4f5149e..4951040082f68 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'fa4379946109467c8a48f20f19d83d7c72968a3e', + 'dart_revision': 'd45c3d15cb3cea0104a87697c085259666eec528', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -231,7 +231,7 @@ deps = { Var('dart_git') + '/dart2js_info.git' + '@' + Var('dart_dart2js_info_tag'), 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@v0.29.0', + Var('dart_git') + '/dartdoc.git@v0.29.1', 'src/third_party/dart/third_party/pkg/ffi': Var('dart_git') + '/ffi.git' + '@' + Var('dart_ffi_tag'), diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 0fd991a4d44a2..931de974f3032 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 2c3233e5e7d7eec7f5757567f2cd61a7 +Signature: 9355024c19864ce227141256962ef660 UNUSED LICENSES: From b4a50d7ab192d49836c16771c0e4866ea437ab4d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 19:11:35 -0500 Subject: [PATCH 071/591] Roll src/third_party/skia c88d1774ed50..4135cf0b57c2 (13 commits) (#13764) https://skia.googlesource.com/skia.git/+log/c88d1774ed50..4135cf0b57c2 git log c88d1774ed50..4135cf0b57c2 --date=short --no-merges --format='%ad %ae %s' 2019-11-08 mtklein@google.com use round() instead of trunc() to f32->unorm 2019-11-08 jlavrova@google.com Yet another build fix 2019-11-08 mtklein@google.com rename to_i32 -> trunc, and add round 2019-11-08 mtklein@google.com avoid redundant clamps 2019-11-08 mtklein@google.com don't clamp alpha, it's already [0,1] 2019-11-08 mtklein@google.com native f32 min/max 2019-11-08 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-08 mtklein@google.com convert SkVMBlitter over to floats 2019-11-08 csmartdalton@google.com Always attach stencil buffers with exact sample count matches 2019-11-08 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial). 2019-11-08 jlavrova@google.com Fixing no icu situation 2019-11-08 reed@google.com add flatten test to SkFont 2019-11-08 csmartdalton@google.com Defer stencil attachment until opsTask execution Created with: gclient setdep -r src/third_party/skia@4135cf0b57c2 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC ethannicholas@google.com on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md Bug: None TBR=ethannicholas@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4951040082f68..ce04a3010fc8f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c88d1774ed5074c8cd035a279bb6f599139e7484', + 'skia_revision': '4135cf0b57c2ef71b60ecf973d93eab37032f4f7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 759edca261f3b..0de77d40ceafc 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 76b5c1ee75cb1ba1b7cf328576dc0703 +Signature: 032ddb8e2831962c302984a5b24f2911 UNUSED LICENSES: From a8f678e01955d1ded7a4208419c8a84cb2d0d69c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 8 Nov 2019 19:18:41 -0500 Subject: [PATCH 072/591] Roll fuchsia/sdk/core/mac-amd64 from KRali... to eq_Rz... (#13766) Roll fuchsia/sdk/core/mac-amd64 from KRali... to eq_Rz... If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. 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/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index ce04a3010fc8f..b6cf17faa5958 100644 --- a/DEPS +++ b/DEPS @@ -542,7 +542,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'KRali1kRjy2d-AU4s8vUOEeBiqRW9rkDXW0Ovc-ac6gC' + 'version': 'eq_RznhOOqBWirQ97NtrVCy1swWdw8dWkpk07aiMiqgC' } ], 'condition': 'host_os == "mac"', From e0b9a2bb23e4270edcf6b4ce561c0de2143c4473 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 8 Nov 2019 16:27:00 -0800 Subject: [PATCH 073/591] Change wordBoundary to take dynamic temporarily (#13765) Converting the argument to Paragraph.wordBoundary to dynamic temporarily until the framework code is converted to send a TextPosition instead of an int. I'll submit this, then update the framework side to send a TextPosition, and expect a TextRange or a List, and then submit that, then I'll change this code to send a TextRange and take a TextPostion only, removing the dynamic here. Once that's done, I'll remove the code in the framework that expects a TextRange or a List, and have it just expect a TextRange. This is so that we can change the API without breaking the builds. Landing on red to kick the engine builds. --- lib/ui/text.dart | 12 ++++++++++- lib/web_ui/lib/src/engine/text/paragraph.dart | 21 +++++++++++++++---- lib/web_ui/lib/src/ui/text.dart | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 94a88b0cb32c0..a2a8df205793c 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1871,7 +1871,17 @@ class Paragraph extends NativeFieldWrapperClass2 { /// on both sides. In such cases, this method will return [offset, offset+1]. /// Word boundaries are defined more precisely in Unicode Standard Annex #29 /// http://www.unicode.org/reports/tr29/#Word_Boundaries - List getWordBoundary(int offset) native 'Paragraph_getWordBoundary'; + List getWordBoundary(dynamic position) { + // TODO(gspencergoog): have this take only a TextPosition once the framework + // code is calling it with that. + if (position is TextPosition) { + return _getWordBoundary(position.offset); + } else { + final int offset = position; + return _getWordBoundary(offset); + } + } + List _getWordBoundary(int offset) native 'Paragraph_getWordBoundary'; // Redirecting the paint function in this way solves some dependency problems // in the C++ code. If we straighten out the C++ dependencies, we can remove diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 6574cbddbc1d8..0c53962ddc020 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -281,13 +281,26 @@ class EngineParagraph implements ui.Paragraph { } @override - List getWordBoundary(int offset) { + List getWordBoundary(dynamic position) { + // TODO(gspencergoog): have this take only a TextPosition once the framework + // code is calling it with that. + if (position is ui.TextPosition) { + ui.TextPosition textPosition = position; + if (_plainText == null) { + return [textPosition.offset, textPosition.offset]; + } + + final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset); + final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset); + return [start, end]; + } + if (_plainText == null) { - return [offset, offset]; + return [position, position]; } - final int start = WordBreaker.prevBreakIndex(_plainText, offset); - final int end = WordBreaker.nextBreakIndex(_plainText, offset); + final int start = WordBreaker.prevBreakIndex(_plainText, position); + final int end = WordBreaker.nextBreakIndex(_plainText, position); return [start, end]; } diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 10d9f760c1aba..774787ae648f0 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1242,7 +1242,7 @@ abstract class Paragraph { /// on both sides. In such cases, this method will return [offset, offset+1]. /// Word boundaries are defined more precisely in Unicode Standard Annex #29 /// http://www.unicode.org/reports/tr29/#Word_Boundaries - List getWordBoundary(int offset); + List getWordBoundary(dynamic position); /// Returns a list of text boxes that enclose all placeholders in the paragraph. /// From 31cd2dfca22a282e53eb449d8a3be93b8365c1bc Mon Sep 17 00:00:00 2001 From: gabeschine Date: Fri, 8 Nov 2019 17:13:17 -0800 Subject: [PATCH 074/591] Remove usage of fuchsia.modular.Clipboard. (#13763) It is deprecated and will go away. --- .../integration/meta/dart_aot_runner_test.cmx | 1 - .../integration/meta/dart_jit_runner_test.cmx | 1 - .../dart_runner/vmservice/meta/vmservice.cmx | 1 - shell/platform/fuchsia/flutter/BUILD.gn | 1 - .../fuchsia/flutter/engine_flutter_runner.gni | 1 - .../platform/fuchsia/flutter/platform_view.cc | 30 ++----------------- .../platform/fuchsia/flutter/platform_view.h | 2 -- 7 files changed, 3 insertions(+), 34 deletions(-) diff --git a/shell/platform/fuchsia/dart_runner/integration/meta/dart_aot_runner_test.cmx b/shell/platform/fuchsia/dart_runner/integration/meta/dart_aot_runner_test.cmx index 273ac887dcb32..b89c069cc0457 100644 --- a/shell/platform/fuchsia/dart_runner/integration/meta/dart_aot_runner_test.cmx +++ b/shell/platform/fuchsia/dart_runner/integration/meta/dart_aot_runner_test.cmx @@ -8,7 +8,6 @@ "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", - "fuchsia.modular.Clipboard", "fuchsia.modular.ContextWriter", "fuchsia.modular.ModuleContext", "fuchsia.netstack.Netstack", diff --git a/shell/platform/fuchsia/dart_runner/integration/meta/dart_jit_runner_test.cmx b/shell/platform/fuchsia/dart_runner/integration/meta/dart_jit_runner_test.cmx index 02feaf8ca3776..b2857816cd77c 100644 --- a/shell/platform/fuchsia/dart_runner/integration/meta/dart_jit_runner_test.cmx +++ b/shell/platform/fuchsia/dart_runner/integration/meta/dart_jit_runner_test.cmx @@ -11,7 +11,6 @@ "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", - "fuchsia.modular.Clipboard", "fuchsia.modular.ContextWriter", "fuchsia.modular.ModuleContext", "fuchsia.netstack.Netstack", diff --git a/shell/platform/fuchsia/dart_runner/vmservice/meta/vmservice.cmx b/shell/platform/fuchsia/dart_runner/vmservice/meta/vmservice.cmx index c3d95e3827a32..0d72fa73bfea7 100644 --- a/shell/platform/fuchsia/dart_runner/vmservice/meta/vmservice.cmx +++ b/shell/platform/fuchsia/dart_runner/vmservice/meta/vmservice.cmx @@ -8,7 +8,6 @@ "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", - "fuchsia.modular.Clipboard", "fuchsia.modular.ContextWriter", "fuchsia.modular.ModuleContext", "fuchsia.netstack.Netstack", diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 452e3f76f94f3..1204b23b60b9f 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -278,7 +278,6 @@ executable("flutter_runner_unittests") { ":aot", ":flutter_runner_fixtures", "//build/fuchsia/fidl:fuchsia.accessibility.semantics", - "//build/fuchsia/fidl:fuchsia.modular", "//build/fuchsia/pkg:async-loop-cpp", "//build/fuchsia/pkg:async-loop-default", "//build/fuchsia/pkg:scenic_cpp", diff --git a/shell/platform/fuchsia/flutter/engine_flutter_runner.gni b/shell/platform/fuchsia/flutter/engine_flutter_runner.gni index ce4fd421d6330..59ec0366d6786 100644 --- a/shell/platform/fuchsia/flutter/engine_flutter_runner.gni +++ b/shell/platform/fuchsia/flutter/engine_flutter_runner.gni @@ -115,7 +115,6 @@ template("flutter_runner") { "$fuchsia_sdk_root/fidl:fuchsia.images", "$fuchsia_sdk_root/fidl:fuchsia.intl", "$fuchsia_sdk_root/fidl:fuchsia.io", - "$fuchsia_sdk_root/fidl:fuchsia.modular", "$fuchsia_sdk_root/fidl:fuchsia.sys", "$fuchsia_sdk_root/fidl:fuchsia.ui.app", "$fuchsia_sdk_root/fidl:fuchsia.ui.scenic", diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index 38832a539ab28..541851ae5afa5 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -110,15 +110,11 @@ PlatformView::PlatformView( SetInterfaceErrorHandler(session_listener_binding_, "SessionListener"); SetInterfaceErrorHandler(ime_, "Input Method Editor"); SetInterfaceErrorHandler(text_sync_service_, "Text Sync Service"); - SetInterfaceErrorHandler(clipboard_, "Clipboard"); SetInterfaceErrorHandler(parent_environment_service_provider_, "Parent Environment Service Provider"); - // Access the clipboard. + // Access the IME service. parent_environment_service_provider_ = parent_environment_service_provider_handle.Bind(); - parent_environment_service_provider_.get()->ConnectToService( - fuchsia::modular::Clipboard::Name_, - clipboard_.NewRequest().TakeChannel()); parent_environment_service_provider_.get()->ConnectToService( fuchsia::ui::input::ImeService::Name_, @@ -638,28 +634,8 @@ void PlatformView::HandleFlutterPlatformChannelPlatformMessage( return; } - fml::RefPtr response = message->response(); - if (method->value == "Clipboard.setData") { - auto text = root["args"]["text"].GetString(); - clipboard_->Push(text); - response->CompleteEmpty(); - } else if (method->value == "Clipboard.getData") { - clipboard_->Peek([response](fidl::StringPtr text) { - rapidjson::StringBuffer json_buffer; - rapidjson::Writer writer(json_buffer); - writer.StartArray(); - writer.StartObject(); - writer.Key("text"); - writer.String(text.value_or("")); - writer.EndObject(); - writer.EndArray(); - std::string result = json_buffer.GetString(); - response->Complete(std::make_unique( - std::vector{result.begin(), result.end()})); - }); - } else { - response->CompleteEmpty(); - } + // Fuchsia does not handle any platform messages at this time. + message->response()->CompleteEmpty(); } // Channel handler for kTextInputChannel diff --git a/shell/platform/fuchsia/flutter/platform_view.h b/shell/platform/fuchsia/flutter/platform_view.h index 6317c75c48f1f..ef30c0a3de753 100644 --- a/shell/platform/fuchsia/flutter/platform_view.h +++ b/shell/platform/fuchsia/flutter/platform_view.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -91,7 +90,6 @@ class PlatformView final : public flutter::PlatformView, fuchsia::ui::input::ImeServicePtr text_sync_service_; fuchsia::sys::ServiceProviderPtr parent_environment_service_provider_; - fuchsia::modular::ClipboardPtr clipboard_; std::unique_ptr surface_; flutter::LogicalMetrics metrics_; fuchsia::ui::gfx::Metrics scenic_metrics_; From 8a99d1074821881fe65966cb39ccfd0cd93c9ee2 Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Fri, 8 Nov 2019 17:14:50 -0800 Subject: [PATCH 075/591] Turn on RasterCache based on view hierarchy (#13762) This is a duplicate of flutter/engine#13360 with the test switched to use the software backend instead of the GL backend. After some debugging and testing on another GL embedder I think the issue with the test is some bug having to do with the GL implementation in the test harness specifically. Fixes flutter/flutter#38903 --- ci/licenses_golden/licenses_flutter | 1 + flow/layers/container_layer.cc | 14 ++ flow/layers/layer.h | 1 + flow/layers/opacity_layer.cc | 8 +- flow/layers/platform_view_layer.cc | 1 + flow/raster_cache.cc | 47 ++-- flow/raster_cache.h | 2 + shell/common/shell.cc | 2 +- shell/common/shell.h | 2 +- shell/platform/embedder/BUILD.gn | 2 + shell/platform/embedder/embedder_engine.cc | 5 + shell/platform/embedder/embedder_engine.h | 2 + shell/platform/embedder/fixtures/main.dart | 44 ++++ .../verifyb143464703_soft_noxform.png | Bin 0 -> 6985 bytes .../embedder/tests/embedder_assertions.h | 5 + .../embedder/tests/embedder_unittests.cc | 207 ++++++++++++++++-- 16 files changed, 296 insertions(+), 47 deletions(-) create mode 100644 shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index d144243bbc9e1..5587e1b6bdc5b 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -878,6 +878,7 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor.png FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor_with_xform.png FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.cc diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 31a5a255afca9..d5c6a2a03a34a 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -26,14 +26,28 @@ void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { + // Platform views have no children, so context->has_platform_view should + // always be false. + FML_DCHECK(!context->has_platform_view); + bool child_has_platform_view = false; for (auto& layer : layers_) { + // Reset context->has_platform_view to false so that layers aren't treated + // as if they have a platform view based on one being previously found in a + // sibling tree. + context->has_platform_view = false; + layer->Preroll(context, child_matrix); if (layer->needs_system_composite()) { set_needs_system_composite(true); } child_paint_bounds->join(layer->paint_bounds()); + + child_has_platform_view = + child_has_platform_view || context->has_platform_view; } + + context->has_platform_view = child_has_platform_view; } void ContainerLayer::PaintChildren(PaintContext& context) const { diff --git a/flow/layers/layer.h b/flow/layers/layer.h index da2dcd8b21ce3..66944376e8ce8 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -58,6 +58,7 @@ struct PrerollContext { TextureRegistry& texture_registry; const bool checkerboard_offscreen_layers; float total_elevation = 0.0f; + bool has_platform_view = false; }; // Represents a single composited layer. Created on the UI thread but then diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 014ee6736d92a..a27981650d2bf 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -46,7 +46,7 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); // See |EnsureSingleChild|. FML_DCHECK(layers().size() == 1); - if (context->view_embedder == nullptr && context->raster_cache && + if (!context->has_platform_view && context->raster_cache && SkRect::Intersects(context->cull_rect, paint_bounds())) { Layer* child = layers()[0].get(); SkMatrix ctm = child_matrix; @@ -75,11 +75,7 @@ void OpacityLayer::Paint(PaintContext& context) const { // See |EnsureSingleChild|. FML_DCHECK(layers().size() == 1); - // Embedded platform views are changing the canvas in the middle of the paint - // traversal. To make sure we paint on the right canvas, when the embedded - // platform views preview is enabled (context.view_embedded is not null) we - // don't use the cache. - if (context.view_embedder == nullptr && context.raster_cache) { + if (context.raster_cache) { const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); RasterCacheResult child_cache = context.raster_cache->Get(layers()[0].get(), ctm); diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 15f9edf9719ee..3f72993f97d66 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -23,6 +23,7 @@ void PlatformViewLayer::Preroll(PrerollContext* context, "does not support embedding"; return; } + context->has_platform_view = true; std::unique_ptr params = std::make_unique(); params->offsetPixels = diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index a2831f83e3fdc..0a2e084c96455 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -157,27 +157,28 @@ void RasterCache::Prepare(PrerollContext* context, entry.access_count = ClampSize(entry.access_count + 1, 0, access_threshold_); entry.used_this_frame = true; if (!entry.image.is_valid()) { - entry.image = Rasterize(context->gr_context, ctm, context->dst_color_space, - checkerboard_images_, layer->paint_bounds(), - [layer, context](SkCanvas* canvas) { - SkISize canvas_size = canvas->getBaseLayerSize(); - SkNWayCanvas internal_nodes_canvas( - canvas_size.width(), canvas_size.height()); - internal_nodes_canvas.addCanvas(canvas); - Layer::PaintContext paintContext = { - (SkCanvas*)&internal_nodes_canvas, - canvas, - context->gr_context, - nullptr, - context->raster_time, - context->ui_time, - context->texture_registry, - context->raster_cache, - context->checkerboard_offscreen_layers}; - if (layer->needs_painting()) { - layer->Paint(paintContext); - } - }); + entry.image = Rasterize( + context->gr_context, ctm, context->dst_color_space, + checkerboard_images_, layer->paint_bounds(), + [layer, context](SkCanvas* canvas) { + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), + canvas_size.height()); + internal_nodes_canvas.addCanvas(canvas); + Layer::PaintContext paintContext = { + (SkCanvas*)&internal_nodes_canvas, + canvas, + context->gr_context, + nullptr, + context->raster_time, + context->ui_time, + context->texture_registry, + context->has_platform_view ? nullptr : context->raster_cache, + context->checkerboard_offscreen_layers}; + if (layer->needs_painting()) { + layer->Paint(paintContext); + } + }); } } @@ -250,6 +251,10 @@ void RasterCache::Clear() { layer_cache_.clear(); } +size_t RasterCache::GetCachedEntriesCount() const { + return layer_cache_.size() + picture_cache_.size(); +} + void RasterCache::SetCheckboardCacheImages(bool checkerboard) { if (checkerboard_images_ == checkerboard) { return; diff --git a/flow/raster_cache.h b/flow/raster_cache.h index f008f2459cc30..3ea92588e1fd7 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -100,6 +100,8 @@ class RasterCache { void SetCheckboardCacheImages(bool checkerboard); + size_t GetCachedEntriesCount() const; + private: struct Entry { bool used_this_frame = false; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index bbd4320d42f09..8443178a8e0d2 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -523,7 +523,7 @@ const TaskRunners& Shell::GetTaskRunners() const { return task_runners_; } -fml::WeakPtr Shell::GetRasterizer() { +fml::WeakPtr Shell::GetRasterizer() const { FML_DCHECK(is_setup_); return weak_rasterizer_; } diff --git a/shell/common/shell.h b/shell/common/shell.h index 74b3406ae3141..c392f08fbb9d4 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -210,7 +210,7 @@ class Shell final : public PlatformView::Delegate, /// /// @return A weak pointer to the rasterizer. /// - fml::WeakPtr GetRasterizer(); + fml::WeakPtr GetRasterizer() const; //------------------------------------------------------------------------------ /// @brief Engines may only be accessed on the UI thread. This method is diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index e6d0ccdcfc2e4..a6429c6f837f3 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -98,6 +98,7 @@ test_fixtures("fixtures") { "fixtures/scene_without_custom_compositor.png", "fixtures/scene_without_custom_compositor_with_xform.png", "fixtures/verifyb143464703.png", + "fixtures/verifyb143464703_soft_noxform.png", ] } @@ -125,6 +126,7 @@ if (current_toolchain == host_toolchain) { deps = [ ":embedder", ":fixtures", + "$flutter_root/flow", "$flutter_root/lib/ui", "$flutter_root/runtime", "$flutter_root/testing:dart", diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 4fcf7ae8937e1..631ef6f23a6a4 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -247,4 +247,9 @@ bool EmbedderEngine::RunTask(const FlutterTask* task) { task->task); } +const Shell& EmbedderEngine::GetShell() const { + FML_DCHECK(shell_); + return *shell_.get(); +} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 1d3255f4360b5..94026d9f9646f 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -80,6 +80,8 @@ class EmbedderEngine { bool RunTask(const FlutterTask* task); + const Shell& GetShell() const; + private: const std::unique_ptr thread_host_; TaskRunners task_runners_; diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 7ba9d55831a6d..e18a7fa3064c4 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -206,6 +206,50 @@ void can_composite_platform_views() { window.scheduleFrame(); } +@pragma('vm:entry-point') +void can_composite_platform_views_with_opacity() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + + // Root node + builder.pushOffset(1.0, 2.0); + + // First sibling layer (no platform view, should be cached) + builder.pushOpacity(127); + builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture()); + builder.pop(); + + // Second sibling layer (platform view, should not be cached) + builder.pushOpacity(127); + builder.addPlatformView(42, width: 123.0, height: 456.0); + builder.pop(); + + // Third sibling layer (no platform view, should be cached) + builder.pushOpacity(127); + builder.addPicture(Offset(2.0, 1.0), CreateSimplePicture()); + builder.pop(); + + signalNativeTest(); // Signal 2 + window.render(builder.build()); + }; + signalNativeTest(); // Signal 1 + window.scheduleFrame(); +} + +@pragma('vm:entry-point') +void can_composite_with_opacity() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.pushOpacity(127); + builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture()); + builder.pop(); // offset + signalNativeTest(); // Signal 2 + window.render(builder.build()); + }; + signalNativeTest(); // Signal 1 + window.scheduleFrame(); +} + Picture CreateColoredBox(Color color, Size size) { Paint paint = Paint(); paint.color = color; diff --git a/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png b/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png new file mode 100644 index 0000000000000000000000000000000000000000..b89b38290b87c9adfd1b1ae13adbed4b86a577a2 GIT binary patch literal 6985 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 TJP-d#0bs+rO>2dp>^$-}t(engine); +} + #endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_ASSERTIONS_H_ diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index c6b0a505f3fa9..643034888aaa9 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -7,6 +7,8 @@ #include #include "embedder.h" +#include "embedder_engine.h" +#include "flutter/flow/raster_cache.h" #include "flutter/fml/file.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" @@ -660,6 +662,180 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) { latch.Wait(); } +//------------------------------------------------------------------------------ +/// Layers in a hierarchy containing a platform view should not be cached. The +/// other layers in the hierarchy should be, however. +TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("can_composite_platform_views_with_opacity"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLFramebuffer); + + fml::CountDownLatch setup(3); + fml::CountDownLatch verify(1); + + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 3u); + + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.struct_size = sizeof(backing_store); + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0, 0); + + ASSERT_EQ(*layers[0], layer); + } + + { + FlutterPlatformView platform_view = {}; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(123.0, 456.0); + layer.offset = FlutterPointMake(1.0, 2.0); + + ASSERT_EQ(*layers[1], layer); + } + + { + FlutterBackingStore backing_store = *layers[2]->backing_store; + backing_store.struct_size = sizeof(backing_store); + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[2], layer); + } + + setup.CountDown(); + }); + + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&setup](Dart_NativeArguments args) { setup.CountDown(); })); + + UniqueEngine engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + setup.Wait(); + const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); + shell.GetTaskRunners().GetGPUTaskRunner()->PostTask([&] { + const flutter::RasterCache& raster_cache = + shell.GetRasterizer()->compositor_context()->raster_cache(); + // 3 layers total, but one of them had the platform view. So the cache + // should only have 2 entries. + ASSERT_EQ(raster_cache.GetCachedEntriesCount(), 2u); + verify.CountDown(); + }); + + verify.Wait(); +} + +//------------------------------------------------------------------------------ +/// The RasterCache should normally be enabled. +/// +TEST_F(EmbedderTest, RasterCacheEnabled) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("can_composite_with_opacity"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLFramebuffer); + + fml::CountDownLatch setup(3); + fml::CountDownLatch verify(1); + + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 1u); + + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.struct_size = sizeof(backing_store); + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0, 0); + + ASSERT_EQ(*layers[0], layer); + } + + setup.CountDown(); + }); + + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&setup](Dart_NativeArguments args) { setup.CountDown(); })); + + UniqueEngine engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + setup.Wait(); + const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); + shell.GetTaskRunners().GetGPUTaskRunner()->PostTask([&] { + const flutter::RasterCache& raster_cache = + shell.GetRasterizer()->compositor_context()->raster_cache(); + ASSERT_EQ(raster_cache.GetCachedEntriesCount(), 1u); + verify.CountDown(); + }); + + verify.Wait(); +} + //------------------------------------------------------------------------------ /// Must be able to render using a custom compositor whose render targets for /// the individual layers are OpenGL textures. @@ -2831,21 +3007,16 @@ TEST_F(EmbedderTest, CanQueryDartAOTMode) { flutter::DartVM::IsRunningPrecompiledCode()); } -TEST_F(EmbedderTest, VerifyB143464703) { +TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { auto& context = GetEmbedderContext(); EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024)); + builder.SetSoftwareRendererConfig(SkISize::Make(1024, 600)); builder.SetCompositor(); builder.SetDartEntrypoint("verify_b143464703"); context.GetCompositor().SetRenderTargetType( - EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); - - const auto root_surface_transformation = - SkMatrix().preTranslate(0, 1024).preRotate(-90, 0, 0); - - context.SetRootSurfaceTransformation(root_surface_transformation); + EmbedderTestCompositor::RenderTargetType::kSoftwareBuffer); fml::CountDownLatch latch(2); context.GetCompositor().SetNextPresentCallback( @@ -2855,15 +3026,14 @@ TEST_F(EmbedderTest, VerifyB143464703) { // Layer 0 (Root) { FlutterBackingStore backing_store = *layers[0]->backing_store; - backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.type = kFlutterBackingStoreTypeSoftware; backing_store.did_update = true; - backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; FlutterLayer layer = {}; layer.struct_size = sizeof(layer); layer.type = kFlutterLayerContentTypeBackingStore; layer.backing_store = &backing_store; - layer.size = FlutterSizeMake(600.0, 1024.0); + layer.size = FlutterSizeMake(1024.0, 600.0); layer.offset = FlutterPointMake(0.0, 0.0); ASSERT_EQ(*layers[0], layer); @@ -2879,8 +3049,8 @@ TEST_F(EmbedderTest, VerifyB143464703) { layer.struct_size = sizeof(layer); layer.type = kFlutterLayerContentTypePlatformView; layer.platform_view = &platform_view; - layer.size = FlutterSizeMake(540.0, 1024.0); - layer.offset = FlutterPointMake(60.0, -135.0); + layer.size = FlutterSizeMake(1024.0, 540.0); + layer.offset = FlutterPointMake(135.0, 60.0); ASSERT_EQ(*layers[1], layer); } @@ -2888,15 +3058,14 @@ TEST_F(EmbedderTest, VerifyB143464703) { // Layer 2 { FlutterBackingStore backing_store = *layers[2]->backing_store; - backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.type = kFlutterBackingStoreTypeSoftware; backing_store.did_update = true; - backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; FlutterLayer layer = {}; layer.struct_size = sizeof(layer); layer.type = kFlutterLayerContentTypeBackingStore; layer.backing_store = &backing_store; - layer.size = FlutterSizeMake(600.0, 1024.0); + layer.size = FlutterSizeMake(1024.0, 600.0); layer.offset = FlutterPointMake(0.0, 0.0); ASSERT_EQ(*layers[2], layer); @@ -2907,7 +3076,8 @@ TEST_F(EmbedderTest, VerifyB143464703) { context.GetCompositor().SetPlatformViewRendererCallback( [](const FlutterLayer& layer, GrContext* context) -> sk_sp { - auto surface = CreateRenderSurface(layer, context); + auto surface = CreateRenderSurface( + layer, nullptr /* null because software compositor */); auto canvas = surface->getCanvas(); FML_CHECK(canvas != nullptr); @@ -2949,7 +3119,8 @@ TEST_F(EmbedderTest, VerifyB143464703) { }); latch.Wait(); - ASSERT_TRUE(ImageMatchesFixture("verifyb143464703.png", renderered_scene)); + ASSERT_TRUE(ImageMatchesFixture("verifyb143464703_soft_noxform.png", + renderered_scene)); } TEST_F(EmbedderTest, From 20e3c5bb83d20fdb70e50b2916e7e91611d9ee0f Mon Sep 17 00:00:00 2001 From: Filip Filmar Date: Fri, 8 Nov 2019 17:34:04 -0800 Subject: [PATCH 076/591] Revert "[fuchsia] Temporarily disable intl provider (#13696)" (#13721) This reverts commit 6c763bb551cbc06da59b6a55b4c5ee0eccb6575f. The reverted code was not the root cause of the issues with rolling flutter into fuchsia, so adding it back. In addition, lowering the severity of the connection error at the outset to WARNING; since it is not a hard failure. --- shell/platform/fuchsia/flutter/engine.cc | 55 +++++++++++++++++++ .../flutter/meta/flutter_aot_runner.cmx | 1 + .../meta/flutter_jit_product_runner.cmx | 1 + .../flutter/meta/flutter_jit_runner.cmx | 1 + .../flutter/meta/flutter_runner_tests.cmx | 1 + 5 files changed, 59 insertions(+) diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 6984a6e035659..665cd1b74f639 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -41,6 +41,13 @@ static void UpdateNativeThreadLabelNames(const std::string& label, set_thread_name(runners.GetIOTaskRunner(), label, ".io"); } +static fml::RefPtr MakeLocalizationPlatformMessage( + const fuchsia::intl::Profile& intl_profile) { + return fml::MakeRefCounted( + "flutter/localization", MakeLocalizationPlatformMessageData(intl_profile), + nullptr); +} + Engine::Engine(Delegate& delegate, std::string thread_label, std::shared_ptr svc, @@ -256,6 +263,54 @@ Engine::Engine(Delegate& delegate, // notification. Fire one eagerly. shell_->GetPlatformView()->NotifyCreated(); + // Connect to the intl property provider. If the connection fails, the + // initialization of the engine will simply proceed, printing a warning + // message. The engine will be fully functional, except that the user's + // locale preferences would not be communicated to flutter engine. + { + intl_property_provider_.set_error_handler([](zx_status_t status) { + FML_LOG(WARNING) << "Failed to connect to " + << fuchsia::intl::PropertyProvider::Name_ << ": " + << zx_status_get_string(status) + << " This is not a fatal error, but the user locale " + << " preferences will not be forwarded to flutter apps"; + }); + + // Note that we're using the runner's services, not the component's. + // Flutter locales should be updated regardless of whether the component has + // direct access to the fuchsia.intl.PropertyProvider service. + ZX_ASSERT(runner_services->Connect(intl_property_provider_.NewRequest()) == + ZX_OK); + + auto get_profile_callback = [flutter_runner_engine = + weak_factory_.GetWeakPtr()]( + const fuchsia::intl::Profile& profile) { + if (!flutter_runner_engine) { + return; + } + if (!profile.has_locales()) { + FML_LOG(WARNING) << "Got intl Profile without locales"; + } + auto message = MakeLocalizationPlatformMessage(profile); + FML_VLOG(-1) << "Sending LocalizationPlatformMessage"; + flutter_runner_engine->shell_->GetPlatformView()->DispatchPlatformMessage( + message); + }; + + FML_VLOG(-1) << "Requesting intl Profile"; + + // Make the initial request + intl_property_provider_->GetProfile(get_profile_callback); + + // And register for changes + intl_property_provider_.events().OnChange = [this, runner_services, + get_profile_callback]() { + FML_VLOG(-1) << fuchsia::intl::PropertyProvider::Name_ << ": OnChange"; + runner_services->Connect(intl_property_provider_.NewRequest()); + intl_property_provider_->GetProfile(get_profile_callback); + }; + } + // Launch the engine in the appropriate configuration. auto run_configuration = flutter::RunConfiguration::InferFromSettings( settings_, task_runners.GetIOTaskRunner()); diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index 358574ef5872a..bdfec3cd2e4d0 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -15,6 +15,7 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx index cd8c9e09ae1e7..912b534df93a2 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx @@ -15,6 +15,7 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx index cd8c9e09ae1e7..912b534df93a2 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -15,6 +15,7 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReporter", "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx index fedcb77867acb..015acc94b4e47 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx @@ -9,6 +9,7 @@ ], "services": [ "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.intl.PropertyProvider", "fuchsia.sys.Launcher" ] } From 55c64a92d8634a0053ce1b8d69e3da910f68435b Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Sat, 9 Nov 2019 01:55:59 +0000 Subject: [PATCH 077/591] Point old plugin registry accessors to new embedding plugin accessors. (#44225) (#13739) --- .../engine/plugins/FlutterPlugin.java | 12 +- .../activity/ActivityPluginBinding.java | 4 + .../flutter/plugin/common/PluginRegistry.java | 113 +++++++++++++++++- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java b/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java index a32daa06856eb..f3ff96e3a584a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java @@ -50,7 +50,17 @@ * be retrieved through a {@link Context}. Developers can access the application context via * {@link FlutterPluginBinding#getApplicationContext()}. *