From 4ae4e67e0e0113ea415309c41792a0e0efcf8a8d Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 20 Mar 2020 13:48:19 -0700 Subject: [PATCH] Revert "Implement unobstructed Platform Views on iOS (#17049)" This reverts commit 2627634be807a28f4d56c5d7cd878c3ff6ce07ac. --- flow/embedded_views.cc | 5 +- flow/embedded_views.h | 5 +- flow/layers/picture_layer.cc | 2 +- flow/layers/picture_layer_unittests.cc | 3 + shell/common/rasterizer.cc | 12 +- .../framework/Source/FlutterPlatformViews.mm | 320 ++++++------------ .../Source/FlutterPlatformViews_Internal.h | 87 +---- .../Source/FlutterPlatformViews_Internal.mm | 3 +- shell/platform/darwin/ios/ios_surface.h | 5 +- shell/platform/darwin/ios/ios_surface.mm | 12 +- .../embedder_external_view_embedder.cc | 6 +- .../embedder_external_view_embedder.h | 5 +- .../Scenarios.xcodeproj/project.pbxproj | 4 - .../ios/Scenarios/Scenarios/AppDelegate.m | 7 - .../UnobstructedPlatformViewTests.m | 254 -------------- ...tform_view_clippath_iPhone 8_simulator.png | Bin 20295 -> 30022 bytes ...form_view_transform_iPhone 8_simulator.png | Bin 22360 -> 33727 bytes testing/scenario_app/lib/main.dart | 6 - .../scenario_app/lib/src/platform_view.dart | 227 +------------ 19 files changed, 124 insertions(+), 839 deletions(-) delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 5234bf1e50c8c..c660f4691318b 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,13 +6,10 @@ namespace flutter { -bool ExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { +bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { return false; }; -void ExternalViewEmbedder::FinishFrame(){}; - void MutatorsStack::PushClipRect(const SkRect& rect) { std::shared_ptr element = std::make_shared(rect); vector_.push_back(element); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 7a491a8a152ef..030eb88c8a06d 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -248,10 +248,7 @@ class ExternalViewEmbedder { // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id) = 0; - virtual bool SubmitFrame(GrContext* context, SkCanvas* background_canvas); - - // This is called after submitting the embedder frame and the surface frame. - virtual void FinishFrame(); + virtual bool SubmitFrame(GrContext* context); FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 08c09cc9e833b..3bc7e394c1033 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -59,7 +59,7 @@ void PictureLayer::Paint(PaintContext& context) const { return; } } - picture()->playback(context.leaf_nodes_canvas); + context.leaf_nodes_canvas->drawPicture(picture()); } } // namespace flutter diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 4f565cf500ecc..687c870eeac66 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -94,6 +94,9 @@ TEST_F(PictureLayerTest, SimplePicture) { 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( layer_offset_matrix)}}, #endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), + SkMatrix()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f7e4350fe9c97..811898b5a42d2 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -342,17 +342,9 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (raster_status == RasterStatus::kFailed) { return raster_status; } + frame->Submit(); if (external_view_embedder != nullptr) { - external_view_embedder->SubmitFrame(surface_->GetContext(), - root_surface_canvas); - // The external view embedder may mutate the root surface canvas while - // submitting the frame. - // Therefore, submit the final frame after asking the external view - // embedder to submit the frame. - frame->Submit(); - external_view_embedder->FinishFrame(); - } else { - frame->Submit(); + external_view_embedder->SubmitFrame(surface_->GetContext()); } FireNextFrameCallbackIfPresent(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b96fcbbbaa76a..b86c4623fa7a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -8,77 +8,16 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" -#include #include #include #include #include "FlutterPlatformViews_Internal.h" -#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { -std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( - GrContext* gr_context, - std::shared_ptr ios_context) { - if (available_layer_index_ >= layers_.size()) { - std::shared_ptr layer; - - if (!gr_context) { - fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - - layer = std::make_shared( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - } else { - CGFloat screenScale = [UIScreen mainScreen].scale; - fml::scoped_nsobject overlay_view( - [[FlutterOverlayView alloc] initWithContentsScale:screenScale]); - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - - layer = std::make_shared( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - layer->gr_context = gr_context; - } - layers_.push_back(layer); - } - auto 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(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); - } - available_layer_index_++; - return layer; -} - -void FlutterPlatformViewLayerPool::RecycleLayers() { - available_layer_index_ = 0; -} - -std::vector> -FlutterPlatformViewLayerPool::GetUnusedLayers() { - std::vector> results; - for (size_t i = available_layer_index_; i < layers_.size(); i++) { - results.push_back(layers_[i]); - } - return results; -} - void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset([flutter_view retain]); } @@ -144,9 +83,6 @@ NSObject* embedded_view = [factory createWithFrame:CGRectZero viewIdentifier:viewId arguments:params]; - // Set a unique view identifier, so the platform view can be identified in unit tests. - [embedded_view view].accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%ld]", viewId]; views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] @@ -260,11 +196,8 @@ int view_id, std::unique_ptr params) { picture_recorders_[view_id] = std::make_unique(); - - auto rtree_factory = RTreeFactory(); - platform_view_rtrees_[view_id] = rtree_factory.getInstance(); - picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_), &rtree_factory); - + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); + picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); composition_order_.push_back(view_id); if (current_composition_params_.count(view_id) == 1 && @@ -428,182 +361,77 @@ composition_order_.clear(); active_composition_order_.clear(); picture_recorders_.clear(); - platform_view_rtrees_.clear(); current_composition_params_.clear(); clip_count_.clear(); views_to_recomposite_.clear(); - layer_pool_->RecycleLayers(); -} - -SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) { - UIView* platform_view = [views_[view_id].get() view]; - 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(GrContext* gr_context, - std::shared_ptr ios_context, - SkCanvas* background_canvas) { + std::shared_ptr ios_context) { DisposeViews(); - // Clipping the background canvas before drawing the picture recorders requires to - // save and restore the clip context. - SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); - // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. - LayersMap platform_view_layers; - - auto did_submit = true; - auto num_platform_views = composition_order_.size(); - - for (size_t i = 0; i < num_platform_views; i++) { - int64_t platform_view_id = composition_order_[i]; - sk_sp rtree = platform_view_rtrees_[platform_view_id]; - sk_sp picture = picture_recorders_[platform_view_id]->finishRecordingAsPicture(); - - // Check if the current picture contains overlays that intersect with the - // current platform view or any of the previous platform views. - for (size_t j = i + 1; j > 0; j--) { - int64_t current_platform_view_id = composition_order_[j - 1]; - SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); - std::list intersection_rects = - rtree->searchNonOverlappingDrawnRects(platform_view_rect); - auto allocation_size = intersection_rects.size(); - - // For testing purposes, the overlay id is used to find the overlay view. - // This is the index of the layer for the current platform view. - auto overlay_id = platform_view_layers[current_platform_view_id].size(); - - // If the max number of allocations per platform view is exceeded, - // then join all the rects into a single one. - // - // TODO(egarciad): Consider making this configurable. - // https://github.com/flutter/flutter/issues/52510 - if (allocation_size > kMaxLayerAllocations) { - SkRect joined_rect; - for (const SkRect& rect : intersection_rects) { - joined_rect.join(rect); - } - // Replace the rects in the intersection rects list for a single rect that is - // the union of all the rects in the list. - intersection_rects.clear(); - intersection_rects.push_back(joined_rect); - } - for (SkRect& joined_rect : intersection_rects) { - // Get the intersection rect between the current rect - // and the platform view rect. - joined_rect.intersect(platform_view_rect); - // Clip the background canvas, so it doesn't contain any of the pixels drawn - // on the overlay layer. - background_canvas->clipRect(joined_rect, SkClipOp::kDifference); - // Get a new host layer. - auto layer = GetLayer(gr_context, // - ios_context, // - picture, // - joined_rect, // - current_platform_view_id, // - overlay_id // - ); - did_submit &= layer->did_submit_last_frame; - platform_view_layers[current_platform_view_id].push_back(layer); - overlay_id++; - } - } - background_canvas->drawPicture(picture); - } - // 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); - // Mark all layers as available, so they can be used in the next frame. - layer_pool_->RecycleLayers(); - // Reset the composition order, so next frame starts empty. - composition_order_.clear(); - return did_submit; -} - -void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { + bool did_submit = true; + for (int64_t view_id : composition_order_) { + EnsureOverlayInitialized(view_id, ios_context, gr_context); + auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (frame) { + SkCanvas* canvas = frame->SkiaCanvas(); + canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); + canvas->flush(); + did_submit &= frame->Submit(); + } + } + picture_recorders_.clear(); + if (composition_order_ == active_composition_order_) { + composition_order_.clear(); + return did_submit; + } + DetachUnusedLayers(); + active_composition_order_.clear(); UIView* flutter_view = flutter_view_.get(); - auto zIndex = 0; - 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(); - if (platform_view_root.superview != flutter_view) { - [flutter_view addSubview:platform_view_root]; + for (size_t i = 0; i < composition_order_.size(); i++) { + int view_id = composition_order_[i]; + // We added a chain of super views to the platform view to handle clipping. + // The `platform_view_root` is the view at the top of the chain which is a direct subview of the + // `FlutterView`. + UIView* platform_view_root = root_views_[view_id].get(); + UIView* overlay = overlays_[view_id]->overlay_view; + FML_CHECK(platform_view_root.superview == overlay.superview); + if (platform_view_root.superview == flutter_view) { + [flutter_view bringSubviewToFront:platform_view_root]; + [flutter_view bringSubviewToFront:overlay]; } else { - platform_view_root.layer.zPosition = zIndex++; - } - for (const std::shared_ptr& layer : layers) { - if ([layer->overlay_view superview] != flutter_view) { - [flutter_view addSubview:layer->overlay_view]; - } else { - layer->overlay_view.get().layer.zPosition = zIndex++; - } + [flutter_view addSubview:platform_view_root]; + [flutter_view addSubview:overlay]; + overlay.frame = flutter_view.bounds; } - active_composition_order_.push_back(platform_view_id); - } -} -std::shared_ptr FlutterPlatformViewsController::GetLayer( - GrContext* gr_context, - std::shared_ptr ios_context, - sk_sp picture, - SkRect rect, - int64_t view_id, - int64_t overlay_id) { - auto layer = layer_pool_->GetLayer(gr_context, ios_context); - auto screenScale = [UIScreen mainScreen].scale; - // Set the size of the overlay UIView. - layer->overlay_view.get().frame = CGRectMake(rect.x() / screenScale, // - rect.y() / screenScale, // - rect.width() / screenScale, // - rect.height() / screenScale // - ); - // Set a unique view identifier, so the overlay can be identified in unit tests. - layer->overlay_view.get().accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; - - std::unique_ptr frame = - layer->surface->AcquireFrame(SkISize::Make(rect.width(), rect.height())); - // If frame is null, AcquireFrame already printed out an error message. - if (!frame) { - return layer; - } - auto overlay_canvas = frame->SkiaCanvas(); - overlay_canvas->clear(SK_ColorTRANSPARENT); - // Offset the picture since its absolute position on the scene is determined - // by the position of the overlay view. - overlay_canvas->translate(-rect.x(), -rect.y()); - overlay_canvas->drawPicture(picture); - - layer->did_submit_last_frame = frame->Submit(); - return layer; -} - -void FlutterPlatformViewsController::RemoveUnusedLayers() { - auto layers = layer_pool_->GetUnusedLayers(); - for (const std::shared_ptr& layer : layers) { - [layer->overlay_view removeFromSuperview]; + active_composition_order_.push_back(view_id); } + composition_order_.clear(); + return did_submit; +} +void FlutterPlatformViewsController::DetachUnusedLayers() { std::unordered_set composition_order_set; 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_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { + if (root_views_.find(view_id) == root_views_.end()) { + continue; + } + // We added a chain of super views to the platform view to handle clipping. + // The `platform_view_root` is the view at the top of the chain which is a direct subview of + // the `FlutterView`. UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; + [overlays_[view_id]->overlay_view.get() removeFromSuperview]; } } } @@ -630,6 +458,56 @@ views_to_dispose_.clear(); } +void FlutterPlatformViewsController::EnsureOverlayInitialized( + int64_t overlay_id, + std::shared_ptr ios_context, + GrContext* gr_context) { + FML_DCHECK(flutter_view_); + + auto overlay_it = overlays_.find(overlay_id); + + if (!gr_context) { + if (overlays_.count(overlay_id) != 0) { + return; + } + fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); + overlay_view.get().frame = flutter_view_.get().bounds; + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + overlays_[overlay_id] = std::make_unique( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + return; + } + + if (overlay_it != overlays_.end()) { + FlutterPlatformViewLayer* overlay = overlay_it->second.get(); + if (gr_context != overlay->gr_context) { + overlay->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 = overlay_it->second->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + overlay_it->second->surface = std::move(surface); + } + return; + } + auto contentsScale = flutter_view_.get().layer.contentsScale; + fml::scoped_nsobject overlay_view( + [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]); + overlay_view.get().frame = flutter_view_.get().bounds; + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + overlays_[overlay_id] = std::make_unique( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + overlays_[overlay_id]->gr_context = gr_context; +} + } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 59dc940c4d126..d135d7d2ac290 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,7 +6,6 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #include "flutter/flow/embedded_views.h" -#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" @@ -69,52 +68,12 @@ struct FlutterPlatformViewLayer { std::unique_ptr ios_surface; std::unique_ptr surface; - // Whether a frame for this layer was submitted. - bool did_submit_last_frame; - // The GrContext that is currently used by the overlay surfaces. // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrContext* gr_context; }; -// This class isn't thread safe. -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(GrContext* gr_context, - std::shared_ptr ios_context); - - // Gets the layers in the pool that aren't currently used. - // This method doesn't mark the layers as unused. - std::vector> GetUnusedLayers(); - - // Marks the layers in the pool as available for reuse. - void RecycleLayers(); - - private: - // The index of the entry in the layers_ vector that determines the beginning of the unused - // layers. For example, consider the following vector: - // _____ - // | 0 | - /// |---| - /// | 1 | <-- available_layer_index_ - /// |---| - /// | 2 | - /// |---| - /// - /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 - /// cannot be reused. - size_t available_layer_index_ = 0; - std::vector> layers_; - - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); -}; - class FlutterPlatformViewsController { public: FlutterPlatformViewsController(); @@ -150,37 +109,14 @@ class FlutterPlatformViewsController { SkCanvas* CompositeEmbeddedView(int 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(int view_id); - // Discards all platform views instances and auxiliary resources. void Reset(); - bool SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context, - SkCanvas* background_canvas); + bool SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: - static const size_t kMaxLayerAllocations = 2; - - using LayersMap = std::map>>; - - // The pool of reusable view layers. The pool allows to recycle layer in each frame. - std::unique_ptr layer_pool_; - - // The platform view's R-tree keyed off the view id, which contains any subsequent - // draw operation until the next platform view or the last leaf node in the layer tree. - // - // The R-trees are deleted by the FlutterPlatformViewsController.reset(). - std::map> platform_view_rtrees_; - - // The platform view's picture recorder keyed off the view id, which contains any subsequent - // operation until the next platform view or the end of the last leaf node in the layer tree. - std::map> picture_recorders_; - fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; @@ -227,12 +163,19 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies; + std::map> picture_recorders_; + void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); + + void DetachUnusedLayers(); // Dispose the views in `views_to_dispose_`. void DisposeViews(); + void EnsureOverlayInitialized(int64_t overlay_id, + std::shared_ptr ios_context, + GrContext* gr_context); // This will return true after pre-roll if any of the embedded views // have mutated for last layer tree. @@ -272,20 +215,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from - // the picture on the layer's canvas. - std::shared_ptr GetLayer(GrContext* gr_context, - std::shared_ptr ios_context, - sk_sp picture, - SkRect rect, - int64_t view_id, - int64_t overlay_id); - // Removes overlay views and platform views that aren't needed in the current frame. - void RemoveUnusedLayers(); - // Appends the overlay views and platform view and sets their z index based on the composition - // order. - void BringLayersIntoView(LayersMap layer_map); - 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 21db9530fe0bb..9310fa1803f11 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -20,8 +20,7 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() - : layer_pool_(std::make_unique()){}; +FlutterPlatformViewsController::FlutterPlatformViewsController() = default; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 8cba9285cfb02..58233d8a2f656 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -77,10 +77,7 @@ class IOSSurface : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; + bool SubmitFrame(GrContext* context) override; public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index f51160e94e471..fe85c77f12c2d 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -132,18 +132,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { } // |ExternalViewEmbedder| -bool IOSSurface::SubmitFrame(GrContext* context, SkCanvas* background_canvas) { +bool IOSSurface::SubmitFrame(GrContext* context) { TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame"); FML_CHECK(platform_views_controller_ != nullptr); - bool submitted = - platform_views_controller_->SubmitFrame(std::move(context), ios_context_, background_canvas); - return submitted; -} - -// |ExternalViewEmbedder| -void IOSSurface::FinishFrame() { - TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + bool submitted = platform_views_controller_->SubmitFrame(std::move(context), ios_context_); [CATransaction commit]; + return submitted; } } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 23658888f7caa..5e77073e7ff47 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -129,8 +129,7 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( } // |ExternalViewEmbedder| -bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { +bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -266,7 +265,4 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, return true; } -// |ExternalViewEmbedder| -void EmbedderExternalViewEmbedder::FinishFrame() {} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 63c944a88d7ef..7000d2cde04cd 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -89,10 +89,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; + bool SubmitFrame(GrContext* context) override; // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 818d902b3e2e9..c24333a3a8a7f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; - 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; }; @@ -150,7 +149,6 @@ 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; - 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; 6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = ""; }; 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = ""; }; 6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = ""; }; @@ -247,7 +245,6 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( - 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */, @@ -491,7 +488,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 9bd732f647039..348889b19b856 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -29,13 +29,6 @@ - (BOOL)application:(UIApplication*)application // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", - @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", - @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", - @"--platform-view-partial-intersection" : @"platform_view_partial_intersection", - @"--platform-view-one-overlay-two-intersecting-overlays" : - @"platform_view_one_overlay_two_intersecting_overlays", - @"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays", - @"--platform-view-max-overlays" : @"platform_view_max_overlays", @"--platform-view-multiple" : @"platform_view_multiple", @"--platform-view-multiple-background-foreground" : @"platform_view_multiple_background_foreground", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m deleted file mode 100644 index 02e7eee35f098..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface UnobstructedPlatformViewTests : XCTestCase - -@end - -@implementation UnobstructedPlatformViewTests - -- (void)setUp { - self.continueAfterFailure = NO; -} - -// A is the layer, which z index is higher than the platform view. -// +--------+ -// | PV | +---+ -// +--------+ | A | -// +---+ -- (void)testNoOverlay { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertFalse(overlay.exists); -} - -// A is the layer above the platform view. -// +-----------------+ -// | PV +---+ | -// | | A | | -// | +---+ | -// +-----------------+ -- (void)testOneOverlay { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 150); - XCTAssertEqual(overlay.frame.origin.y, 150); - XCTAssertEqual(overlay.frame.size.width, 50); - XCTAssertEqual(overlay.frame.size.height, 50); -} - -// A is the layer above the platform view. -// +-----------------+ -// | PV +---+ | -// +-----------| A |-+ -// +---+ -- (void)testOneOverlayPartialIntersection { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-partial-intersection" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 200); - XCTAssertEqual(overlay.frame.origin.y, 250); - XCTAssertEqual(overlay.frame.size.width, 50); - // Half the height of the overlay. - XCTAssertEqual(overlay.frame.size.height, 25); -} - -// A and B are the layers above the platform view. -// +--------------------+ -// | PV +------------+ | -// | | B +-----+ | | -// | +---| A |-+ | -// +----------| |---+ -// +-----+ -- (void)testTwoIntersectingOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-two-intersecting-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 150); - XCTAssertEqual(overlay.frame.origin.y, 150); - XCTAssertEqual(overlay.frame.size.width, 75); - XCTAssertEqual(overlay.frame.size.height, 75); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); -} - -// A, B, and C are the layers above the platform view. -// +-------------------------+ -// | PV +-----------+ | -// | +---+ | B +-----+ | | -// | | C | +---| A |-+ | -// | +---+ +-----+ | -// +-------------------------+ -- (void)testOneOverlayAndTwoIntersectingOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-one-overlay-two-intersecting-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay1.exists); - XCTAssertEqual(overlay1.frame.origin.x, 150); - XCTAssertEqual(overlay1.frame.origin.y, 150); - XCTAssertEqual(overlay1.frame.size.width, 75); - XCTAssertEqual(overlay1.frame.size.height, 75); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[0].overlay[1]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 75); - XCTAssertEqual(overlay2.frame.origin.y, 225); - XCTAssertEqual(overlay2.frame.size.width, 50); - XCTAssertEqual(overlay2.frame.size.height, 50); -} - -// A is the layer, which z index is higher than the platform view. -// +--------+ -// | PV | +---+ -// +--------+ | A | -// +--------+ +---+ -// | PV | -// +--------+ -- (void)testMultiplePlatformViewsWithoutOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-multiple-without-overlays" ]; - [app launch]; - - XCUIElement* platform_view1 = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view1.exists); - XCTAssertEqual(platform_view1.frame.origin.x, 25); - XCTAssertEqual(platform_view1.frame.origin.y, 325); - XCTAssertEqual(platform_view1.frame.size.width, 250); - XCTAssertEqual(platform_view1.frame.size.height, 250); - - XCUIElement* platform_view2 = app.textViews[@"platform_view[1]"]; - XCTAssertTrue(platform_view2.exists); - XCTAssertEqual(platform_view2.frame.origin.x, 25); - XCTAssertEqual(platform_view2.frame.origin.y, 25); - XCTAssertEqual(platform_view2.frame.size.width, 250); - XCTAssertEqual(platform_view2.frame.size.height, 250); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); - XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); -} - -// A is the layer above both platform view. -// +------------+ -// | PV +----+ | -// +-----| A |-+ -// +-----| |-+ -// | PV +----+ | -// +------------+ -- (void)testMultiplePlatformViewsWithOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-multiple-background-foreground" ]; - [app launch]; - - XCUIElement* platform_view1 = app.textViews[@"platform_view[8]"]; - XCTAssertTrue(platform_view1.exists); - XCTAssertEqual(platform_view1.frame.origin.x, 25); - XCTAssertEqual(platform_view1.frame.origin.y, 325); - XCTAssertEqual(platform_view1.frame.size.width, 250); - XCTAssertEqual(platform_view1.frame.size.height, 250); - - XCUIElement* platform_view2 = app.textViews[@"platform_view[9]"]; - XCTAssertTrue(platform_view2.exists); - XCTAssertEqual(platform_view2.frame.origin.x, 25); - XCTAssertEqual(platform_view2.frame.origin.y, 25); - XCTAssertEqual(platform_view2.frame.size.width, 250); - XCTAssertEqual(platform_view2.frame.size.height, 250); - - XCUIElement* overlay1 = app.otherElements[@"platform_view[8].overlay[0]"]; - XCTAssertTrue(overlay1.exists); - XCTAssertEqual(overlay1.frame.origin.x, 25); - XCTAssertEqual(overlay1.frame.origin.y, 325); - XCTAssertEqual(overlay1.frame.size.width, 225); - XCTAssertEqual(overlay1.frame.size.height, 175); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[9].overlay[0]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 25); - XCTAssertEqual(overlay2.frame.origin.y, 25); - XCTAssertEqual(overlay2.frame.size.width, 225); - XCTAssertEqual(overlay2.frame.size.height, 250); -} - -// More then two overlays are merged into a single layer. -// +---------------------+ -// | +---+ +---+ +---+ | -// | | A | | B | | C | | -// | +---+ +---+ +---+ | -// | +-------+ | -// +-| D |-----------+ -// +-------+ -- (void)testPlatformViewsMaxOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-max-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 75); - XCTAssertEqual(overlay.frame.origin.y, 85); - XCTAssertEqual(overlay.frame.size.width, 150); - XCTAssertEqual(overlay.frame.size.height, 190); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png index 9ec19ab474f03b6cc7786cb038ee3b3e6d0c51a4..a193faeb040223e56343903c777f9181edb5bcc0 100644 GIT binary patch literal 30022 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ΠAr29j`n?8;-QZE z4jSFDu$|OBK+1@&kY%gDyh4f+et*UW!xwP*gQNm<3K_Yi(Xv2`G0u{G@CDL_`Za|R z0&ba{A^qz!h73(?gBQ zaOaF%@BB*^QHtUx^a&4#swelbeQ@;feaTS45;*zevVw|@B_*Du_fg$vTTApLhT z;(~N8NauodE=cEg)wx}DZdaZ22H-9H%#@2te_y_G)xmx1@#nP+_vs7wCD1Qx;69A{ z-@X&;4$L>JfxjakBUrV-?pPC7bYAj`F8b4z{e%L^L5)Ap&Ej}pv9OANp0FJk6lCu2 z`qwMCQ-p#C$T2zwwupZl2?j`8#a1jqb& z0=E@=#V?uu=M`PWHYtj}0{Y_#&Wfu+&iGHHKM&yc@?V(~vi|c9FG*CCezoXdPx+n% zIcrwd|B-1q@2k?|%s;R2x|fU6rSJXgF(6k7@Me>%1g;WLALUvCG}*aI;3|QWvbdJO zwFIsuaDJV@wFIsua4msbJ8%mq_&GOM30x(hZ4Is^K;6hy0#^x~-^Fq*;eTWaOX`;t XeZBM9{0{PeD?M`X*ny1wR>A)dG2_6W literal 20295 zcmeHuX*iVa8@HyJnz3Yzr3E$ieMzzvCQH_kA+oDbk8B}3O<5*oDSL~pkUjg(q>u%s=030UI)CT+JD2M|+}G1lqoY1XO+`gTr=hN* zPelcrrJ_PeAz|PX)smMN!DXMTzM2wMUK`sa_>ZTJv4-u13si#O9!Z7RhoRaJJp%qx z?K?(=pxjeYY3^hF_g;S=|DR{Tnjtr+;D4Sm0axfxB=`Z+f3C1(*xx5$v&sAa-Xo-- zN15f^xWEPFtbW;*ii(8?`q`(U&pi)5WV@lFYXGj$#-Yno@DKm)6}p!srTj#Pg6l&K zm2(E3`=@BVmW3Qoo zEAE=V()MiS$ENStBiA{d&TI4$Uu9`u;g8Qjx^L@!$Qxhsl75%>84gx2Zkt9p zRMxIl)orxYi*C=;2XrpKI37oy58lCFSM-=&@xF=oTnrW3#;5$UiQAH?R`gyXGY?cu zrq>A6T!x{2ge|-jD(&p3OzZK`!Cx68%Q=PKgb{}sqw2|M$ zql9LA7VneSbIERwo4HO~Pv=4E+IARkA+J5RTrU5ixIMDd-Gj|3_n70e{kVRPqm%YbIrEf=)%WS0xw@4K>=zfOA>tKgx*wpiLK}#s8`F)Z8tF7a!t7z7<4>CBITKz z!{_m5FWzUiD$~o$dd}A|mHK|YyyH4I(Bw_OI$M<_R7_9vIen|xbfYUnVx`aZZ%(5}4Y;&(4*>&L_jytIj{Ezty%+*$Wvj~__PCB!-R41mQZ@4VB z(*JF1Rc{KP5FV~<_snHf?8gPCb<3b_3%5`07E%0D?E@JS){IU*z3tSj^fc0a9lab$ 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 diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png index a2e9351d385e0b2f7800ec71448a05e2736e196e..793082f8f0f7c4edaa29fd14c5ee5a782d1e69b1 100644 GIT binary patch literal 33727 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%0D$f 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 HeDr?+Nh376 literal 22360 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 mf=d=$vf%&Cf@1cqGyR|38zWqd;cH7-*4ss diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 0ab3f7c5352f1..494d6585aa7fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -19,12 +19,6 @@ import 'src/touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index fcea41a65f7d5..0dde98fd9eebb 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -48,224 +48,6 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } } -/// A simple platform view with overlay that doesn't intersect with the platform view. -class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - finishBuilderByAddingPlatformViewAndPicture( - builder, - 0, - overlayOffset: const Offset(150, 350), - ); - } -} - -/// A simple platform view with an overlay that partially intersects with the platform view. -class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - finishBuilderByAddingPlatformViewAndPicture( - builder, - 0, - overlayOffset: const Offset(150, 250), - ); - } -} - -/// A simple platform view with two overlays that intersect with each other and the platform view. -class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// A simple platform view with one overlay and two overlays that intersect with each other and the platform view. -class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// Two platform views without an overlay intersecting either platform view. -class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - builder.pushOffset(0, 600); - _addPlatformViewtoScene(builder, 0, 500, 500); - builder.pop(); - - _addPlatformViewtoScene(builder, 1, 500, 500); - - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTRB(0, 0, 100, 1000), - Paint()..color = const Color(0xFFFF0000), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(580, 0), picture); - - builder.pop(); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// A simple platform view with too many overlays result in a single native view. -class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, -80), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - /// Builds a scene with 2 platform views. class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. @@ -642,17 +424,12 @@ mixin _BasePlatformViewScenarioMixin on Scenario { } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture( - SceneBuilder sceneBuilder, - int viewId, { - Offset overlayOffset, - }) { - overlayOffset ??= const Offset(50, 50); + void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( - overlayOffset, + const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF), );