diff --git a/flow/surface_frame.h b/flow/surface_frame.h index 44b0c9ca703bb..4e66be913cfa4 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -93,6 +93,19 @@ class SurfaceFrame { // // Defaults to true, which is generally a safe value. bool frame_boundary = true; + + // Whether this surface presents with a CATransaction on Apple platforms. + // + // When there are platform views in the scene, the drawable needs to be + // presented in the same CATransaction as the one created for platform view + // mutations. + // + // If the drawables are being presented from the raster thread, we cannot + // use a transaction as it will dirty the UIViews being presented. If there + // is a non-Flutter UIView active, such as in add2app or a + // presentViewController page transition, then this will cause CoreAnimation + // assertion errors and exit the app. + bool present_with_transaction = false; }; bool Encode(); diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index dd028266a4aa7..c5dd5a61eeff6 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -58,6 +58,13 @@ class SurfaceMTL final : public Surface { // Returns a Rect defining the area of the surface in device pixels IRect coverage() const; + /// Mark this surface as presenting with a transaction. + /// + /// If true, [Present] will block on the scheduling of a command buffer. + void PresentWithTransaction(bool present_with_transaction) { + present_with_transaction_ = present_with_transaction; + } + // |Surface| bool Present() const override; @@ -74,6 +81,7 @@ class SurfaceMTL final : public Surface { bool requires_blit_ = false; std::optional clip_rect_; bool frame_boundary_ = false; + bool present_with_transaction_ = false; static bool ShouldPerformPartialRepaint(std::optional damage_rect); diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index e8cd572c5f5b1..c94ad6cd4c0da 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -286,7 +286,7 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; // If the threads have been merged, or there is a pending frame capture, // then block on cmd buffer scheduling to ensure that the // transaction/capture work correctly. - if ([[NSThread currentThread] isMainThread] || + if (present_with_transaction_ || [[NSThread currentThread] isMainThread] || [[MTLCaptureManager sharedCaptureManager] isCapturing] || alwaysWaitForScheduling) { TRACE_EVENT0("flutter", "waitUntilScheduled"); diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index f20b0507231c9..54a86c2855623 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -77,6 +77,8 @@ class GPUSurfaceMetalDelegate { /// virtual bool PresentDrawable(GrMTLHandle drawable) const = 0; + virtual bool PreparePresent(GrMTLHandle drawable) const { return true; } + //------------------------------------------------------------------------------ /// @brief Returns the handle to the MTLTexture to render to. This is only /// called when the specified render target type is `kMTLTexture`. diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 86f0517e429de..5041fc4092c11 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -109,8 +109,11 @@ disable_partial_repaint = disable_partial_repaint_, // aiks_context = aiks_context_, // drawable, // - last_texture // + last_texture, // + mtl_layer // ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool { + mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; + if (!aiks_context) { return false; } @@ -151,6 +154,7 @@ if (!surface) { return false; } + surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction); if (clip_rect && clip_rect->IsEmpty()) { surface_frame.set_user_data(std::move(surface)); @@ -271,6 +275,8 @@ auto surface = impeller::SurfaceMTL::MakeFromTexture(aiks_context->GetContext(), mtl_texture, clip_rect); + surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction); + if (clip_rect && clip_rect->IsEmpty()) { return surface->Present(); } diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index c4f4e4d377f40..9b7e255345a60 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -157,8 +157,9 @@ return nullptr; } - auto encode_callback = [this, drawable](const SurfaceFrame& surface_frame, - DlCanvas* canvas) -> bool { + SurfaceFrame::EncodeCallback encode_callback = + [this, drawable, mtl_layer](const SurfaceFrame& surface_frame, DlCanvas* canvas) -> bool { + mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; if (canvas == nullptr) { FML_DLOG(ERROR) << "Canvas not available."; return false; @@ -186,7 +187,8 @@ return true; }; - auto submit_callback = [this, drawable](const SurfaceFrame& surface_frame) -> bool { + SurfaceFrame::SubmitCallback submit_callback = + [this, drawable](const SurfaceFrame& surface_frame) -> bool { TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit"); return delegate_->PresentDrawable(drawable); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 5f2a1fa74ff82..669eb9a35a39f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -219,7 +219,6 @@ - (instancetype)initWithName:(NSString*)labelPrefix _pluginPublications = [[NSMutableDictionary alloc] init]; _registrars = [[NSMutableDictionary alloc] init]; [self recreatePlatformViewController]; - _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self]; _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self]; _connections.reset(new flutter::ConnectionCollection()); @@ -869,6 +868,8 @@ - (BOOL)createShell:(NSString*)entrypoint flutter::Shell::CreateCallback on_create_platform_view = [self](flutter::Shell& shell) { [self recreatePlatformViewController]; + self->_platformViewsController->SetTaskRunner( + shell.GetTaskRunners().GetPlatformTaskRunner()); return std::make_unique( shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch()); @@ -1468,6 +1469,8 @@ - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint flutter::Shell::CreateCallback on_create_platform_view = [result, context](flutter::Shell& shell) { [result recreatePlatformViewController]; + result->_platformViewsController->SetTaskRunner( + shell.GetTaskRunners().GetPlatformTaskRunner()); return std::make_unique( shell, context, result->_platformViewsController, shell.GetTaskRunners()); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm index 2e98396d603c0..76d81013dc90f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm @@ -12,7 +12,6 @@ FLUTTER_ASSERT_ARC // This is mostly a duplication of FlutterView. -// TODO(amirh): once GL support is in evaluate if we can merge this with FlutterView. @implementation FlutterOverlayView { fml::CFRef _colorSpaceRef; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index e08e7fbb995b4..5509c2245fdf6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -5,15 +5,28 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #include +#include +#include "flow/surface_frame.h" #include "flutter/flow/view_slicer.h" +#include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" +#include "fml/logging.h" +#include "fml/synchronization/count_down_latch.h" FLUTTER_ASSERT_ARC +#ifdef FML_OS_IOS_SIMULATOR +// The number of frames the rasterizer task runner will continue +// to run on the platform thread after no platform view is rendered. +// +// Note: this is an arbitrary number. +static const int kDefaultMergedLeaseDuration = 10; +#endif // FML_OS_IOS_SIMULATOR + @implementation UIView (FirstResponder) - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { if (self.isFirstResponder) { @@ -80,72 +93,70 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied. BOOL canApplyBlurBackdrop = YES; -std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { - if (available_layer_index_ >= layers_.size()) { - std::shared_ptr layer; - fml::scoped_nsobject overlay_view; - fml::scoped_nsobject overlay_view_wrapper; - - bool impeller_enabled = !!ios_context->GetImpellerContext(); - if (!gr_context && !impeller_enabled) { - overlay_view.reset([[FlutterOverlayView alloc] init]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); - - auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; - std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - - layer = std::make_shared( - std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), - std::move(surface)); - } else { - CGFloat screenScale = [UIScreen mainScreen].scale; - overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale - pixelFormat:pixel_format]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale - pixelFormat:pixel_format]); - - auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; - std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - - layer = std::make_shared( - std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), - std::move(surface)); - layer->gr_context = gr_context; - } - // The overlay view wrapper masks the overlay view. - // This is required to keep the backing surface size unchanged between frames. - // - // Otherwise, changing the size of the overlay would require a new surface, - // which can be very expensive. - // - // This is the case of an animation in which the overlay size is changing in every frame. - // - // +------------------------+ - // | overlay_view | - // | +--------------+ | +--------------+ - // | | wrapper | | == mask => | overlay_view | - // | +--------------+ | +--------------+ - // +------------------------+ - layer->overlay_view_wrapper.get().clipsToBounds = YES; - [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; - layers_.push_back(layer); +std::shared_ptr FlutterPlatformViewLayerPool::GetNextLayer() { + std::shared_ptr result; + if (available_layer_index_ < layers_.size()) { + result = layers_[available_layer_index_]; + available_layer_index_++; } - std::shared_ptr layer = layers_[available_layer_index_]; - if (gr_context != layer->gr_context) { - layer->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = layer->ios_surface.get(); + + return result; +} + +void FlutterPlatformViewLayerPool::CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { + FML_DCHECK([[NSThread currentThread] isMainThread]); + std::shared_ptr layer; + fml::scoped_nsobject overlay_view; + fml::scoped_nsobject overlay_view_wrapper; + + bool impeller_enabled = !!ios_context->GetImpellerContext(); + if (!gr_context && !impeller_enabled) { + overlay_view.reset([[FlutterOverlayView alloc] init]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); + + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; + std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + + layer = std::make_shared(std::move(overlay_view), + std::move(overlay_view_wrapper), + std::move(ios_surface), std::move(surface)); + } else { + CGFloat screenScale = [UIScreen mainScreen].scale; + overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; + std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); + + layer = std::make_shared(std::move(overlay_view), + std::move(overlay_view_wrapper), + std::move(ios_surface), std::move(surface)); + layer->gr_context = gr_context; } - available_layer_index_++; - return layer; + // The overlay view wrapper masks the overlay view. + // This is required to keep the backing surface size unchanged between frames. + // + // Otherwise, changing the size of the overlay would require a new surface, + // which can be very expensive. + // + // This is the case of an animation in which the overlay size is changing in every frame. + // + // +------------------------+ + // | overlay_view | + // | +--------------+ | +--------------+ + // | | wrapper | | == mask => | overlay_view | + // | +--------------+ | +--------------+ + // +------------------------+ + layer->overlay_view_wrapper.get().clipsToBounds = YES; + [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; + + layers_.push_back(layer); } void FlutterPlatformViewLayerPool::RecycleLayers() { @@ -208,10 +219,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSString* viewTypeString = args[@"viewType"]; std::string viewType(viewTypeString.UTF8String); - if (views_.count(viewId) != 0) { + if (platform_views_.count(viewId) != 0) { result([FlutterError errorWithCode:@"recreating_view" message:@"trying to create an already created view" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; } NSObject* factory = factories_[viewType].get(); @@ -247,19 +259,22 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Set a unique view identifier, so the platform view can be identified in unit tests. platform_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld]", viewId]; - views_[viewId] = fml::scoped_nsobject>(embedded_view); FlutterTouchInterceptingView* touch_interceptor = [[FlutterTouchInterceptingView alloc] initWithEmbeddedView:platform_view platformViewsController:GetWeakPtr() gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]]; - touch_interceptors_[viewId] = - fml::scoped_nsobject(touch_interceptor); - ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero]; [clipping_view addSubview:touch_interceptor]; - root_views_[viewId] = fml::scoped_nsobject(clipping_view); + + platform_views_.emplace( + viewId, PlatformViewData{ + .view = fml::scoped_nsobject>(embedded_view), // + .touch_interceptor = + fml::scoped_nsobject(touch_interceptor), // + .root_view = fml::scoped_nsobject(clipping_view) // + }); result(nil); } @@ -268,7 +283,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSNumber* arg = [call arguments]; int64_t viewId = [arg longLongValue]; - if (views_.count(viewId) == 0) { + if (platform_views_.count(viewId) == 0) { result([FlutterError errorWithCode:@"unknown_view" message:@"trying to dispose an unknown" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); @@ -284,14 +299,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; - if (views_.count(viewId) == 0) { + if (platform_views_.count(viewId) == 0) { result([FlutterError errorWithCode:@"unknown_view" message:@"trying to set gesture state for an unknown view" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); return; } - FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + FlutterTouchInterceptingView* view = platform_views_[viewId].touch_interceptor.get(); [view releaseGesture]; result(nil); @@ -302,14 +317,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; - if (views_.count(viewId) == 0) { + if (platform_views_.count(viewId) == 0) { result([FlutterError errorWithCode:@"unknown_view" message:@"trying to set gesture state for an unknown view" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); return; } - FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + FlutterTouchInterceptingView* view = platform_views_[viewId].touch_interceptor.get(); [view blockGesture]; result(nil); @@ -334,20 +349,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ResetFrameState(); } -// TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 -// Make this method check if there are pending view operations instead. -// Also rename it to `HasPendingViewOperations`. -bool FlutterPlatformViewsController::HasPlatformViewThisOrNextFrame() { - return !composition_order_.empty() || !active_composition_order_.empty(); -} - -const int FlutterPlatformViewsController::kDefaultMergedLeaseDuration; - PostPrerollResult FlutterPlatformViewsController::PostPrerollAction( const fml::RefPtr& raster_thread_merger) { - // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 - // Rename `has_platform_view` to `view_mutated` when the above issue is resolved. - if (!HasPlatformViewThisOrNextFrame()) { + // TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim. +#ifdef FML_OS_IOS_SIMULATOR + if (composition_order_.empty()) { return PostPrerollResult::kSuccess; } if (!raster_thread_merger->IsMerged()) { @@ -366,17 +372,21 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // In order to sync the rendering of the platform views (quartz) with skia's rendering, // We need to begin an explicit CATransaction. This transaction needs to be submitted // after the current frame is submitted. - BeginCATransaction(); raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); return PostPrerollResult::kSuccess; +#else + return PostPrerollResult::kSuccess; +#endif // FML_OS_IOS_SIMULATOR } void FlutterPlatformViewsController::EndFrame( bool should_resubmit_frame, const fml::RefPtr& raster_thread_merger) { +#if FML_OS_IOS_SIMULATOR if (should_resubmit_frame) { raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); } +#endif // FML_OS_IOS_SIMULATOR } void FlutterPlatformViewsController::PushFilterToVisitedPlatformViews( @@ -392,10 +402,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::PrerollCompositeEmbeddedView( int64_t view_id, std::unique_ptr params) { - // All the CATransactions should be committed by the end of the last frame, - // so catransaction_added_ must be false. - FML_DCHECK(!catransaction_added_); - SkRect view_bounds = SkRect::Make(frame_size_); std::unique_ptr view; view = std::make_unique(view_bounds); @@ -426,34 +432,24 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, FlutterTouchInterceptingView* FlutterPlatformViewsController::GetFlutterTouchInterceptingViewByID( int64_t view_id) { - if (views_.empty()) { + if (platform_views_.empty()) { return nil; } - return touch_interceptors_[view_id].get(); + return platform_views_[view_id].touch_interceptor.get(); } long FlutterPlatformViewsController::FindFirstResponderPlatformViewId() { - for (auto const& [id, root_view] : root_views_) { - if (((UIView*)root_view.get()).flt_hasFirstResponderInViewHierarchySubtree) { + for (auto const& [id, platform_view_data] : platform_views_) { + UIView* root_view = (UIView*)platform_view_data.root_view.get(); + if (root_view.flt_hasFirstResponderInViewHierarchySubtree) { return id; } } return -1; } -int FlutterPlatformViewsController::CountClips(const MutatorsStack& mutators_stack) { - std::vector>::const_reverse_iterator iter = mutators_stack.Bottom(); - int clipCount = 0; - while (iter != mutators_stack.Top()) { - if ((*iter)->IsClipType()) { - clipCount++; - } - ++iter; - } - return clipCount; -} - void FlutterPlatformViewsController::ClipViewSetMaskView(UIView* clipView) { + FML_DCHECK([[NSThread currentThread] isMainThread]); if (clipView.maskView) { return; } @@ -472,7 +468,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, if (flutter_view_ == nullptr) { return; } - FML_DCHECK(CATransform3DEqualToTransform(embedded_view.layer.transform, CATransform3DIdentity)); + ResetAnchor(embedded_view.layer); ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview; @@ -597,7 +593,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params) { CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); - FlutterTouchInterceptingView* touchInterceptor = touch_interceptors_[view_id].get(); + FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor.get(); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG FML_DCHECK(CGPointEqualToPoint([touchInterceptor embeddedView].frame.origin, CGPointZero)); if (non_zero_origin_views_.find(view_id) == non_zero_origin_views_.end() && @@ -621,7 +617,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, touchInterceptor.alpha = 1; const MutatorsStack& mutatorStack = params.mutatorsStack(); - UIView* clippingView = root_views_[view_id].get(); + UIView* clippingView = platform_views_[view_id].root_view.get(); // The frame of the clipping view should be the final bounding rect. // Because the translate matrix in the Mutator Stack also includes the offset, // when we apply the transforms matrix in |ApplyMutators|, we need @@ -634,125 +630,231 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } DlCanvas* FlutterPlatformViewsController::CompositeEmbeddedView(int64_t view_id) { - // Any UIKit related code has to run on main thread. - FML_DCHECK([[NSThread currentThread] isMainThread]); - // Do nothing if the view doesn't need to be composited. - if (views_to_recomposite_.count(view_id) == 0) { - return slices_[view_id]->canvas(); - } - CompositeWithParams(view_id, current_composition_params_[view_id]); - views_to_recomposite_.erase(view_id); return slices_[view_id]->canvas(); } void FlutterPlatformViewsController::Reset() { - for (int64_t view_id : active_composition_order_) { - UIView* sub_view = root_views_[view_id].get(); - [sub_view removeFromSuperview]; - } - root_views_.clear(); - touch_interceptors_.clear(); - views_.clear(); + // Reset will only be called from the raster thread or a merged raster/platform thread. + // platform_views_ must only be modified on the platform thread, and any operations that + // read or modify platform views should occur there. + fml::TaskRunner::RunNowOrPostTask( + platform_task_runner_, [&, composition_order = composition_order_]() { + for (int64_t view_id : composition_order_) { + [platform_views_[view_id].root_view.get() removeFromSuperview]; + } + platform_views_.clear(); + }); + composition_order_.clear(); - active_composition_order_.clear(); slices_.clear(); current_composition_params_.clear(); - clip_count_.clear(); views_to_recomposite_.clear(); layer_pool_->RecycleLayers(); visited_platform_views_.clear(); } -SkRect FlutterPlatformViewsController::GetPlatformViewRect(int64_t view_id) { - UIView* platform_view = GetPlatformViewByID(view_id); - UIScreen* screen = [UIScreen mainScreen]; - CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds - toView:flutter_view_]; - return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // - platform_view_cgrect.origin.y * screen.scale, // - platform_view_cgrect.size.width * screen.scale, // - platform_view_cgrect.size.height * screen.scale // - ); -} - bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - std::unique_ptr frame) { + std::unique_ptr background_frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); - // Any UIKit related code has to run on main thread. - FML_DCHECK([[NSThread currentThread] isMainThread]); - if (flutter_view_ == nullptr) { - return frame->Submit(); + // No platform views to render; we're done. + if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) { + had_platform_views_ = false; + return background_frame->Submit(); } + had_platform_views_ = true; - DisposeViews(); - - DlCanvas* background_canvas = frame->Canvas(); - + bool did_encode = true; + LayersMap platform_view_layers; + std::vector> surface_frames; + surface_frames.reserve(composition_order_.size()); std::unordered_map view_rects; - for (auto view_id : composition_order_) { - view_rects[view_id] = GetPlatformViewRect(view_id); + + for (int64_t view_id : composition_order_) { + view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect(); } std::unordered_map overlay_layers = - SliceViews(background_canvas, composition_order_, slices_, view_rects); + SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects); - LayersMap platform_view_layers; - auto did_submit = true; + size_t required_overlay_layers = 0; + for (int64_t view_id : composition_order_) { + std::unordered_map::const_iterator overlay = overlay_layers.find(view_id); + if (overlay == overlay_layers.end()) { + continue; + } + required_overlay_layers++; + } - int overlay_id = 0; + // If there are not sufficient overlay layers, we must construct them on the platform + // thread, at least until we've refactored iOS surface creation to use IOSurfaces + // instead of CALayers. + CreateMissingOverlays(gr_context, ios_context, required_overlay_layers); + + int64_t overlay_id = 0; for (int64_t view_id : composition_order_) { std::unordered_map::const_iterator overlay = overlay_layers.find(view_id); if (overlay == overlay_layers.end()) { continue; } - std::shared_ptr layer = - GetLayer(gr_context, // - ios_context, // - slices_[view_id].get(), // - overlay->second, // - view_id, // - overlay_id, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - did_submit &= layer->did_submit_last_frame; - platform_view_layers[view_id].push_back(layer); + std::shared_ptr layer = GetExistingLayer(); + if (!layer) { + continue; + } + + std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (!frame) { + continue; + } + DlCanvas* overlay_canvas = frame->Canvas(); + int restore_count = overlay_canvas->GetSaveCount(); + overlay_canvas->Save(); + overlay_canvas->ClipRect(overlay->second); + overlay_canvas->Clear(DlColor::kTransparent()); + slices_[view_id]->render_into(overlay_canvas); + overlay_canvas->RestoreToCount(restore_count); + + // This flutter view is never the last in a frame, since we always submit the + // underlay view last. + frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true}); + layer->did_submit_last_frame = frame->Encode(); + + did_encode &= layer->did_submit_last_frame; + platform_view_layers[view_id] = LayerData{ + .rect = overlay->second, // + .view_id = view_id, // + .overlay_id = overlay_id, // + .layer = layer // + }; + surface_frames.push_back(std::move(frame)); overlay_id++; } - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(); - // Organize the layers by their z indexes. - BringLayersIntoView(platform_view_layers); + background_frame->set_submit_info({.present_with_transaction = true}); + background_frame->Encode(); + surface_frames.push_back(std::move(background_frame)); + // Mark all layers as available, so they can be used in the next frame. + std::vector> unused_layers = + layer_pool_->RemoveUnusedLayers(); layer_pool_->RecycleLayers(); - did_submit &= frame->Submit(); + auto task = [&, // + platform_view_layers = std::move(platform_view_layers), // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + composition_order = composition_order_, // + unused_layers = std::move(unused_layers), // + surface_frames = std::move(surface_frames) // + ]() mutable { + PerformSubmit(platform_view_layers, // + current_composition_params, // + views_to_recomposite, // + composition_order, // + unused_layers, // + surface_frames // + ); + }; + + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task))); + + return did_encode; +} + +void FlutterPlatformViewsController::CreateMissingOverlays( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + size_t required_overlay_layers) { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); + + if (required_overlay_layers <= layer_pool_->size()) { + return; + } + auto missing_layer_count = required_overlay_layers - layer_pool_->size(); + + // If the raster thread isn't merged, create layers on the platform thread and block until + // complete. + auto latch = std::make_shared(1u); + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&]() { + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); + } + latch->CountDown(); + }); + if (![[NSThread currentThread] isMainThread]) { + latch->Wait(); + } +} + +/// Update the buffers and mutate the platform views in CATransaction on the platform thread. +void FlutterPlatformViewsController::PerformSubmit( + const LayersMap& platform_view_layers, + std::unordered_map& current_composition_params, + const std::unordered_set& views_to_recomposite, + const std::vector& composition_order, + const std::vector>& unused_layers, + const std::vector>& surface_frames) { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::PerformSubmit"); + FML_DCHECK([[NSThread currentThread] isMainThread]); + + [CATransaction begin]; + + // Configure Flutter overlay views. + for (const auto& [view_id, layer_data] : platform_view_layers) { + layer_data.layer->UpdateViewState(flutter_view_, // + layer_data.rect, // + layer_data.view_id, // + layer_data.overlay_id // + ); + } + + // Dispose unused Flutter Views. + for (auto& view : GetViewsToDispose()) { + [view removeFromSuperview]; + } + + // Composite Platform Views. + for (int64_t view_id : views_to_recomposite) { + CompositeWithParams(view_id, current_composition_params[view_id]); + } + + // Present callbacks. + for (const auto& frame : surface_frames) { + frame->Submit(); + } + + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(unused_layers, composition_order); + + // Organize the layers by their z indexes. + BringLayersIntoView(platform_view_layers, composition_order); - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. - CommitCATransactionIfNeeded(); - return did_submit; + [CATransaction commit]; } -void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { +void FlutterPlatformViewsController::BringLayersIntoView( + const LayersMap& layer_map, + const std::vector& composition_order) { FML_DCHECK(flutter_view_); UIView* flutter_view = flutter_view_.get(); - // Clear the `active_composition_order_`, which will be populated down below. - active_composition_order_.clear(); + + previous_composition_order_.clear(); NSMutableArray* desired_platform_subviews = [NSMutableArray array]; - for (size_t i = 0; i < composition_order_.size(); i++) { - int64_t platform_view_id = composition_order_[i]; - std::vector> layers = layer_map[platform_view_id]; - UIView* platform_view_root = root_views_[platform_view_id].get(); + for (int64_t platform_view_id : composition_order) { + UIView* platform_view_root = platform_views_[platform_view_id].root_view.get(); [desired_platform_subviews addObject:platform_view_root]; - for (const std::shared_ptr& layer : layers) { - [desired_platform_subviews addObject:layer->overlay_view_wrapper]; + + auto maybe_layer_data = layer_map.find(platform_view_id); + if (maybe_layer_data != layer_map.end()) { + [desired_platform_subviews addObject:maybe_layer_data->second.layer->overlay_view_wrapper]; } - active_composition_order_.push_back(platform_view_id); + previous_composition_order_.push_back(platform_view_id); } NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews]; @@ -761,6 +863,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSDictionary* bindings) { return [desired_platform_subviews_set containsObject:object]; }]]; + // Manipulate view hierarchy only if needed, to address a performance issue where // `BringLayersIntoView` is called even when view hierarchy stays the same. // See: https://github.com/flutter/flutter/issues/121833 @@ -773,19 +876,21 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -std::shared_ptr FlutterPlatformViewsController::GetLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - EmbedderViewSlice* slice, - SkRect rect, - int64_t view_id, - int64_t overlay_id, - MTLPixelFormat pixel_format) { - FML_DCHECK(flutter_view_); - std::shared_ptr layer = - layer_pool_->GetLayer(gr_context, ios_context, pixel_format); +std::shared_ptr FlutterPlatformViewsController::GetExistingLayer() { + return layer_pool_->GetNextLayer(); +} - UIView* overlay_view_wrapper = layer->overlay_view_wrapper.get(); +void FlutterPlatformViewsController::CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { + layer_pool_->CreateLayer(gr_context, ios_context, pixel_format); +} + +void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view, + SkRect rect, + int64_t view_id, + int64_t overlay_id) { + UIView* overlay_view_wrapper = this->overlay_view_wrapper.get(); auto screenScale = [UIScreen mainScreen].scale; // Set the size of the overlay view wrapper. // This wrapper view masks the overlay view. @@ -795,61 +900,41 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, overlay_view_wrapper.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; - UIView* overlay_view = layer->overlay_view.get(); + UIView* overlay_view = this->overlay_view.get(); // Set the size of the overlay view. // This size is equal to the device screen size. - overlay_view.frame = [flutter_view_.get() convertRect:flutter_view_.get().bounds - toView:overlay_view_wrapper]; + overlay_view.frame = [flutter_view convertRect:flutter_view.bounds toView:overlay_view_wrapper]; // Set a unique view identifier, so the overlay_view can be identified in XCUITests. overlay_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; - - std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); - // If frame is null, AcquireFrame already printed out an error message. - if (!frame) { - return layer; - } - DlCanvas* overlay_canvas = frame->Canvas(); - int restore_count = overlay_canvas->GetSaveCount(); - overlay_canvas->Save(); - overlay_canvas->ClipRect(rect); - overlay_canvas->Clear(DlColor::kTransparent()); - slice->render_into(overlay_canvas); - overlay_canvas->RestoreToCount(restore_count); - - // This flutter view is never the last in a frame, since we always submit the - // underlay view last. - frame->set_submit_info({.frame_boundary = false}); - - layer->did_submit_last_frame = frame->Submit(); - return layer; } -void FlutterPlatformViewsController::RemoveUnusedLayers() { - for (const auto& layer : layer_pool_->RemoveUnusedLayers()) { +void FlutterPlatformViewsController::RemoveUnusedLayers( + const std::vector>& unused_layers, + const std::vector& composition_order) { + for (const std::shared_ptr& layer : unused_layers) { [layer->overlay_view_wrapper removeFromSuperview]; } std::unordered_set composition_order_set; - for (int64_t view_id : composition_order_) { + for (int64_t view_id : composition_order) { composition_order_set.insert(view_id); } // Remove unused platform views. - for (int64_t view_id : active_composition_order_) { + for (int64_t view_id : previous_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { - UIView* platform_view_root = root_views_[view_id].get(); + UIView* platform_view_root = platform_views_[view_id].root_view.get(); [platform_view_root removeFromSuperview]; } } } -void FlutterPlatformViewsController::DisposeViews() { +std::vector FlutterPlatformViewsController::GetViewsToDispose() { + std::vector views; if (views_to_dispose_.empty()) { - return; + return views; } - FML_DCHECK([[NSThread currentThread] isMainThread]); - std::unordered_set views_to_composite(composition_order_.begin(), composition_order_.end()); std::unordered_set views_to_delay_dispose; @@ -858,31 +943,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_delay_dispose.insert(viewId); continue; } - UIView* root_view = root_views_[viewId].get(); - [root_view removeFromSuperview]; - views_.erase(viewId); - touch_interceptors_.erase(viewId); - root_views_.erase(viewId); + UIView* root_view = platform_views_[viewId].root_view.get(); + views.push_back(root_view); current_composition_params_.erase(viewId); - clip_count_.erase(viewId); views_to_recomposite_.erase(viewId); + platform_views_.erase(viewId); } views_to_dispose_ = std::move(views_to_delay_dispose); -} - -void FlutterPlatformViewsController::BeginCATransaction() { - FML_DCHECK([[NSThread currentThread] isMainThread]); - FML_DCHECK(!catransaction_added_); - [CATransaction begin]; - catransaction_added_ = true; -} - -void FlutterPlatformViewsController::CommitCATransactionIfNeeded() { - if (catransaction_added_) { - FML_DCHECK([[NSThread currentThread] isMainThread]); - [CATransaction commit]; - catransaction_added_ = false; - } + return views; } void FlutterPlatformViewsController::ResetFrameState() { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 43d25192c39a2..16e51ff0cd1d0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -5,6 +5,7 @@ #import #import #import +#include "fml/synchronization/count_down_latch.h" #import "flutter/fml/thread.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" @@ -123,28 +124,28 @@ void UpdateAssetResolverByType(std::unique_ptr updated_a } // namespace } // namespace flutter -namespace { -fml::RefPtr CreateNewThread(const std::string& name) { - auto thread = std::make_unique(name); - auto runner = thread->GetTaskRunner(); - return runner; -} -} // namespace - @interface FlutterPlatformViewsTest : XCTestCase @end @implementation FlutterPlatformViewsTest +namespace { +fml::RefPtr GetDefaultTaskRunner() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + return fml::MessageLoop::GetCurrent().GetTaskRunner(); +} +} // namespace + - (void)testFlutterViewOnlyCreateOnceInOneFrame { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -167,8 +168,8 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { methodCallWithMethodName:@"create" arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], result); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -185,9 +186,6 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - - flutterPlatformViewsController->GetPlatformViewRect(2); XCTAssertNotNil(gMockPlatformView); @@ -196,13 +194,14 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { - (void)testCanCreatePlatformViewWithoutFlutterView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -288,13 +287,14 @@ - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { - (void)testApplyBackdropFilter { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -320,8 +320,8 @@ - (void)testApplyBackdropFilter { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -336,13 +336,15 @@ - (void)testApplyBackdropFilter { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; // childClippingView has visual effect view with the correct configurations. NSUInteger numberOfExpectedVisualEffectView = 0; @@ -362,13 +364,14 @@ - (void)testApplyBackdropFilter { - (void)testApplyBackdropFilterWithCorrectFrame { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -394,8 +397,8 @@ - (void)testApplyBackdropFilterWithCorrectFrame { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -410,13 +413,15 @@ - (void)testApplyBackdropFilterWithCorrectFrame { std::make_unique(screenScaleMatrix, SkSize::Make(5, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; // childClippingView has visual effect view with the correct configurations. NSUInteger numberOfExpectedVisualEffectView = 0; @@ -436,13 +441,14 @@ - (void)testApplyBackdropFilterWithCorrectFrame { - (void)testApplyMultipleBackdropFilters { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -468,8 +474,8 @@ - (void)testApplyMultipleBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -486,13 +492,15 @@ - (void)testApplyMultipleBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(20, 20), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSUInteger numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -511,13 +519,14 @@ - (void)testApplyMultipleBackdropFilters { - (void)testAddBackdropFilters { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -543,8 +552,8 @@ - (void)testAddBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -559,13 +568,15 @@ - (void)testAddBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -596,9 +607,11 @@ - (void)testAddBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -627,13 +640,14 @@ - (void)testAddBackdropFilters { - (void)testRemoveBackdropFilters { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -659,8 +673,8 @@ - (void)testRemoveBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -677,13 +691,15 @@ - (void)testRemoveBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -712,9 +728,11 @@ - (void)testRemoveBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -751,9 +769,11 @@ - (void)testRemoveBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSUInteger numberOfExpectedVisualEffectView = 0u; for (UIView* subview in childClippingView.subviews) { @@ -766,13 +786,14 @@ - (void)testRemoveBackdropFilters { - (void)testEditBackdropFilters { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -798,8 +819,8 @@ - (void)testEditBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -816,13 +837,15 @@ - (void)testEditBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -860,9 +883,11 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -914,9 +939,11 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; for (UIView* subview in childClippingView.subviews) { if (![subview isKindOfClass:[UIVisualEffectView class]]) { @@ -966,9 +993,11 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; for (UIView* subview in childClippingView.subviews) { if (![subview isKindOfClass:[UIVisualEffectView class]]) { @@ -1014,9 +1043,11 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; for (UIView* subview in childClippingView.subviews) { if (![subview isKindOfClass:[UIVisualEffectView class]]) { @@ -1045,13 +1076,14 @@ - (void)testEditBackdropFilters { - (void)testApplyBackdropFilterNotDlBlurImageFilter { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1077,8 +1109,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1093,14 +1125,16 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSUInteger numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1133,9 +1167,11 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1172,9 +1208,11 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1211,9 +1249,11 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1244,9 +1284,11 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1353,13 +1395,14 @@ - (void)testBackdropFilterVisualEffectSubviewBackgroundColor { - (void)testCompositePlatformView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1385,8 +1428,8 @@ - (void)testCompositePlatformView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1403,21 +1446,24 @@ - (void)testCompositePlatformView { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds - toView:mockFlutterView]; + toView:flutterView]; XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); } - (void)testBackdropFilterCorrectlyPushedAndReset { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1443,8 +1489,8 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1461,13 +1507,15 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); flutterPlatformViewsController->PushFilterToVisitedPlatformViews( filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; // childClippingView has visual effect view with the correct configurations. NSUInteger numberOfExpectedVisualEffectView = 0; @@ -1489,11 +1537,13 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1507,13 +1557,14 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1539,8 +1590,8 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1558,9 +1609,11 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds - toView:mockFlutterView]; + toView:flutterView]; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; // The childclippingview's frame is set based on flow, but the platform view's frame is set based @@ -1580,13 +1633,14 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1612,8 +1666,8 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params. flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack. @@ -1636,26 +1690,29 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; XCTAssertNil(childClippingView.maskView); } - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1681,8 +1738,8 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack. @@ -1703,27 +1760,30 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; XCTAssertNotNil(childClippingView.maskView); } - (void)testClipRect { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1749,8 +1809,8 @@ - (void)testClipRect { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1765,20 +1825,22 @@ - (void)testClipRect { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; CGRect insideClipping = CGRectMake(2, 2, 3, 3); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView]; if (CGRectContainsPoint(insideClipping, point)) { XCTAssertEqual(alpha, 255); } else { @@ -1790,13 +1852,14 @@ - (void)testClipRect { - (void)testClipRRect { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1822,8 +1885,8 @@ - (void)testClipRRect { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1838,14 +1901,16 @@ - (void)testClipRRect { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; /* ClippingMask outterClipping @@ -1872,7 +1937,7 @@ - (void)testClipRRect { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView]; if (CGRectContainsPoint(innerClipping1, point) || CGRectContainsPoint(innerClipping2, point)) { // Pixels inside either of the 2 inner clippings should be fully opaque. @@ -1890,13 +1955,14 @@ - (void)testClipRRect { - (void)testClipPath { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1922,8 +1988,8 @@ - (void)testClipPath { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1939,14 +2005,16 @@ - (void)testClipPath { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; /* ClippingMask outterClipping @@ -1973,7 +2041,7 @@ - (void)testClipPath { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView]; if (CGRectContainsPoint(innerClipping1, point) || CGRectContainsPoint(innerClipping2, point)) { // Pixels inside either of the 2 inner clippings should be fully opaque. @@ -1991,13 +2059,14 @@ - (void)testClipPath { - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2043,27 +2112,28 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { // Before setting flutter view controller, events are not dispatched. NSSet* touches1 = [[NSSet alloc] init]; id event1 = OCMClassMock([UIEvent class]); - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMReject([flutterViewContoller touchesBegan:touches1 withEvent:event1]); // Set flutter view controller allows events to be dispatched. NSSet* touches2 = [[NSSet alloc] init]; id event2 = OCMClassMock([UIEvent class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesBegan:touches2 withEvent:event2]); } - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2105,15 +2175,15 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu break; } } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); { // ***** Sequence 1, finishing touch event with touchEnded ***** // - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); NSSet* touches1 = [[NSSet alloc] init]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]); flutterPlatformViewsController->SetFlutterViewController(nil); @@ -2121,33 +2191,33 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu NSSet* touches2 = [[NSSet alloc] init]; id event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesMoved:touches2 withEvent:event2]); NSSet* touches3 = [[NSSet alloc] init]; id event3 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); + OCMVerify([flutterViewContoller touchesEnded:touches3 withEvent:event3]); // Now the 2nd touch sequence should not be allowed. NSSet* touches4 = [[NSSet alloc] init]; id event4 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; - OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + OCMReject([flutterViewContoller touchesBegan:touches4 withEvent:event4]); NSSet* touches5 = [[NSSet alloc] init]; id event5 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([flutterViewContoller touchesEnded:touches5 withEvent:event5]); } { // ***** Sequence 2, finishing touch event with touchCancelled ***** // - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); NSSet* touches1 = [[NSSet alloc] init]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]); flutterPlatformViewsController->SetFlutterViewController(nil); @@ -2155,23 +2225,23 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu NSSet* touches2 = [[NSSet alloc] init]; id event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesMoved:touches2 withEvent:event2]); NSSet* touches3 = [[NSSet alloc] init]; id event3 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]); + OCMVerify([flutterViewContoller forceTouchesCancelled:touches3]); // Now the 2nd touch sequence should not be allowed. NSSet* touches4 = [[NSSet alloc] init]; id event4 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; - OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + OCMReject([flutterViewContoller touchesBegan:touches4 withEvent:event4]); NSSet* touches5 = [[NSSet alloc] init]; id event5 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([flutterViewContoller touchesEnded:touches5 withEvent:event5]); } flutterPlatformViewsController->Reset(); @@ -2180,13 +2250,14 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu - (void) testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2228,78 +2299,79 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu break; } } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); // The touches in this sequence requires 1 touch object, we always create the NSSet with one item. NSSet* touches1 = [NSSet setWithObject:@1]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]); - FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); + FlutterViewController* flutterViewContoller2 = OCMClassMock([FlutterViewController class]); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller2); // Touch events should still send to the old FlutterViewController if FlutterViewController // is updated in between. NSSet* touches2 = [NSSet setWithObject:@1]; id event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); - OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesBegan:touches2 withEvent:event2]); + OCMReject([flutterViewContoller2 touchesBegan:touches2 withEvent:event2]); NSSet* touches3 = [NSSet setWithObject:@1]; id event3 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); - OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); + OCMVerify([flutterViewContoller touchesMoved:touches3 withEvent:event3]); + OCMReject([flutterViewContoller2 touchesMoved:touches3 withEvent:event3]); NSSet* touches4 = [NSSet setWithObject:@1]; id event4 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); - OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); + OCMVerify([flutterViewContoller touchesEnded:touches4 withEvent:event4]); + OCMReject([flutterViewContoller2 touchesEnded:touches4 withEvent:event4]); NSSet* touches5 = [NSSet setWithObject:@1]; id event5 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); - OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); + OCMVerify([flutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([flutterViewContoller2 touchesEnded:touches5 withEvent:event5]); // Now the 2nd touch sequence should go to the new FlutterViewController NSSet* touches6 = [NSSet setWithObject:@1]; id event6 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; - OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); - OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); + OCMVerify([flutterViewContoller2 touchesBegan:touches6 withEvent:event6]); + OCMReject([flutterViewContoller touchesBegan:touches6 withEvent:event6]); // Allow the touch events to finish NSSet* touches7 = [NSSet setWithObject:@1]; id event7 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; - OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); - OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); + OCMVerify([flutterViewContoller2 touchesMoved:touches7 withEvent:event7]); + OCMReject([flutterViewContoller touchesMoved:touches7 withEvent:event7]); NSSet* touches8 = [NSSet setWithObject:@1]; id event8 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; - OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); - OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); + OCMVerify([flutterViewContoller2 touchesEnded:touches8 withEvent:event8]); + OCMReject([flutterViewContoller touchesEnded:touches8 withEvent:event8]); flutterPlatformViewsController->Reset(); } - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2341,29 +2413,30 @@ - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { break; } } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); NSSet* touches1 = [NSSet setWithObject:@1]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]); + OCMVerify([flutterViewContoller forceTouchesCancelled:touches1]); flutterPlatformViewsController->Reset(); } - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2397,7 +2470,9 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; auto mock_surface = std::make_unique( nullptr, framebuffer_info, @@ -2410,7 +2485,9 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin auto embeddedViewParams_2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + auto mock_surface_submit_true = std::make_unique( nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, @@ -2423,13 +2500,14 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin - (void) testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2440,8 +2518,8 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2463,7 +2541,7 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin auto embeddedViewParams = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not // added to flutter_view_. @@ -2475,13 +2553,14 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin - (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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2492,8 +2571,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2516,7 +2595,9 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); // Second frame, |EmbeddedViewCount| should be empty at the start @@ -2526,20 +2607,23 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); } - (void) testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2550,8 +2634,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2581,11 +2665,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); + auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -2599,10 +2682,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; - UIView* flutterView = clippingView1.superview; XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); @@ -2613,11 +2696,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); + embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2627,6 +2709,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*frame_size=*/SkISize::Make(800, 600)); XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added after the second clipping view."); @@ -2635,13 +2718,14 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { - (void) testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2652,8 +2736,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2683,11 +2767,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); + auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -2701,10 +2784,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; - UIView* flutterView = clippingView1.superview; XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); @@ -2715,11 +2798,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); + embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2729,76 +2811,12 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*frame_size=*/SkISize::Make(800, 600)); XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); } -- (void)testThreadMergeAtEndFrame { - flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner_platform = CreateNewThread("FlutterPlatformViewsTest1"); - auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); - flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread_task_runner_platform, - /*raster=*/thread_task_runner_other, - /*ui=*/thread_task_runner_other, - /*io=*/thread_task_runner_other); - auto flutterPlatformViewsController = std::make_shared(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - XCTestExpectation* waitForPlatformView = - [self expectationWithDescription:@"wait for platform view to be created"]; - FlutterResult result = ^(id result) { - [waitForPlatformView fulfill]; - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; - XCTAssertNotNil(gMockPlatformView); - - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - SkMatrix finalMatrix; - flutter::MutatorsStack stack; - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - - fml::RefPtr raster_thread_merger = - fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), - thread_task_runner_other->GetTaskQueueId()); - XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), - flutter::PostPrerollResult::kSkipAndRetryFrame); - XCTAssertFalse(raster_thread_merger->IsMerged()); - - flutterPlatformViewsController->EndFrame(true, raster_thread_merger); - XCTAssertTrue(raster_thread_merger->IsMerged()); - - // Unmerge threads before the end of the test - // TaskRunners are required to be unmerged before destruction. - while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) { - } -} - - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { unsigned char pixel[4] = {0}; @@ -2887,13 +2905,14 @@ - (void)testMaskViewsReleasedWhenPoolIsReleased { - (void)testClipMaskViewIsReused { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2918,8 +2937,8 @@ - (void)testClipMaskViewIsReused { result); XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack1; // Layer tree always pushes a screen scale factor to the stack @@ -2934,7 +2953,9 @@ - (void)testClipMaskViewIsReused { screenScaleMatrix, SkSize::Make(10, 10), stack1); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + UIView* childClippingView1 = gMockPlatformView.superview.superview; UIView* maskView1 = childClippingView1.maskView; XCTAssertNotNil(maskView1); @@ -2947,7 +2968,9 @@ - (void)testClipMaskViewIsReused { auto embeddedViewParams3 = std::make_unique( screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); - flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + childClippingView1 = gMockPlatformView.superview.superview; // This overrides gMockPlatformView to point to the newly created platform view. @@ -2960,7 +2983,9 @@ - (void)testClipMaskViewIsReused { auto embeddedViewParams4 = std::make_unique( screenScaleMatrix, SkSize::Make(10, 10), stack1); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + UIView* childClippingView2 = gMockPlatformView.superview.superview; UIView* maskView2 = childClippingView2.maskView; @@ -2971,13 +2996,14 @@ - (void)testClipMaskViewIsReused { - (void)testDifferentClipMaskViewIsUsedForEachView { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3012,8 +3038,8 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { UIView* view2 = gMockPlatformView; XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack1; // Layer tree always pushes a screen scale factor to the stack @@ -3033,11 +3059,15 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + UIView* childClippingView1 = view1.superview.superview; flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + UIView* childClippingView2 = view2.superview.superview; UIView* maskView1 = childClippingView1.maskView; UIView* maskView2 = childClippingView2.maskView; @@ -3046,13 +3076,14 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3078,8 +3109,8 @@ - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { result); XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack1; // Layer tree always pushes a screen scale factor to the stack @@ -3099,7 +3130,9 @@ - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + UIView* childClippingView = gMockPlatformView.superview.superview; UIView* maskView = childClippingView.maskView; @@ -3136,13 +3169,14 @@ - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView - (void)testDisposingViewInCompositionOrderDoNotCrash { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3153,8 +3187,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -3184,12 +3218,11 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { auto embeddedViewParams0 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); - flutterPlatformViewsController->CompositeEmbeddedView(0); auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."]; @@ -3227,7 +3260,6 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); @@ -3248,13 +3280,14 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { } - (void)testOnlyPlatformViewsAreRemovedWhenReset { 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); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3277,8 +3310,8 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { methodCallWithMethodName:@"create" arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], result); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -3295,7 +3328,6 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -3310,11 +3342,11 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); UIView* someView = [[UIView alloc] init]; - [mockFlutterView addSubview:someView]; + [flutterView addSubview:someView]; flutterPlatformViewsController->Reset(); - XCTAssertEqual(mockFlutterView.subviews.count, 1u); - XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); + XCTAssertEqual(flutterView.subviews.count, 1u); + XCTAssertEqual(flutterView.subviews.firstObject, someView); } - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { @@ -3335,9 +3367,9 @@ - (void)testLayerPool { auto pool = flutter::FlutterPlatformViewLayerPool{}; // Add layers to the pool. - auto layer1 = pool.GetLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); + pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); XCTAssertEqual(pool.size(), 1u); - auto layer2 = pool.GetLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); + pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); XCTAssertEqual(pool.size(), 2u); // Mark all layers as unused. diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 01d7127cf48f8..e87d1cbb15cfb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,6 +6,9 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "fml/task_runner.h" +#include "impeller/base/thread_safety.h" +#include "third_party/skia/include/core/SkRect.h" #include @@ -138,7 +141,6 @@ void ResetAnchor(CALayer* layer); CGRect GetCGRectFromSkRect(const SkRect& clipSkRect); BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2); -class IOSContextGL; class IOSSurface; struct FlutterPlatformViewLayer { @@ -161,28 +163,41 @@ struct FlutterPlatformViewLayer { // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrDirectContext* gr_context; + + void UpdateViewState(UIView* flutter_view, SkRect rect, int64_t view_id, int64_t overlay_id); }; -// This class isn't thread safe. +/// @brief Storage for Overlay layers across frames. +/// +/// Note: this class does not synchronize access to its layers or any layer removal. As it +/// is currently used, layers must be created on the platform thread but other methods of +/// it are called on the raster thread. This is safe as overlay layers are only ever added +/// while the raster thread is latched. class FlutterPlatformViewLayerPool { public: FlutterPlatformViewLayerPool() = default; ~FlutterPlatformViewLayerPool() = default; - // Gets a layer from the pool if available, or allocates a new one. - // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. - std::shared_ptr GetLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); + /// @brief Gets a layer from the pool if available. + /// + /// The layer is marked as used until [RecycleLayers] is called. + std::shared_ptr GetNextLayer(); - // Removes unused layers from the pool. Returns the unused layers. + /// @brief Create a new overlay layer. + /// + /// This method can only be called on the Platform thread. + void CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); + + /// @brief Removes unused layers from the pool. Returns the unused layers. std::vector> RemoveUnusedLayers(); - // Marks the layers in the pool as available for reuse. + /// @brief Marks the layers in the pool as available for reuse. void RecycleLayers(); - // Returns the count of layers currently in the pool. + /// @brief The count of layers currently in the pool. size_t size() const; private: @@ -212,6 +227,8 @@ class FlutterPlatformViewsController { fml::WeakPtr GetWeakPtr(); + void SetTaskRunner(const fml::RefPtr& platform_task_runner); + void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer)); void SetFlutterViewController(UIViewController* flutter_view_controller) @@ -233,6 +250,7 @@ class FlutterPlatformViewsController { // Also reverts the composition_order_ to its original state at the beginning of the frame. void CancelFrame(); + // Runs on the raster thread. void PrerollCompositeEmbeddedView(int64_t view_id, std::unique_ptr params); @@ -254,21 +272,27 @@ class FlutterPlatformViewsController { // returns nil. FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id); + // Runs on the raster thread. PostPrerollResult PostPrerollAction( const fml::RefPtr& raster_thread_merger); + // Runs on the raster thread. void EndFrame(bool should_resubmit_frame, const fml::RefPtr& raster_thread_merger); + // Return the Canvas for the overlay slice for the given platform view. + // + // Runs on the raster thread. DlCanvas* CompositeEmbeddedView(int64_t view_id); - // The rect of the platform view at index view_id. This rect has been translated into the - // host view coordinate system. Units are device screen pixels. - SkRect GetPlatformViewRect(int64_t view_id); - // Discards all platform views instances and auxiliary resources. + // + // Runs on the raster thread. void Reset(); + // Encode rendering for the Flutter overlay views and queue up perform platform view mutations. + // + // Runs on the raster thread. bool SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, std::unique_ptr frame); @@ -287,8 +311,39 @@ class FlutterPlatformViewsController { // Pushes the view id of a visted platform view to the list of visied platform views. void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); } + // Visible for testing. + void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); + + const EmbeddedViewParams& GetCompositionParams(int64_t view_id) const { + return current_composition_params_.find(view_id)->second; + } + private: - using LayersMap = std::map>>; + struct LayerData { + SkRect rect; + int64_t view_id; + int64_t overlay_id; + std::shared_ptr layer; + }; + + using LayersMap = std::unordered_map; + + // Update the buffers and mutate the platform views in CATransaction. + // + // Runs on the platform thread. + void PerformSubmit(const LayersMap& platform_view_layers, + std::unordered_map& current_composition_params, + const std::unordered_set& views_to_recomposite, + const std::vector& composition_order, + const std::vector>& unused_layers, + const std::vector>& surface_frames); + + /// @brief Populate any missing overlay layers. + /// + /// This requires posting a task to the platform thread and blocking on its completion. + void CreateMissingOverlays(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + size_t required_overlay_layers); void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); void OnDispose(FlutterMethodCall* call, FlutterResult result) @@ -297,18 +352,9 @@ class FlutterPlatformViewsController { __attribute__((cf_audited_transfer)); void OnRejectGesture(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); - // Dispose the views in `views_to_dispose_`. - void DisposeViews(); - - // Returns true if there are embedded views in the scene at current frame - // Or there will be embedded views in the next frame. - // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 - // Make this method check if there are pending view operations instead. - // Also rename it to `HasPendingViewOperations`. - bool HasPlatformViewThisOrNextFrame(); - // Traverse the `mutators_stack` and return the number of clip operations. - int CountClips(const MutatorsStack& mutators_stack); + /// @brief Return all views to be disposed on the platform thread. + std::vector GetViewsToDispose(); void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer)); @@ -325,30 +371,23 @@ class FlutterPlatformViewsController { UIView* embedded_view, const SkRect& bounding_rect) __attribute__((cf_audited_transfer)); - void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); + std::shared_ptr GetExistingLayer(); + + // Runs on the platform thread. + void CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); - // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from - // the picture on the layer's canvas. - std::shared_ptr GetLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - EmbedderViewSlice* slice, - SkRect rect, - int64_t view_id, - int64_t overlay_id, - MTLPixelFormat pixel_format); // Removes overlay views and platform views that aren't needed in the current frame. // Must run on the platform thread. - void RemoveUnusedLayers(); + void RemoveUnusedLayers( + const std::vector>& unused_layers, + const std::vector& composition_order); + // Appends the overlay views and platform view and sets their z index based on the composition // order. - void BringLayersIntoView(LayersMap layer_map); - - // Begin a CATransaction. - // This transaction needs to be balanced with |CommitCATransactionIfNeeded|. - void BeginCATransaction(); - - // Commit a CATransaction if |BeginCATransaction| has been called during the frame. - void CommitCATransactionIfNeeded(); + void BringLayersIntoView(const LayersMap& layer_map, + const std::vector& composition_order); // Resets the state of the frame. void ResetFrameState(); @@ -366,62 +405,82 @@ class FlutterPlatformViewsController { fml::scoped_nsobject flutter_view_; fml::scoped_nsobject> flutter_view_controller_; fml::scoped_nsobject mask_view_pool_; - std::map>> factories_; - std::map>> views_; - std::map> touch_interceptors_; - // Mapping a platform view ID to the top most parent view (root_view) of a platform view. In - // |SubmitFrame|, root_views_ are added to flutter_view_ as child views. - // - // The platform view with the view ID is a child of the root view; If the platform view is not - // clipped, and no clipping view is added, the root view will be the intercepting view. - std::map> root_views_; - // Mapping a platform view ID to its latest composition params. - std::map current_composition_params_; - // Mapping a platform view ID to the count of the clipping operations that were applied to the - // platform view last time it was composited. - std::map clip_count_; + std::unordered_map>> + factories_; + + // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. + std::unordered_map + gesture_recognizers_blocking_policies_; + + /// The size of the current onscreen surface in physical pixels. SkISize frame_size_; - // The number of frames the rasterizer task runner will continue - // to run on the platform thread after no platform view is rendered. - // - // Note: this is an arbitrary number that attempts to account for cases - // where the platform view might be momentarily off the screen. - static const int kDefaultMergedLeaseDuration = 10; + /// The task runner for posting tasks to the platform thread. + fml::RefPtr platform_task_runner_; - // Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on - // the next frame. + /// Each of the following structs stores part of the platform view hierarchy according to its + /// ID. + /// + /// This data must only be accessed on the platform thread. + struct PlatformViewData { + fml::scoped_nsobject> view; + fml::scoped_nsobject touch_interceptor; + fml::scoped_nsobject root_view; + }; + + /// This data must only be accessed on the platform thread. + std::unordered_map platform_views_; + + /// The composition parameters for each platform view. + /// + /// This state is only modified on the raster thread. + std::unordered_map current_composition_params_; + + /// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on + /// the next frame. + /// + /// This state is modified on both the platform and raster thread. std::unordered_set views_to_dispose_; - // A vector of embedded view IDs according to their composition order. - // The last ID in this vector belond to the that is composited on top of all others. + /// view IDs in composition order. + /// + /// This state is only modified on the raster thread. std::vector composition_order_; - // A vector of visited platform view IDs. + /// platform view IDs visited during layer tree composition. + /// + /// This state is only modified on the raster thread. std::vector visited_platform_views_; - // The latest composition order that was presented in Present(). - std::vector active_composition_order_; - - // Only compoiste platform views in this set. + /// Only composite platform views in this set. + /// + /// This state is only modified on the raster thread. std::unordered_set views_to_recomposite_; - // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. - std::map - gesture_recognizers_blocking_policies_; +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + /// A set to keep track of embedded views that do not have (0, 0) origin. + /// An insertion triggers a warning message about non-zero origin logged on the debug console. + /// See https://github.com/flutter/flutter/issues/109700 for details. + std::unordered_set non_zero_origin_views_; +#endif - bool catransaction_added_ = false; + /// @brief The composition order from the previous thread. + /// + /// Only accessed from the platform thread. + std::vector previous_composition_order_; + + /// Whether the previous frame had any platform views in active composition order. + /// + /// This state is tracked so that the first frame after removing the last platform view + /// runs through the platform view rendering code path, giving us a chance to remove the + /// platform view from the UIView hierarchy. + /// + /// Only accessed from the raster thread. + bool had_platform_views_ = false; // WeakPtrFactory must be the last member. std::unique_ptr> weak_factory_; -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG - // A set to keep track of embedded views that does not have (0, 0) origin. - // An insertion triggers a warning message about non-zero origin logged on the debug console. - // See https://github.com/flutter/flutter/issues/109700 for details. - std::unordered_set non_zero_origin_views_; -#endif - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index c11bd1da28500..83d01ec2c6901 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -36,6 +36,11 @@ FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; +void FlutterPlatformViewsController::SetTaskRunner( + const fml::RefPtr& platform_task_runner) { + platform_task_runner_ = platform_task_runner; +} + fml::WeakPtr FlutterPlatformViewsController::GetWeakPtr() { return weak_factory_->GetWeakPtr(); } diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index ec823293681c8..81b79a386ea70 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -281,6 +281,7 @@ - (void)testSemanticsDeallocated { auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -342,6 +343,7 @@ - (void)testSemanticsDeallocatedWithoutLoadingView { auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -388,6 +390,7 @@ - (void)testReplacedSemanticsDoesNotCleanupChildren { /*io=*/thread_task_runner); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -484,6 +487,7 @@ - (void)testScrollableSemanticsDeallocated { /*io=*/thread_task_runner); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -558,6 +562,7 @@ - (void)testBridgeReplacesSemanticsNode { /*io=*/thread_task_runner); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2178,6 +2183,8 @@ - (void)testPlatformViewDestructorDoesNotCallSemanticsAPIs { id mockFlutterViewController = OCMClassMock([FlutterViewController class]); auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); + OCMStub([mockFlutterViewController platformViewsController]) .andReturn(flutterPlatformViewsController.get()); auto weakFactory = std::make_unique>( diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 953966378b15a..cc7ec59b34800 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -14,7 +14,7 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { public: IOSExternalViewEmbedder(const std::shared_ptr& platform_views_controller, - std::shared_ptr context); + const std::shared_ptr& context); // |ExternalViewEmbedder| virtual ~IOSExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 0b03e8dd1664a..6cd0430481251 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/ios_external_view_embedder.h" +#include "fml/task_runner.h" #include "flutter/common/constants.h" @@ -12,8 +13,8 @@ IOSExternalViewEmbedder::IOSExternalViewEmbedder( const std::shared_ptr& platform_views_controller, - std::shared_ptr context) - : platform_views_controller_(platform_views_controller), ios_context_(std::move(context)) { + const std::shared_ptr& context) + : platform_views_controller_(platform_views_controller), ios_context_(context) { FML_CHECK(ios_context_); } @@ -76,6 +77,7 @@ const std::shared_ptr& aiks_context, std::unique_ptr frame) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFlutterView"); + // TODO(dkwingsmt): This class only supports rendering into the implicit view. // Properly support multi-view in the future. FML_DCHECK(flutter_view_id == kFlutterImplicitViewId); @@ -94,7 +96,12 @@ // |ExternalViewEmbedder| bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() { +// TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim. +#if FML_OS_IOS_SIMULATOR return true; +#else + return false; +#endif // FML_OS_IOS_SIMULATOR } // |ExternalViewEmbedder| diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index edf0e6c886192..8d1a75d6f1893 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -58,14 +58,6 @@ // backdrop filters. Flutter plugins that create platform views may also read from the layer. layer.framebufferOnly = NO; - // When there are platform views in the scene, the drawable needs to be presented in the same - // transaction as the one created for platform views. When the drawable are being presented from - // the raster thread, we may not be able to use a transaction as it will dirty the UIViews being - // presented. If there is a non-Flutter UIView active, such as in add2app or a - // presentViewController page transition, then this will cause CoreAnimation assertion errors and - // exit the app. - layer.presentsWithTransaction = [[NSThread currentThread] isMainThread]; - return (__bridge GPUCAMetalLayerHandle)layer; } diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.h b/shell/platform/darwin/ios/ios_surface_metal_skia.h index 65549762c4949..e696bab8c40de 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.h +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.h @@ -47,6 +47,9 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalSkia final : public IOSSurf // |GPUSurfaceMetalDelegate| bool PresentDrawable(GrMTLHandle drawable) const override __attribute__((cf_audited_transfer)); + // |GPUSurfaceMetalDelegate| + bool PreparePresent(GrMTLHandle drawable) const override; + // |GPUSurfaceMetalDelegate| GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override __attribute__((cf_audited_transfer)); diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 32465e2153201..6c0b534d56d1a 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -80,22 +80,25 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; } // |GPUSurfaceMetalDelegate| -bool IOSSurfaceMetalSkia::PresentDrawable(GrMTLHandle drawable) const { - if (drawable == nullptr) { - FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface."; - return false; - } - +bool IOSSurfaceMetalSkia::PreparePresent(GrMTLHandle drawable) const { id command_buffer = [command_queue_ commandBuffer]; - id metal_drawable = (__bridge id)drawable; if ([metal_drawable conformsToProtocol:@protocol(FlutterMetalDrawable)]) { [(id)metal_drawable flutterPrepareForPresent:command_buffer]; } - [command_buffer commit]; [command_buffer waitUntilScheduled]; + return true; +} +// |GPUSurfaceMetalDelegate| +bool IOSSurfaceMetalSkia::PresentDrawable(GrMTLHandle drawable) const { + if (drawable == nullptr) { + FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface."; + return false; + } + + id metal_drawable = (__bridge id)drawable; [metal_drawable present]; return true; } diff --git a/testing/scenario_app/ios/README.md b/testing/scenario_app/ios/README.md index 62e3e538f3df6..bec73196610a1 100644 --- a/testing/scenario_app/ios/README.md +++ b/testing/scenario_app/ios/README.md @@ -9,14 +9,14 @@ run: ```sh # From the root of the engine repository -$ ./testing/run_ios_tests.sh ios_debug_sim_unopt +$ ./testing/scenario_app/run_ios_tests.sh ios_debug_sim_unopt ``` or: ```sh # From the root of the engine repository -$ ./testing/run_ios_tests.sh ios_debug_sim_unopt_arm64 +$ ./testing/scenario_app/run_ios_tests.sh ios_debug_sim_unopt_arm64 ``` To run or debug in Xcode, open the xcodeproj file located in