From b25cb3e4ada55cf5bc01aa372aa4da7cce2e9716 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 25 Jan 2024 19:24:53 -0800 Subject: [PATCH 1/7] Fix Shell::Screenshot for Impeller --- shell/common/rasterizer.cc | 205 ++++++++++++++---- shell/common/rasterizer.h | 34 ++- shell/common/shell.cc | 12 + .../ios/framework/Source/FlutterView.mm | 62 ++++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 1 + .../FlutterViewControllerTest.m | 54 +++++ testing/scenario_app/lib/src/scenarios.dart | 2 + testing/scenario_app/lib/src/solid_blue.dart | 33 +++ 8 files changed, 341 insertions(+), 62 deletions(-) create mode 100644 testing/scenario_app/lib/src/solid_blue.dart diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index e9a50aa32b70c..d8114edd61504 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -8,6 +8,7 @@ #include #include +#include "display_list/dl_builder.h" #include "flow/frame_timings.h" #include "flutter/common/constants.h" #include "flutter/common/graphics/persistent_cache.h" @@ -17,6 +18,10 @@ #include "flutter/shell/common/base64.h" #include "flutter/shell/common/serialization_callbacks.h" #include "fml/make_copyable.h" +#include "fml/synchronization/waitable_event.h" +#include "impeller/aiks/aiks_context.h" +#include "impeller/core/formats.h" +#include "impeller/display_list/dl_dispatcher.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkImage.h" @@ -802,11 +807,132 @@ static sk_sp ScreenshotLayerTreeAsPicture( return recorder.finishRecordingAsPicture()->serialize(&procs); } -sk_sp Rasterizer::ScreenshotLayerTreeAsImage( - flutter::LayerTree* tree, +static void RenderFrameForScreenshot( flutter::CompositorContext& compositor_context, + DlCanvas* canvas, + flutter::LayerTree* tree, GrDirectContext* surface_context, + const std::shared_ptr& aiks_context) { + // There is no root surface transformation for the screenshot layer. Reset + // the matrix to identity. + SkMatrix root_surface_transformation; + root_surface_transformation.reset(); + + auto frame = compositor_context.AcquireFrame( + surface_context, // skia context + canvas, // canvas + nullptr, // view embedder + root_surface_transformation, // root surface transformation + false, // instrumentation enabled + true, // render buffer readback supported + nullptr, // thread merger + aiks_context.get() // aiks context + ); + canvas->Clear(DlColor::kTransparent()); + frame->Raster(*tree, true, nullptr); + canvas->Flush(); +} + +#if IMPELLER_SUPPORTS_RENDERING +Rasterizer::ScreenshotFormat ToScreenshotFormat(impeller::PixelFormat format) { + switch (format) { + case impeller::PixelFormat::kUnknown: + case impeller::PixelFormat::kA8UNormInt: + case impeller::PixelFormat::kR8UNormInt: + case impeller::PixelFormat::kR8G8UNormInt: + case impeller::PixelFormat::kR8G8B8A8UNormIntSRGB: + case impeller::PixelFormat::kB8G8R8A8UNormIntSRGB: + case impeller::PixelFormat::kB10G10R10XRSRGB: + case impeller::PixelFormat::kS8UInt: + case impeller::PixelFormat::kD24UnormS8Uint: + case impeller::PixelFormat::kD32FloatS8UInt: + case impeller::PixelFormat::kR32G32B32A32Float: + case impeller::PixelFormat::kB10G10R10XR: + case impeller::PixelFormat::kB10G10R10A10XR: + FML_DCHECK(false); + return Rasterizer::ScreenshotFormat::kUnknown; + case impeller::PixelFormat::kR8G8B8A8UNormInt: + return Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt; + case impeller::PixelFormat::kB8G8R8A8UNormInt: + return Rasterizer::ScreenshotFormat::kB8G8R8A8UNormInt; + case impeller::PixelFormat::kR16G16B16A16Float: + return Rasterizer::ScreenshotFormat::kR16G16B16A16Float; + } +} + +static std::pair, Rasterizer::ScreenshotFormat> +ScreenshotLayerTreeAsImageImpeller( + const std::shared_ptr& aiks_context, + flutter::LayerTree* tree, + flutter::CompositorContext& compositor_context, + bool compressed) { + if (compressed) { + FML_LOG(ERROR) << "Compressed screenshots not supported for Impeller"; + return {nullptr, Rasterizer::ScreenshotFormat::kUnknown}; + } + + DisplayListBuilder builder(SkRect::MakeSize( + SkSize::Make(tree->frame_size().fWidth, tree->frame_size().fHeight))); + + RenderFrameForScreenshot(compositor_context, &builder, tree, nullptr, + aiks_context); + + impeller::DlDispatcher dispatcher; + builder.Build()->Dispatch(dispatcher); + const auto& picture = dispatcher.EndRecordingAsPicture(); + const auto& image = picture.ToImage( + *aiks_context, + impeller::ISize(tree->frame_size().fWidth, tree->frame_size().fHeight)); + const auto& texture = image->GetTexture(); + impeller::DeviceBufferDescriptor buffer_desc; + buffer_desc.storage_mode = impeller::StorageMode::kHostVisible; + buffer_desc.size = + texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel(); + auto impeller_context = aiks_context->GetContext(); + auto buffer = + impeller_context->GetResourceAllocator()->CreateBuffer(buffer_desc); + auto command_buffer = impeller_context->CreateCommandBuffer(); + command_buffer->SetLabel("BlitTextureToBuffer Command Buffer"); + auto pass = command_buffer->CreateBlitPass(); + pass->AddCopy(texture, buffer); + pass->EncodeCommands(impeller_context->GetResourceAllocator()); + fml::AutoResetWaitableEvent latch; + sk_sp sk_data; + auto completion = [buffer, &buffer_desc, &sk_data, + &latch](impeller::CommandBuffer::Status status) { + if (status != impeller::CommandBuffer::Status::kCompleted) { + FML_LOG(ERROR) << "Failed to complete blit pass."; + latch.Signal(); + return; + } + auto buffer_view = impeller::DeviceBuffer::AsBufferView(buffer); + sk_data = SkData::MakeWithCopy(buffer->OnGetContents(), buffer_desc.size); + + latch.Signal(); + }; + + if (!command_buffer->SubmitCommands(completion)) { + FML_LOG(ERROR) << "Failed to submit commands."; + } + latch.Wait(); + return std::make_pair( + sk_data, ToScreenshotFormat(texture->GetTextureDescriptor().format)); +} +#endif + +std::pair, Rasterizer::ScreenshotFormat> +Rasterizer::ScreenshotLayerTreeAsImage( + flutter::LayerTree* tree, + flutter::CompositorContext& compositor_context, bool compressed) { +#if IMPELLER_SUPPORTS_RENDERING + if (delegate_.GetSettings().enable_impeller) { + return ScreenshotLayerTreeAsImageImpeller(GetAiksContext(), tree, + compositor_context, compressed); + } +#endif // IMPELLER_SUPPORTS_RENDERING + + GrDirectContext* surface_context = GetGrContext(); // Attempt to create a snapshot surface depending on whether we have access // to a valid GPU rendering context. std::unique_ptr snapshot_surface = @@ -814,16 +940,11 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( if (!snapshot_surface->IsValid()) { FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface"; - return nullptr; + return {nullptr, ScreenshotFormat::kUnknown}; } // Draw the current layer tree into the snapshot surface. - auto* canvas = snapshot_surface->GetCanvas(); - - // There is no root surface transformation for the screenshot layer. Reset - // the matrix to identity. - SkMatrix root_surface_transformation; - root_surface_transformation.reset(); + DlCanvas* canvas = snapshot_surface->GetCanvas(); // snapshot_surface->makeImageSnapshot needs the GL context to be set if the // render context is GL. frame->Raster() pops the gl context in platforms @@ -832,29 +953,26 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( auto context_switch = surface_->MakeRenderContextCurrent(); if (!context_switch->GetResult()) { FML_LOG(ERROR) << "Screenshot: unable to make image screenshot"; - return nullptr; + return {nullptr, ScreenshotFormat::kUnknown}; } - auto frame = compositor_context.AcquireFrame( - surface_context, // skia context - canvas, // canvas - nullptr, // view embedder - root_surface_transformation, // root surface transformation - false, // instrumentation enabled - true, // render buffer readback supported - nullptr, // thread merger - nullptr // aiks context - ); - canvas->Clear(DlColor::kTransparent()); - frame->Raster(*tree, true, nullptr); - canvas->Flush(); + RenderFrameForScreenshot(compositor_context, canvas, tree, surface_context, + nullptr); - return snapshot_surface->GetRasterData(compressed); + return std::make_pair(snapshot_surface->GetRasterData(compressed), + ScreenshotFormat::kUnknown); } Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { + if (delegate_.GetSettings().enable_impeller && + type == ScreenshotType::SkiaPicture) { + FML_DCHECK(false); + FML_LOG(ERROR) << "Last layer tree cannot be screenshotted as a " + "SkiaPicture when using Impeller."; + return {}; + } // TODO(dkwingsmt): Support screenshotting all last layer trees // when the shell protocol supports multi-views. // https://github.com/flutter/flutter/issues/135534 @@ -865,48 +983,49 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( return {}; } - sk_sp data = nullptr; + std::pair, ScreenshotFormat> data{nullptr, + ScreenshotFormat::kUnknown}; std::string format; - GrDirectContext* surface_context = - surface_ ? surface_->GetContext() : nullptr; - switch (type) { case ScreenshotType::SkiaPicture: format = "ScreenshotType::SkiaPicture"; - data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_); + data.first = + ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_); break; case ScreenshotType::UncompressedImage: format = "ScreenshotType::UncompressedImage"; - data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_, - surface_context, false); + data = + ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_, false); break; case ScreenshotType::CompressedImage: format = "ScreenshotType::CompressedImage"; - data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_, - surface_context, true); + data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_, true); break; case ScreenshotType::SurfaceData: { Surface::SurfaceData surface_data = surface_->GetSurfaceData(); format = surface_data.pixel_format; - data = surface_data.data; + data.first = surface_data.data; break; } } - if (data == nullptr) { + if (data.first == nullptr) { FML_LOG(ERROR) << "Screenshot data was null."; return {}; } if (base64_encode) { - size_t b64_size = Base64::EncodedSize(data->size()); + size_t b64_size = Base64::EncodedSize(data.first->size()); auto b64_data = SkData::MakeUninitialized(b64_size); - Base64::Encode(data->data(), data->size(), b64_data->writable_data()); - return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format}; + Base64::Encode(data.first->data(), data.first->size(), + b64_data->writable_data()); + return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format, + data.second}; } - return Rasterizer::Screenshot{data, layer_tree->frame_size(), format}; + return Rasterizer::Screenshot{data.first, layer_tree->frame_size(), format, + data.second}; } void Rasterizer::SetNextFrameCallback(const fml::closure& callback) { @@ -977,8 +1096,12 @@ Rasterizer::Screenshot::Screenshot() {} Rasterizer::Screenshot::Screenshot(sk_sp p_data, SkISize p_size, - const std::string& p_format) - : data(std::move(p_data)), frame_size(p_size), format(p_format) {} + const std::string& p_format, + ScreenshotFormat p_pixel_format) + : data(std::move(p_data)), + frame_size(p_size), + format(p_format), + pixel_format(p_pixel_format) {} Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default; diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 6c49c15aba17e..721eed3e61dee 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -25,8 +25,8 @@ #include "flutter/fml/time/time_delta.h" #include "flutter/fml/time/time_point.h" #if IMPELLER_SUPPORTS_RENDERING -// GN is having trouble understanding how this works in the Fuchsia builds. #include "impeller/aiks/aiks_context.h" // nogncheck +#include "impeller/core/formats.h" // nogncheck #include "impeller/renderer/context.h" // nogncheck #include "impeller/typographer/backends/skia/typographer_context_skia.h" // nogncheck #endif // IMPELLER_SUPPORTS_RENDERING @@ -44,6 +44,7 @@ namespace impeller { class Context; class AiksContext; +enum class PixelFormat { kUnknown }; } // namespace impeller #endif // !IMPELLER_SUPPORTS_RENDERING @@ -357,9 +358,10 @@ class Rasterizer final : public SnapshotDelegate, SkiaPicture, //-------------------------------------------------------------------------- - /// A format used to denote uncompressed image data. This format + /// A format used to denote uncompressed image data. For Skia, this format /// is 32 bits per pixel, 8 bits per component and - /// denoted by the `kN32_SkColorType ` Skia color type. + /// denoted by the `kN32_SkColorType ` Skia color type. For Impeller, its + /// format is specified in Screenshot::pixel_format. /// UncompressedImage, @@ -377,6 +379,18 @@ class Rasterizer final : public SnapshotDelegate, // NOLINTEND(readability-identifier-naming) }; + // Specifies the format of pixel data in a Screenshot. + enum class ScreenshotFormat { + // Unknown format, or Skia default. + kUnknown, + // RGBA 8 bits per channel. + kR8G8B8A8UNormInt, + // BGRA 8 bits per channel. + kB8G8R8A8UNormInt, + // RGBA 16 bit floating point per channel. + kR16G16B16A16Float, + }; + //---------------------------------------------------------------------------- /// @brief A POD type used to return the screenshot data along with the /// size of the frame. @@ -400,6 +414,13 @@ class Rasterizer final : public SnapshotDelegate, /// std::string format; + //-------------------------------------------------------------------------- + /// The pixel format of the data in `data`. + /// + /// If the impeller backend is not used, this value is always kUnknown and + /// the data is in RGBA8888 format. + ScreenshotFormat pixel_format = ScreenshotFormat::kUnknown; + //-------------------------------------------------------------------------- /// @brief Creates an empty screenshot /// @@ -411,10 +432,12 @@ class Rasterizer final : public SnapshotDelegate, /// @param[in] p_data The screenshot data /// @param[in] p_size The screenshot size. /// @param[in] p_format The screenshot format. + /// @param[in] p_pixel_format The screenshot format. /// Screenshot(sk_sp p_data, SkISize p_size, - const std::string& p_format); + const std::string& p_format, + ScreenshotFormat p_pixel_format); //-------------------------------------------------------------------------- /// @brief The copy constructor for a screenshot. @@ -663,10 +686,9 @@ class Rasterizer final : public SnapshotDelegate, return delegate_.GetIsGpuDisabledSyncSwitch(); } - sk_sp ScreenshotLayerTreeAsImage( + std::pair, ScreenshotFormat> ScreenshotLayerTreeAsImage( flutter::LayerTree* tree, flutter::CompositorContext& compositor_context, - GrDirectContext* surface_context, bool compressed); // This method starts with the frame timing recorder at build end. This diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 354881d26fe07..ac50842915b34 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -2144,6 +2144,18 @@ void Shell::RemoveView(int64_t view_id) { Rasterizer::Screenshot Shell::Screenshot( Rasterizer::ScreenshotType screenshot_type, bool base64_encode) { + if (settings_.enable_impeller) { + switch (screenshot_type) { + case Rasterizer::ScreenshotType::SkiaPicture: + FML_LOG(ERROR) + << "Impeller backend cannot produce ScreenshotType::SkiaPicture."; + return {}; + case Rasterizer::ScreenshotType::UncompressedImage: + case Rasterizer::ScreenshotType::CompressedImage: + case Rasterizer::ScreenshotType::SurfaceData: + break; + } + } TRACE_EVENT0("flutter", "Shell::Screenshot"); fml::AutoResetWaitableEvent latch; Rasterizer::Screenshot screenshot; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index d1af6ebf5beb1..337dc7d277f46 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -172,27 +172,59 @@ - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { fml::CFRef colorspace(CGColorSpaceCreateDeviceRGB()); + // Defaults for RGBA8888. + size_t bits_per_component = 8u; + size_t bits_per_pixel = 32u; + size_t bytes_per_row_multiplier = 4u; + CGBitmapInfo bitmap_info = + static_cast(static_cast(kCGImageAlphaPremultipliedLast) | + static_cast(kCGBitmapByteOrder32Big)); + + switch (screenshot.pixel_format) { + case flutter::Rasterizer::ScreenshotFormat::kUnknown: + case flutter::Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt: + // Assume unknown is Skia and is RGBA8888. Keep defaults. + break; + case flutter::Rasterizer::ScreenshotFormat::kB8G8R8A8UNormInt: + // Treat this as little endian with the alpha first so that it's read backwards. + bitmap_info = + static_cast(static_cast(kCGImageAlphaPremultipliedFirst) | + static_cast(kCGBitmapByteOrder32Little)); + break; + case flutter::Rasterizer::ScreenshotFormat::kR16G16B16A16Float: + bits_per_component = 16u; + bits_per_pixel = 64u; + bytes_per_row_multiplier = 8u; + bitmap_info = + static_cast(static_cast(kCGImageAlphaPremultipliedLast) | + static_cast(kCGBitmapFloatComponents) | + static_cast(kCGBitmapByteOrder16Little)); + break; + } + fml::CFRef image(CGImageCreate( - screenshot.frame_size.width(), // size_t width - screenshot.frame_size.height(), // size_t height - 8, // size_t bitsPerComponent - 32, // size_t bitsPerPixel, - 4 * screenshot.frame_size.width(), // size_t bytesPerRow - colorspace, // CGColorSpaceRef space - static_cast( - static_cast(kCGImageAlphaPremultipliedLast) | - static_cast(kCGBitmapByteOrder32Big)), // CGBitmapInfo bitmapInfo - image_data_provider, // CGDataProviderRef provider - nullptr, // const CGFloat* decode - false, // bool shouldInterpolate - kCGRenderingIntentDefault // CGColorRenderingIntent intent + screenshot.frame_size.width(), // size_t width + screenshot.frame_size.height(), // size_t height + bits_per_component, // size_t bitsPerComponent + bits_per_pixel, // size_t bitsPerPixel, + bytes_per_row_multiplier * screenshot.frame_size.width(), // size_t bytesPerRow + colorspace, // CGColorSpaceRef space + bitmap_info, // CGBitmapInfo bitmapInfo + image_data_provider, // CGDataProviderRef provider + nullptr, // const CGFloat* decode + false, // bool shouldInterpolate + kCGRenderingIntentDefault // CGColorRenderingIntent intent )); const CGRect frame_rect = CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height()); - CGContextSaveGState(context); - CGContextTranslateCTM(context, 0.0, CGBitmapContextGetHeight(context)); + // If the CGContext is not a bitmap based context, this returns zero. + CGFloat height = CGBitmapContextGetHeight(context); + if (height == 0) { + height = CGFloat(screenshot.frame_size.height()); + } + CGContextTranslateCTM(context, 0.0, height); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, frame_rect, image); CGContextRestoreGState(context); diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 042fb8c6c49ad..324ee3aa8fffc 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -80,6 +80,7 @@ - (BOOL)application:(UIApplication*)application @"platform_view_gesture_accept_with_overlapping_platform_views", @"--tap-status-bar" : @"tap_status_bar", @"--animated-color-square" : @"animated_color_square", + @"--solid-blue" : @"solid_blue", @"--platform-view-with-continuous-texture" : @"platform_view_with_continuous_texture", @"--bogus-font-text" : @"bogus_font_text", @"--spawn-engine-works" : @"spawn_engine_works", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterViewControllerTest.m b/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterViewControllerTest.m index bc31baa05ab0a..1f88ece57e2ae 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterViewControllerTest.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterViewControllerTest.m @@ -60,4 +60,58 @@ - (void)testFirstFrameCallback { [self waitForExpectationsWithTimeout:30.0 handler:nil]; } +- (void)testDrawLayer { + XCTestExpectation* firstFrameRendered = [self expectationWithDescription:@"firstFrameRendered"]; + XCTestExpectation* imageRendered = [self expectationWithDescription:@"imageRendered"]; + + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil]; + [engine runWithEntrypoint:nil]; + [engine.binaryMessenger + setMessageHandlerOnChannel:@"waiting_for_status" + binaryMessageHandler:^(NSData* _Nullable message, FlutterBinaryReply _Nonnull reply) { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"driver" + binaryMessenger:engine.binaryMessenger + codec:[FlutterJSONMethodCodec sharedInstance]]; + [channel invokeMethod:@"set_scenario" arguments:@{@"name" : @"solid_blue"}]; + }]; + + self.flutterViewController = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + + XCTAssertFalse(self.flutterViewController.isDisplayingFlutterUI); + + [self.flutterViewController setFlutterViewDidRenderCallback:^{ + [firstFrameRendered fulfill]; + }]; + + AppDelegate* appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate; + UIViewController* rootVC = appDelegate.window.rootViewController; + [rootVC presentViewController:self.flutterViewController animated:NO completion:nil]; + + CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB(); + + __block dispatch_block_t callback; + callback = ^{ + size_t width = 300u; + CGContextRef context = + CGBitmapContextCreate(nil, width, width, 8, 4 * width, color_space, + kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + [appDelegate.window.layer renderInContext:context]; + uint32_t* image_data = (uint32_t*)CGBitmapContextGetData(context); + if (image_data[20] == 0xFF0000FF) { + [imageRendered fulfill]; + return; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), + callback); + }; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), + callback); + + [self waitForExpectationsWithTimeout:30.0 handler:nil]; +} + @end diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 87f1f6cbba952..bd09ed21efb0f 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -14,6 +14,7 @@ import 'locale_initialization.dart'; import 'platform_view.dart'; import 'poppable_screen.dart'; import 'scenario.dart'; +import 'solid_blue.dart'; import 'texture.dart'; import 'touches_scenario.dart'; @@ -23,6 +24,7 @@ int _viewId = 0; Map _scenarios = { 'animated_color_square': (FlutterView view) => AnimatedColorSquareScenario(view), + 'solid_blue': (FlutterView view) => SolidBlueScenario(view), 'locale_initialization': (FlutterView view) => LocaleInitialization(view), 'platform_view': (FlutterView view) => PlatformViewScenario(view, id: _viewId++), 'platform_view_no_overlay_intersection': (FlutterView view) => PlatformViewNoOverlayIntersectionScenario(view, id: _viewId++), diff --git a/testing/scenario_app/lib/src/solid_blue.dart b/testing/scenario_app/lib/src/solid_blue.dart new file mode 100644 index 0000000000000..d8074d6c25292 --- /dev/null +++ b/testing/scenario_app/lib/src/solid_blue.dart @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; +import 'dart:ui'; + +import 'scenario.dart'; + +/// Fills the screen with a solid blue color. +class SolidBlueScenario extends Scenario { + /// Creates the AnimatedColorSquare scenario. + SolidBlueScenario(super.view); + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + + canvas.drawPaint(Paint()..color = const Color(0xFF0000FF)); + final Picture picture = recorder.endRecording(); + + builder.addPicture( + Offset.zero, + picture, + willChangeHint: true, + ); + final Scene scene = builder.build(); + view.render(scene); + scene.dispose(); + } +} From 5e537fcd0c8d29efd5a7b80a0ad1214d0120b4a7 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 25 Jan 2024 19:51:01 -0800 Subject: [PATCH 2/7] build fixes --- shell/common/rasterizer.cc | 9 ++++++--- shell/platform/android/android_shell_holder.cc | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d8114edd61504..5c0052d58ae6c 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -19,9 +19,6 @@ #include "flutter/shell/common/serialization_callbacks.h" #include "fml/make_copyable.h" #include "fml/synchronization/waitable_event.h" -#include "impeller/aiks/aiks_context.h" -#include "impeller/core/formats.h" -#include "impeller/display_list/dl_dispatcher.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkImage.h" @@ -39,6 +36,12 @@ #include "third_party/skia/include/gpu/GrTypes.h" #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" +#if IMPELLER_SUPPORTS_RENDERING +#include "impeller/aiks/aiks_context.h" // nogncheck +#include "impeller/core/formats.h" // nogncheck +#include "impeller/display_list/dl_dispatcher.h" // nogncheck +#endif + namespace flutter { // The rasterizer will tell Skia to purge cached resources that have not been diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 8d2f349e3d87f..334db9ba96b6f 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -298,7 +298,8 @@ Rasterizer::Screenshot AndroidShellHolder::Screenshot( if (!IsValid()) { return {nullptr, SkISize::MakeEmpty(), ""}; } - return shell_->Screenshot(type, base64_encode); + return {shell_->Screenshot(type, base64_encode), + Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt}; } fml::WeakPtr AndroidShellHolder::GetPlatformView() { From 86c6b8376875bb0c3b79072b6fc79dda2b369117 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 25 Jan 2024 19:54:18 -0800 Subject: [PATCH 3/7] Update solid_blue.dart --- testing/scenario_app/lib/src/solid_blue.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/scenario_app/lib/src/solid_blue.dart b/testing/scenario_app/lib/src/solid_blue.dart index d8074d6c25292..009727233841c 100644 --- a/testing/scenario_app/lib/src/solid_blue.dart +++ b/testing/scenario_app/lib/src/solid_blue.dart @@ -9,7 +9,7 @@ import 'scenario.dart'; /// Fills the screen with a solid blue color. class SolidBlueScenario extends Scenario { - /// Creates the AnimatedColorSquare scenario. + /// Creates the SolidBlue scenario. SolidBlueScenario(super.view); @override From 8eb4a20a430a79a65f07ec7a44367707ff010333 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 25 Jan 2024 19:55:07 -0800 Subject: [PATCH 4/7] unused code --- shell/common/rasterizer.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 5c0052d58ae6c..899380d6f8795 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -908,7 +908,6 @@ ScreenshotLayerTreeAsImageImpeller( latch.Signal(); return; } - auto buffer_view = impeller::DeviceBuffer::AsBufferView(buffer); sk_data = SkData::MakeWithCopy(buffer->OnGetContents(), buffer_desc.size); latch.Signal(); From ef93ac30b06089d30e128d477c7bfbe181c608f3 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 25 Jan 2024 21:37:06 -0800 Subject: [PATCH 5/7] fixes --- shell/common/rasterizer.h | 2 +- shell/platform/android/android_shell_holder.cc | 3 ++- testing/scenario_app/lib/src/solid_blue.dart | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 721eed3e61dee..9c97d49c10659 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -44,7 +44,7 @@ namespace impeller { class Context; class AiksContext; -enum class PixelFormat { kUnknown }; +enum class PixelFormat : uint8_t { kUnknown }; } // namespace impeller #endif // !IMPELLER_SUPPORTS_RENDERING diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 334db9ba96b6f..1626f5a0384bb 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -296,7 +296,8 @@ Rasterizer::Screenshot AndroidShellHolder::Screenshot( Rasterizer::ScreenshotType type, bool base64_encode) { if (!IsValid()) { - return {nullptr, SkISize::MakeEmpty(), ""}; + return {nullptr, SkISize::MakeEmpty(), "", + Rasterizer::ScreenshotFormat::kUnknown}; } return {shell_->Screenshot(type, base64_encode), Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt}; diff --git a/testing/scenario_app/lib/src/solid_blue.dart b/testing/scenario_app/lib/src/solid_blue.dart index 009727233841c..ac94311ebcd3c 100644 --- a/testing/scenario_app/lib/src/solid_blue.dart +++ b/testing/scenario_app/lib/src/solid_blue.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:math' as math; import 'dart:ui'; import 'scenario.dart'; From 8705e8b9f697bd21055d8f7bbbe381bf5bcb0c5f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 26 Jan 2024 08:19:04 -0800 Subject: [PATCH 6/7] wrong line... --- shell/platform/android/android_shell_holder.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 1626f5a0384bb..d22cac6452a24 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -299,8 +299,7 @@ Rasterizer::Screenshot AndroidShellHolder::Screenshot( return {nullptr, SkISize::MakeEmpty(), "", Rasterizer::ScreenshotFormat::kUnknown}; } - return {shell_->Screenshot(type, base64_encode), - Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt}; + return shell_->Screenshot(type, base64_encode); } fml::WeakPtr AndroidShellHolder::GetPlatformView() { From bc58bcb0ac0308b4c8fcb8aad2ca79d5231df7af Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 26 Jan 2024 08:20:27 -0800 Subject: [PATCH 7/7] unnecessary forward decl --- shell/common/rasterizer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 9c97d49c10659..04620957ed383 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -44,7 +44,6 @@ namespace impeller { class Context; class AiksContext; -enum class PixelFormat : uint8_t { kUnknown }; } // namespace impeller #endif // !IMPELLER_SUPPORTS_RENDERING