From f620cb87fbb1848d751082d84079a1e6fe49bbd3 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 17 Nov 2020 13:37:39 -0800 Subject: [PATCH 1/8] BeginFrame to reset composition order --- .../ios/framework/Source/FlutterPlatformViews.mm | 10 ++-------- .../framework/Source/FlutterPlatformViews_Internal.h | 8 +------- .../platform/darwin/ios/ios_external_view_embedder.mm | 3 +-- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 3fc52d9dd5f5b..8a408ea5a7e8c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -241,7 +241,8 @@ gesture_recognizers_blocking_policies[idString] = gestureRecognizerBlockingPolicy; } -void FlutterPlatformViewsController::SetFrameSize(SkISize frame_size) { +void FlutterPlatformViewsController::BeginFrame(SkISize frame_size) { + composition_order_.clear(); frame_size_ = frame_size; } @@ -590,13 +591,6 @@ } } -void FlutterPlatformViewsController::EndFrame( - bool should_resubmit_frame, - fml::RefPtr raster_thread_merger) { - // Reset the composition order, so next frame starts empty. - composition_order_.clear(); -} - std::shared_ptr FlutterPlatformViewsController::GetLayer( GrDirectContext* gr_context, std::shared_ptr ios_context, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 36f69c7822e4c..1706b39e260e8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -141,7 +141,7 @@ class FlutterPlatformViewsController { NSString* factoryId, FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy); - void SetFrameSize(SkISize frame_size); + void BeginFrame(SkISize frame_size); // Indicates that we don't compisite any platform views or overlays during this frame. // Also reverts the composition_order_ to its original state at the begining of the frame. @@ -174,12 +174,6 @@ class FlutterPlatformViewsController { std::shared_ptr ios_context, std::unique_ptr frame); - // Invoked at the very end of a frame. - // After invoking this method, nothing should happen on the current TaskRunner during the same - // frame. - void EndFrame(bool should_resubmit_frame, - fml::RefPtr raster_thread_merger); - void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 46cb3014b636f..a4921af254af9 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -37,7 +37,7 @@ fml::RefPtr raster_thread_merger) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::BeginFrame"); FML_CHECK(platform_views_controller_); - platform_views_controller_->SetFrameSize(frame_size); + platform_views_controller_->BeginFrame(frame_size); } // |ExternalViewEmbedder| @@ -85,7 +85,6 @@ fml::RefPtr raster_thread_merger) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::EndFrame"); FML_CHECK(platform_views_controller_); - return platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger); } // |ExternalViewEmbedder| From 0c02873f83ae1db60183d565d0fdb5146d8c7ab5 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 17 Nov 2020 15:48:26 -0800 Subject: [PATCH 2/8] add test --- .../Scenarios.xcodeproj/project.pbxproj | 6 ++ .../ios/Scenarios/Scenarios/AppDelegate.m | 5 ++ .../Scenarios/Scenarios/ContinuousTexture.h | 20 +++++ .../Scenarios/Scenarios/ContinuousTexture.m | 82 +++++++++++++++++++ .../ScenariosUITests/PlatformViewUITests.m | 33 ++++++++ .../scenario_app/lib/src/platform_view.dart | 18 ++++ testing/scenario_app/lib/src/scenarios.dart | 1 + 7 files changed, 165 insertions(+) create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 71d8cb57f989d..b6a23373f7b17 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 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 */; }; 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; }; + 68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -172,6 +173,8 @@ 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 = ""; }; 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = ""; }; + 68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = ""; }; + 68D4017C2564859300ECD91A /* ContinuousTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContinuousTexture.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -238,6 +241,8 @@ 0A57B3BC2323C4BD00DD9521 /* ScreenBeforeFlutter.m */, 0A57B3BE2323C74200DD9521 /* FlutterEngine+ScenariosTest.m */, 0A57B3C02323C74D00DD9521 /* FlutterEngine+ScenariosTest.h */, + 68D4017B2564859300ECD91A /* ContinuousTexture.h */, + 68D4017C2564859300ECD91A /* ContinuousTexture.m */, ); path = Scenarios; sourceTree = ""; @@ -462,6 +467,7 @@ buildActionMask = 2147483647; files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, + 68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */, 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, 0A57B3BF2323C74200DD9521 /* FlutterEngine+ScenariosTest.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 96ddd0c734f6d..b9ae8456fa41e 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -7,6 +7,7 @@ #import "FlutterEngine+ScenariosTest.h" #import "ScreenBeforeFlutter.h" #import "TextPlatformView.h" +#import "ContinuousTexture.h" @interface NoStatusBarFlutterViewController : FlutterViewController @@ -52,6 +53,7 @@ - (BOOL)application:(UIApplication*)application @"--tap-status-bar" : @"tap_status_bar", @"--text-semantics-focus" : @"text_semantics_focus", @"--animated-color-square" : @"animated_color_square", + @"--platform-view-with-continuous-texture": @"platform_view_with_continuous_texture" }; __block NSString* flutterViewControllerTestName = nil; [launchArgsMap @@ -70,6 +72,9 @@ - (BOOL)application:(UIApplication*)application } [self.window makeKeyAndVisible]; + if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--with-continuous-texture"]) { + [ContinuousTexture registerWithRegistrar:[self registrarForPlugin:@"com.constant.firing.texture"]]; + } return [super application:application didFinishLaunchingWithOptions:launchOptions]; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h new file mode 100644 index 0000000000000..1ef5879990c73 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h @@ -0,0 +1,20 @@ +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +// A texture plugin that ready textures continuously. +@interface ContinuousTexture : NSObject + +@end + +// The testing texture used by |ContinuousTexture| +@interface FlutterScenarioTestTexture : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m new file mode 100644 index 0000000000000..120e1d3e4959e --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m @@ -0,0 +1,82 @@ +// 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 "ContinuousTexture.h" + +@implementation ContinuousTexture + ++ (void)registerWithRegistrar:(nonnull NSObject *)registrar { + NSObject *textureRegistry = [registrar textures]; + FlutterScenarioTestTexture *texture = [[FlutterScenarioTestTexture alloc] init]; + int64_t textureId = [textureRegistry registerTexture:texture]; + [NSTimer scheduledTimerWithTimeInterval:0.05 repeats:YES block:^(NSTimer * _Nonnull timer) { + [textureRegistry textureFrameAvailable:textureId]; + }]; +} + +@end + +@interface FlutterScenarioTestTexture() + +@property (strong, nonatomic) UIImage *image; + +@end + +@implementation FlutterScenarioTestTexture + +- (instancetype)init +{ + self = [super init]; + if (self) { + UIGraphicsBeginImageContext(CGSizeMake(100, 100)); + [[UIColor yellowColor] setFill]; + UIRectFill(CGRectMake(0, 0, 100, 100)); + self.image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + return self; +} + +- (CVPixelBufferRef _Nullable)copyPixelBuffer { + return [self pixelBuffer]; +} + +- (CVPixelBufferRef)pixelBuffer +{ + NSDictionary *options = @{ + (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES, + (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES, + (NSString*)kCVPixelBufferMetalCompatibilityKey: @YES + }; + size_t width = CGImageGetWidth(self.image.CGImage); + size_t height = CGImageGetHeight(self.image.CGImage); + CVPixelBufferRef pxbuffer = NULL; + CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, + height, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef) options, + &pxbuffer); + if (status!=kCVReturnSuccess) { + NSLog(@"Operation failed"); + } + NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); + + CVPixelBufferLockBaseAddress(pxbuffer, 0); + void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); + + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(pxdata, width, + height, 8, 4*width, rgbColorSpace, + kCGImageAlphaNoneSkipFirst); + NSParameterAssert(context); + + CGContextDrawImage(context, CGRectMake(0, 0, width, + height), self.image.CGImage); + CGColorSpaceRelease(rgbColorSpace); + CGContextRelease(context); + + CVPixelBufferUnlockBaseAddress(pxbuffer, 0); + return pxbuffer; +} + + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index 1d23e3a064378..24419a649910b 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -4,6 +4,8 @@ #import "GoldenPlatformViewTests.h" +static const NSInteger kSecondsToWaitForPlatformView = 30; + @interface PlatformViewUITests : GoldenPlatformViewTests @end @@ -170,4 +172,35 @@ - (void)testPlatformView { XCUIDevice.sharedDevice.orientation = UIDeviceOrientationLandscapeLeft; [self checkGolden]; } + +@end + +@interface PlatformViewWithContinuousTexture : XCTestCase + +@end + +@implementation PlatformViewWithContinuousTexture + +- (void)setUp { + self.continueAfterFailure = NO; +} + +- (void)testPlatformViewWithContinuousTexture { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = + @[ @"--platform-view-with-continuous-texture", @"--with-continuous-texture" ]; + [app launch]; + + XCUIElement* platformView = app.textViews.firstMatch; + BOOL exists = [platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]; + if (!exists) { + XCTFail(@"It took longer than %@ second to find the platform view." + @"There might be issues with the platform view's construction," + @"or with how the scenario is built.", + @(kSecondsToWaitForPlatformView)); + } + + XCTAssertNotNil(platformView); +} + @end diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index fd6f5f47887e9..eab2373a7d2bc 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -613,6 +613,24 @@ class PlatformViewForTouchIOSScenario extends Scenario } } +/// A simple platform view for testing platform view with a continuous texture layer. +class PlatformViewWithContinuousTexture extends PlatformViewScenario { + /// Constructs a platform view with continuous texture layer + PlatformViewWithContinuousTexture(PlatformDispatcher dispatcher, String text, { int id = 0 }) + : super(dispatcher, text, id: id); + + @override + void onBeginFrame(Duration duration) { + print('begin frame'); + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + builder.addTexture(0, width: 300, height: 300, offset: const Offset(200, 200)); + + finishBuilderByAddingPlatformViewAndPicture(builder, id); + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 7431752d4bbe1..e2c1f27206546 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -43,6 +43,7 @@ Map _scenarios = { 'tap_status_bar': () => TouchesScenario(PlatformDispatcher.instance), 'text_semantics_focus': () => SendTextFocusSemantics(PlatformDispatcher.instance), 'initial_route_reply': () => InitialRouteReply(PlatformDispatcher.instance), + 'platform_view_with_continuous_texture': () => PlatformViewWithContinuousTexture(PlatformDispatcher.instance, 'Platform View', id: _viewId++), }; Map _currentScenarioParams = {}; From 77df2286a795d1782e5593a75bf802a9a0965a31 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 17 Nov 2020 17:48:43 -0800 Subject: [PATCH 3/8] review --- .../ios/Scenarios/Scenarios/AppDelegate.m | 7 +- .../Scenarios/Scenarios/ContinuousTexture.h | 2 +- .../Scenarios/Scenarios/ContinuousTexture.m | 88 +++++++++---------- .../scenario_app/lib/src/platform_view.dart | 5 +- 4 files changed, 50 insertions(+), 52 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index b9ae8456fa41e..0cbdafaa5984f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -4,10 +4,10 @@ #import "AppDelegate.h" +#import "ContinuousTexture.h" #import "FlutterEngine+ScenariosTest.h" #import "ScreenBeforeFlutter.h" #import "TextPlatformView.h" -#import "ContinuousTexture.h" @interface NoStatusBarFlutterViewController : FlutterViewController @@ -53,7 +53,7 @@ - (BOOL)application:(UIApplication*)application @"--tap-status-bar" : @"tap_status_bar", @"--text-semantics-focus" : @"text_semantics_focus", @"--animated-color-square" : @"animated_color_square", - @"--platform-view-with-continuous-texture": @"platform_view_with_continuous_texture" + @"--platform-view-with-continuous-texture" : @"platform_view_with_continuous_texture" }; __block NSString* flutterViewControllerTestName = nil; [launchArgsMap @@ -73,7 +73,8 @@ - (BOOL)application:(UIApplication*)application [self.window makeKeyAndVisible]; if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--with-continuous-texture"]) { - [ContinuousTexture registerWithRegistrar:[self registrarForPlugin:@"com.constant.firing.texture"]]; + [ContinuousTexture + registerWithRegistrar:[self registrarForPlugin:@"com.constant.firing.texture"]]; } return [super application:application didFinishLaunchingWithOptions:launchOptions]; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h index 1ef5879990c73..996de850caa09 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.h @@ -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 #import +#import NS_ASSUME_NONNULL_BEGIN diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m index 120e1d3e4959e..36edf2e20905f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m @@ -6,27 +6,28 @@ @implementation ContinuousTexture -+ (void)registerWithRegistrar:(nonnull NSObject *)registrar { - NSObject *textureRegistry = [registrar textures]; - FlutterScenarioTestTexture *texture = [[FlutterScenarioTestTexture alloc] init]; ++ (void)registerWithRegistrar:(nonnull NSObject*)registrar { + NSObject* textureRegistry = [registrar textures]; + FlutterScenarioTestTexture* texture = [[FlutterScenarioTestTexture alloc] init]; int64_t textureId = [textureRegistry registerTexture:texture]; - [NSTimer scheduledTimerWithTimeInterval:0.05 repeats:YES block:^(NSTimer * _Nonnull timer) { - [textureRegistry textureFrameAvailable:textureId]; - }]; + [NSTimer scheduledTimerWithTimeInterval:0.05 + repeats:YES + block:^(NSTimer* _Nonnull timer) { + [textureRegistry textureFrameAvailable:textureId]; + }]; } @end -@interface FlutterScenarioTestTexture() +@interface FlutterScenarioTestTexture () -@property (strong, nonatomic) UIImage *image; +@property(strong, nonatomic) UIImage* image; @end @implementation FlutterScenarioTestTexture -- (instancetype)init -{ +- (instancetype)init { self = [super init]; if (self) { UIGraphicsBeginImageContext(CGSizeMake(100, 100)); @@ -42,41 +43,38 @@ - (CVPixelBufferRef _Nullable)copyPixelBuffer { return [self pixelBuffer]; } -- (CVPixelBufferRef)pixelBuffer -{ - NSDictionary *options = @{ - (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES, - (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES, - (NSString*)kCVPixelBufferMetalCompatibilityKey: @YES - }; - size_t width = CGImageGetWidth(self.image.CGImage); - size_t height = CGImageGetHeight(self.image.CGImage); - CVPixelBufferRef pxbuffer = NULL; - CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, - height, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef) options, - &pxbuffer); - if (status!=kCVReturnSuccess) { - NSLog(@"Operation failed"); - } - NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); - - CVPixelBufferLockBaseAddress(pxbuffer, 0); - void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); - - CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = CGBitmapContextCreate(pxdata, width, - height, 8, 4*width, rgbColorSpace, - kCGImageAlphaNoneSkipFirst); - NSParameterAssert(context); - - CGContextDrawImage(context, CGRectMake(0, 0, width, - height), self.image.CGImage); - CGColorSpaceRelease(rgbColorSpace); - CGContextRelease(context); - - CVPixelBufferUnlockBaseAddress(pxbuffer, 0); - return pxbuffer; -} +- (CVPixelBufferRef)pixelBuffer { + NSDictionary* options = @{ + // This key is required to convert CGImage to CVPixelBufferRef. + (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES, + // This key is required to generate SKPicture with CVPixelBufferRef in metal. + (NSString*)kCVPixelBufferMetalCompatibilityKey : @YES + }; + size_t width = CGImageGetWidth(self.image.CGImage); + size_t height = CGImageGetHeight(self.image.CGImage); + CVPixelBufferRef pxbuffer = NULL; + CVReturn status = + CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, + (__bridge CFDictionaryRef)options, &pxbuffer); + if (status != kCVReturnSuccess) { + NSLog(@"Operation failed"); + } + NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); + + CVPixelBufferLockBaseAddress(pxbuffer, 0); + void* pxdata = CVPixelBufferGetBaseAddress(pxbuffer); + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(pxdata, width, height, 8, 4 * width, rgbColorSpace, + kCGImageAlphaNoneSkipFirst); + NSParameterAssert(context); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.image.CGImage); + CGColorSpaceRelease(rgbColorSpace); + CGContextRelease(context); + + CVPixelBufferUnlockBaseAddress(pxbuffer, 0); + return pxbuffer; +} @end diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index eab2373a7d2bc..31f45fd6837e2 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -614,17 +614,16 @@ class PlatformViewForTouchIOSScenario extends Scenario } /// A simple platform view for testing platform view with a continuous texture layer. +/// For example, it simulates a video being played. class PlatformViewWithContinuousTexture extends PlatformViewScenario { - /// Constructs a platform view with continuous texture layer + /// Constructs a platform view with continuous texture layer. PlatformViewWithContinuousTexture(PlatformDispatcher dispatcher, String text, { int id = 0 }) : super(dispatcher, text, id: id); @override void onBeginFrame(Duration duration) { - print('begin frame'); final SceneBuilder builder = SceneBuilder(); - builder.pushOffset(0, 0); builder.addTexture(0, width: 300, height: 300, offset: const Offset(200, 200)); finishBuilderByAddingPlatformViewAndPicture(builder, id); From d83d9866b838a9888112643c0b83e45deae8e4b0 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 18 Nov 2020 10:57:24 -0800 Subject: [PATCH 4/8] unittest --- .../framework/Source/FlutterPlatformViews.mm | 10 +++- .../Source/FlutterPlatformViewsTest.mm | 55 +++++++++++++++++++ .../Source/FlutterPlatformViews_Internal.h | 4 ++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 121829631c238..90786cbb46e7b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -242,13 +242,12 @@ } void FlutterPlatformViewsController::BeginFrame(SkISize frame_size) { - composition_order_.clear(); + ResetFrameState(); frame_size_ = frame_size; } void FlutterPlatformViewsController::CancelFrame() { - picture_recorders_.clear(); - composition_order_.clear(); + ResetFrameState(); } // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 @@ -699,6 +698,11 @@ } } +void FlutterPlatformViewsController::ResetFrameState() { + picture_recorders_.clear(); + composition_order_.clear(); +} + } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 31a19b29b6e9c..3bb8aa297e758 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -869,6 +869,61 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin XCTAssertNil(gMockPlatformView); } +- (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { + flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; + auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/thread_task_runner, + /*raster=*/thread_task_runner, + /*ui=*/thread_task_runner, + /*io=*/thread_task_runner); + auto flutterPlatformViewsController = std::make_shared(); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners); + + UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)] autorelease]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], + result); + + // First frame, |GetCurrentCanvases| is not empty after composite. + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + auto embeddedViewParams1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), (unsigned long)1); + + // Second frame, |GetCurrentCanvases| should be empty at the start + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), (unsigned long)0); + + auto embeddedViewParams2 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), (unsigned long)1); + + flutterPlatformViewsController->Reset(); +} + - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { unsigned char pixel[4] = {0}; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 66cefd4277b2d..584a201693788 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -141,6 +141,7 @@ class FlutterPlatformViewsController { NSString* factoryId, FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy); + // Called at the begining of each frame. void BeginFrame(SkISize frame_size); // Indicates that we don't compisite any platform views or overlays during this frame. @@ -303,6 +304,9 @@ class FlutterPlatformViewsController { std::shared_ptr ios_context, std::unique_ptr frame); + // Resets the state of the frame. + void ResetFrameState(); + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; From 782e34e045d77a9c3b8ec92c0ae2624e75420407 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 18 Nov 2020 12:23:29 -0800 Subject: [PATCH 5/8] cleanup test --- .../Source/FlutterPlatformViewsTest.mm | 6 +-- .../Scenarios/Scenarios/ContinuousTexture.m | 40 ++----------------- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 3bb8aa297e758..248b268ba5d35 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -909,17 +909,17 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); - XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), (unsigned long)1); + XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), 1UL); // Second frame, |GetCurrentCanvases| should be empty at the start flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), (unsigned long)0); + XCTAssertTrue(flutterPlatformViewsController->GetCurrentCanvases().empty()); auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(0); - XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), (unsigned long)1); + XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), 1UL); flutterPlatformViewsController->Reset(); } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m index 36edf2e20905f..abac1860afffc 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m @@ -19,61 +19,27 @@ + (void)registerWithRegistrar:(nonnull NSObject*)registr @end -@interface FlutterScenarioTestTexture () - -@property(strong, nonatomic) UIImage* image; - -@end - @implementation FlutterScenarioTestTexture -- (instancetype)init { - self = [super init]; - if (self) { - UIGraphicsBeginImageContext(CGSizeMake(100, 100)); - [[UIColor yellowColor] setFill]; - UIRectFill(CGRectMake(0, 0, 100, 100)); - self.image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } - return self; -} - - (CVPixelBufferRef _Nullable)copyPixelBuffer { return [self pixelBuffer]; } - (CVPixelBufferRef)pixelBuffer { NSDictionary* options = @{ - // This key is required to convert CGImage to CVPixelBufferRef. - (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES, // This key is required to generate SKPicture with CVPixelBufferRef in metal. (NSString*)kCVPixelBufferMetalCompatibilityKey : @YES }; - size_t width = CGImageGetWidth(self.image.CGImage); - size_t height = CGImageGetHeight(self.image.CGImage); + size_t width = 200; + size_t height = 200; CVPixelBufferRef pxbuffer = NULL; CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef)options, &pxbuffer); - if (status != kCVReturnSuccess) { - NSLog(@"Operation failed"); - } + NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); CVPixelBufferLockBaseAddress(pxbuffer, 0); - void* pxdata = CVPixelBufferGetBaseAddress(pxbuffer); - - CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = CGBitmapContextCreate(pxdata, width, height, 8, 4 * width, rgbColorSpace, - kCGImageAlphaNoneSkipFirst); - NSParameterAssert(context); - - CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.image.CGImage); - CGColorSpaceRelease(rgbColorSpace); - CGContextRelease(context); - - CVPixelBufferUnlockBaseAddress(pxbuffer, 0); return pxbuffer; } From 2d22e2dc4f1d1a7c6688bbcba786d42d86bcf234 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 18 Nov 2020 12:23:56 -0800 Subject: [PATCH 6/8] more cleanup --- .../scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m index abac1860afffc..2c205a5b21f7c 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m @@ -30,11 +30,9 @@ - (CVPixelBufferRef)pixelBuffer { // This key is required to generate SKPicture with CVPixelBufferRef in metal. (NSString*)kCVPixelBufferMetalCompatibilityKey : @YES }; - size_t width = 200; - size_t height = 200; CVPixelBufferRef pxbuffer = NULL; CVReturn status = - CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, + CVPixelBufferCreate(kCFAllocatorDefault, 200, 200, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef)options, &pxbuffer); NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); From 319e87c45ff2fe1d73d0a1ee453471c03e975927 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 18 Nov 2020 12:27:40 -0800 Subject: [PATCH 7/8] more test cleanups --- .../framework/Source/FlutterPlatformViewsTest.mm | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 248b268ba5d35..55a143e6d0aa7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -136,8 +136,6 @@ - (void)testCanCreatePlatformViewWithoutFlutterView { result); XCTAssertNotNil(gMockPlatformView); - - flutterPlatformViewsController->Reset(); } - (void)testChildClippingViewHitTests { @@ -213,7 +211,6 @@ - (void)testCompositePlatformView { CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds toView:mockFlutterView]; XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); - flutterPlatformViewsController->Reset(); } - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { @@ -283,8 +280,6 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { XCTAssertLessThan( fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height), kFloatCompareEpsilon); - - flutterPlatformViewsController->Reset(); } - (void)testClipRect { @@ -356,7 +351,6 @@ - (void)testClipRect { } } } - flutterPlatformViewsController->Reset(); } - (void)testClipRRect { @@ -428,7 +422,6 @@ - (void)testClipRRect { } } } - flutterPlatformViewsController->Reset(); } - (void)testClipPath { @@ -501,7 +494,6 @@ - (void)testClipPath { } } } - flutterPlatformViewsController->Reset(); } - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { @@ -564,8 +556,6 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); - - flutterPlatformViewsController->Reset(); } - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { @@ -816,8 +806,6 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin gpu_is_disabled->SetSwitch(false); XCTAssertTrue(flutterPlatformViewsController->SubmitFrame( nullptr, nullptr, std::move(mock_surface_submit_false), gpu_is_disabled)); - - flutterPlatformViewsController->Reset(); } - (void) @@ -920,8 +908,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(0); XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), 1UL); - - flutterPlatformViewsController->Reset(); } - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { From d01fc480bf85413dcb8cc5baf988cbd09daa3a78 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 18 Nov 2020 12:43:17 -0800 Subject: [PATCH 8/8] format --- .../scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m index 2c205a5b21f7c..0211062a283b5 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/ContinuousTexture.m @@ -31,9 +31,8 @@ - (CVPixelBufferRef)pixelBuffer { (NSString*)kCVPixelBufferMetalCompatibilityKey : @YES }; CVPixelBufferRef pxbuffer = NULL; - CVReturn status = - CVPixelBufferCreate(kCFAllocatorDefault, 200, 200, kCVPixelFormatType_32BGRA, - (__bridge CFDictionaryRef)options, &pxbuffer); + CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, 200, 200, kCVPixelFormatType_32BGRA, + (__bridge CFDictionaryRef)options, &pxbuffer); NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);