diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 29fc85ee8c2..7e1fabd3e5c 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -1227,6 +1227,7 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_xform.png FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor.png FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor_with_xform.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/snapshot_large_scene.png FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703.png FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc diff --git a/engine/src/flutter/shell/common/rasterizer.cc b/engine/src/flutter/shell/common/rasterizer.cc index 0c671b8534e..d0f70b63acf 100644 --- a/engine/src/flutter/shell/common/rasterizer.cc +++ b/engine/src/flutter/shell/common/rasterizer.cc @@ -4,6 +4,7 @@ #include "flutter/shell/common/rasterizer.h" +#include #include #include "flutter/common/graphics/persistent_cache.h" @@ -270,13 +271,30 @@ sk_sp Rasterizer::DoMakeRasterSnapshot( return; } + GrRecordingContext* context = surface_->GetContext(); + auto max_size = context->maxRenderTargetSize(); + double scale_factor = std::min( + 1.0, static_cast(max_size) / + static_cast(std::max(image_info.width(), + image_info.height()))); + + // Scale down the render target size to the max supported by the + // GPU if necessary. Exceeding the max would otherwise cause a + // null result. + if (scale_factor < 1.0) { + image_info = image_info.makeWH( + static_cast(image_info.width()) * scale_factor, + static_cast(image_info.height()) * scale_factor); + } + // When there is an on screen surface, we need a render target // SkSurface because we want to access texture backed images. - sk_sp surface = SkSurface::MakeRenderTarget( - surface_->GetContext(), // context - SkBudgeted::kNo, // budgeted - image_info // image info - ); + sk_sp surface = + SkSurface::MakeRenderTarget(context, // context + SkBudgeted::kNo, // budgeted + image_info // image info + ); + surface->getCanvas()->scale(scale_factor, scale_factor); result = DrawSnapshot(surface, draw_callback); })); } diff --git a/engine/src/flutter/shell/platform/embedder/BUILD.gn b/engine/src/flutter/shell/platform/embedder/BUILD.gn index edaa0a57228..484aba2c640 100644 --- a/engine/src/flutter/shell/platform/embedder/BUILD.gn +++ b/engine/src/flutter/shell/platform/embedder/BUILD.gn @@ -164,6 +164,7 @@ test_fixtures("fixtures") { "fixtures/gradient_xform.png", "fixtures/scene_without_custom_compositor.png", "fixtures/scene_without_custom_compositor_with_xform.png", + "fixtures/snapshot_large_scene.png", "fixtures/verifyb143464703.png", "fixtures/verifyb143464703_soft_noxform.png", ] diff --git a/engine/src/flutter/shell/platform/embedder/fixtures/main.dart b/engine/src/flutter/shell/platform/embedder/fixtures/main.dart index f80f5dcf039..6c9290c82a3 100644 --- a/engine/src/flutter/shell/platform/embedder/fixtures/main.dart +++ b/engine/src/flutter/shell/platform/embedder/fixtures/main.dart @@ -879,3 +879,39 @@ void nativeArgumentsCallback(List args) native 'NativeArgumentsCallback' void dart_entrypoint_args(List args) { nativeArgumentsCallback(args); } + +void snapshotsCallback(Image big_image, Image small_image) native 'SnapshotsCallback'; + +@pragma('vm:entry-point') +void snapshot_large_scene(int max_size) async { + // Set width to double the max size, which will result in height being half the max size after scaling. + double width = max_size * 2.0, height = max_size.toDouble(); + + PictureRecorder recorder = PictureRecorder(); + { + Canvas canvas = Canvas(recorder, Rect.fromLTWH(0, 0, width, height)); + Paint paint = Paint(); + // Bottom left + paint.color = Color.fromARGB(255, 100, 255, 100); + canvas.drawRect(Rect.fromLTWH(0, height / 2, width / 2, height / 2), paint); + // Top right + paint.color = Color.fromARGB(255, 100, 100, 255); + canvas.drawRect(Rect.fromLTWH(width / 2, 0, width / 2, height / 2), paint); + } + Picture picture = recorder.endRecording(); + Image big_image = await picture.toImage(width.toInt(), height.toInt()); + + // The max size varies across hardware/drivers, so normalize the result to a smaller target size in + // order to reliably test against an image fixture. + double small_width = 128, small_height = 64; + recorder = PictureRecorder(); + { + Canvas canvas = Canvas(recorder, Rect.fromLTWH(0, 0, small_width, small_height)); + canvas.scale(small_width / big_image.width); + canvas.drawImage(big_image, Offset.zero, Paint()); + } + picture = recorder.endRecording(); + Image small_image = await picture.toImage(small_width.toInt(), small_height.toInt()); + + snapshotsCallback(big_image, small_image); +} diff --git a/engine/src/flutter/shell/platform/embedder/fixtures/snapshot_large_scene.png b/engine/src/flutter/shell/platform/embedder/fixtures/snapshot_large_scene.png new file mode 100644 index 00000000000..7190c22a0f3 Binary files /dev/null and b/engine/src/flutter/shell/platform/embedder/fixtures/snapshot_large_scene.png differ diff --git a/engine/src/flutter/shell/platform/embedder/tests/embedder_unittests_gl.cc b/engine/src/flutter/shell/platform/embedder/tests/embedder_unittests_gl.cc index 95ea662e063..b6744999753 100644 --- a/engine/src/flutter/shell/platform/embedder/tests/embedder_unittests_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/tests/embedder_unittests_gl.cc @@ -18,6 +18,7 @@ #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/thread.h" +#include "flutter/lib/ui/painting/image.h" #include "flutter/runtime/dart_vm.h" #include "flutter/shell/platform/embedder/tests/embedder_assertions.h" #include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" @@ -3399,5 +3400,48 @@ TEST_F(EmbedderTest, CompositorRenderTargetsNotRecycledWhenAvoidsCacheSet) { ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u); } +TEST_F(EmbedderTest, SnapshotRenderTargetScalesDownToDriverMax) { + auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + + auto max_size = context.GetCompositor().GetGrContext()->maxRenderTargetSize(); + + context.AddIsolateCreateCallback([&]() { + Dart_Handle snapshot_large_scene = Dart_GetField( + Dart_RootLibrary(), tonic::ToDart("snapshot_large_scene")); + tonic::DartInvoke(snapshot_large_scene, {tonic::ToDart(max_size)}); + }); + + fml::AutoResetWaitableEvent latch; + context.AddNativeCallback( + "SnapshotsCallback", CREATE_NATIVE_ENTRY(([&](Dart_NativeArguments args) { + auto get_arg = [&args](int index) { + Dart_Handle dart_image = Dart_GetNativeArgument(args, index); + Dart_Handle internal_image = + Dart_GetField(dart_image, tonic::ToDart("_image")); + return tonic::DartConverter::FromDart( + internal_image); + }; + + CanvasImage* big_image = get_arg(0); + ASSERT_EQ(big_image->width(), max_size); + ASSERT_EQ(big_image->height(), max_size / 2); + + CanvasImage* small_image = get_arg(1); + ASSERT_TRUE(ImageMatchesFixture("snapshot_large_scene.png", + small_image->image())); + + latch.Signal(); + }))); + + UniqueEngine engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn b/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn index 6152166211b..354bd958d9c 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn @@ -834,6 +834,10 @@ fuchsia_test_archive("embedder_tests") { path = "$root_gen_dir/flutter/shell/platform/embedder/assets/scene_without_custom_compositor_with_xform.png" dest = "assets/scene_without_custom_compositor_with_xform.png" }, + { + path = "$root_gen_dir/flutter/shell/platform/embedder/assets/snapshot_large_scene.png" + dest = "assets/snapshot_large_scene.png" + }, { path = "$root_gen_dir/flutter/shell/platform/embedder/assets/verifyb143464703.png" dest = "assets/verifyb143464703.png"