diff --git a/engine/src/flutter/flow/compositor_context.cc b/engine/src/flutter/flow/compositor_context.cc index 779a42c84eb..5047e4d3ecc 100644 --- a/engine/src/flutter/flow/compositor_context.cc +++ b/engine/src/flutter/flow/compositor_context.cc @@ -12,6 +12,7 @@ namespace flutter { std::optional FrameDamage::ComputeClipRect( flutter::LayerTree& layer_tree) { + TRACE_EVENT0("flutter", "FrameDamage::ComputeClipRect"); if (layer_tree.root_layer()) { PaintRegionMap empty_paint_region_map; DiffContext context(layer_tree.frame_size(), @@ -118,6 +119,10 @@ RasterStatus CompositorContext::ScopedFrame::Raster( std::optional clip_rect = frame_damage ? frame_damage->ComputeClipRect(layer_tree) : std::nullopt; + if (frame_damage && frame_damage->GetFrameDamage() && + frame_damage->GetFrameDamage()->isEmpty()) { + return RasterStatus::kDiscarded; + } bool root_needs_readback = layer_tree.Preroll( *this, ignore_raster_cache, clip_rect ? *clip_rect : kGiantRect); bool needs_save_layer = root_needs_readback && !surface_supports_readback(); diff --git a/engine/src/flutter/shell/common/BUILD.gn b/engine/src/flutter/shell/common/BUILD.gn index a1c1fed6205..7a516eb57d3 100644 --- a/engine/src/flutter/shell/common/BUILD.gn +++ b/engine/src/flutter/shell/common/BUILD.gn @@ -287,6 +287,7 @@ if (enable_unittests) { "//flutter/shell/profiling:profiling_unittests", "//flutter/shell/version", "//flutter/testing:fixture_test", + "//flutter/testing:skia", "//third_party/googletest:gmock", ] diff --git a/engine/src/flutter/shell/common/rasterizer.cc b/engine/src/flutter/shell/common/rasterizer.cc index 36fff4a281e..6cfd45f3061 100644 --- a/engine/src/flutter/shell/common/rasterizer.cc +++ b/engine/src/flutter/shell/common/rasterizer.cc @@ -601,7 +601,8 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( damage.get() // frame damage ); if (raster_status == RasterStatus::kFailed || - raster_status == RasterStatus::kSkipAndRetry) { + raster_status == RasterStatus::kSkipAndRetry || + raster_status == RasterStatus::kDiscarded) { return raster_status; } diff --git a/engine/src/flutter/shell/common/rasterizer_unittests.cc b/engine/src/flutter/shell/common/rasterizer_unittests.cc index b626bd386bf..42b079fcdaf 100644 --- a/engine/src/flutter/shell/common/rasterizer_unittests.cc +++ b/engine/src/flutter/shell/common/rasterizer_unittests.cc @@ -13,6 +13,9 @@ #include "flutter/shell/common/thread_host.h" #include "flutter/testing/testing.h" +#include "flow/layers/container_layer.h" +#include "flow/layers/physical_shape_layer.h" +#include "flutter/testing/mock_canvas.h" #include "gmock/gmock.h" using testing::_; @@ -726,4 +729,138 @@ TEST( latch.Wait(); } +TEST(RasterizerTest, drawWithFrameDamageIsEmpty) { + std::string test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + ThreadHost thread_host("io.flutter.test." + test_name + ".", + ThreadHost::Type::Platform | ThreadHost::Type::RASTER | + ThreadHost::Type::IO | ThreadHost::Type::UI); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + fml::AutoResetWaitableEvent latch; + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { + CompositorContext compositor_context; + flutter::testing::MockCanvas canvas; + auto compositor_frame = compositor_context.AcquireFrame( + nullptr, &canvas, nullptr, SkMatrix(), false, true, nullptr, nullptr); + auto last_layer_tree = + std::make_unique(/*frame_size=*/SkISize::Make(100, 100), + /*device_pixel_ratio=*/2.0f); + // Use container as the root layer and PhysicalShapeLayer as the child + // layer. Frame size is 100 x 100 PhysicalShapeLayer's size is 50 x 50 The + // frame_damage is 50,50 in the first frame, and the frame_damage is empty + // in the second frame + auto last_root_layer = std::make_shared(); + auto last_child = std::make_shared( + SK_ColorBLACK, SK_ColorBLACK, + 0.0f, // elevation + SkPath::Rect(SkRect::MakeIWH(50, 50), SkPathDirection::kCCW, 0), + Clip::none); + last_root_layer.get()->Add(last_child); + last_layer_tree->set_root_layer(last_root_layer); + + auto layer_tree = std::make_unique( + /*frame_size=*/SkISize::Make(100, 100), /*device_pixel_ratio=*/2.0f); + auto cur_root_layer = std::make_shared(); + auto cur_child = std::make_shared( + SK_ColorBLACK, SK_ColorBLACK, + 0.0f, // elevation + SkPath::Rect(SkRect::MakeIWH(50, 50), SkPathDirection::kCCW, 0), + Clip::none); + cur_child.get()->AssignOldLayer(last_child.get()); + cur_root_layer.get()->Add(cur_child); + layer_tree->set_root_layer(cur_root_layer); + + // Draw Last Frame + std::unique_ptr last_damage = std::make_unique(); + RasterStatus last_raster_status = + compositor_frame->Raster(*last_layer_tree.get(), // layer tree + false, // ignore raster cache + last_damage.get() // frame damage + ); + EXPECT_EQ(last_raster_status, RasterStatus::kSuccess); + + // Draw Current Frame + std::unique_ptr cur_damage = std::make_unique(); + cur_damage->SetPreviousLayerTree(last_layer_tree.get()); + cur_damage->AddAdditonalDamage(SkIRect()); + RasterStatus cur_raster_status = + compositor_frame->Raster(*layer_tree.get(), // layer tree + false, // ignore raster cache + cur_damage.get() // frame damage + ); + EXPECT_EQ(cur_raster_status, RasterStatus::kDiscarded); + latch.Signal(); + }); + latch.Wait(); +} + +TEST(RasterizerTest, drawWithFrameDamageIsNotEmpty) { + std::string test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + ThreadHost thread_host("io.flutter.test." + test_name + ".", + ThreadHost::Type::Platform | ThreadHost::Type::RASTER | + ThreadHost::Type::IO | ThreadHost::Type::UI); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + fml::AutoResetWaitableEvent latch; + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { + CompositorContext compositor_context; + flutter::testing::MockCanvas canvas; + auto compositor_frame = compositor_context.AcquireFrame( + nullptr, &canvas, nullptr, SkMatrix(), false, true, nullptr, nullptr); + auto last_layer_tree = + std::make_unique(/*frame_size=*/SkISize::Make(100, 100), + /*device_pixel_ratio=*/2.0f); + // Use container as the root layer and PhysicalShapeLayer as the child + // layer. Frame size is 100 x 100 PhysicalShapeLayer's size is 50 x 50 The + // frame_damage is 50,50 in the first frame, and the frame_damage is 50,50 + // in the second frame + auto last_root_layer = std::make_shared(); + auto last_child = std::make_shared( + SK_ColorBLACK, SK_ColorBLACK, + 0.0f, // elevation + SkPath::Rect(SkRect::MakeIWH(50, 50), SkPathDirection::kCCW, 0), + Clip::none); + last_root_layer.get()->Add(last_child); + last_layer_tree->set_root_layer(last_root_layer); + + auto layer_tree = std::make_unique( + /*frame_size=*/SkISize::Make(100, 100), /*device_pixel_ratio=*/2.0f); + auto cur_root_layer = std::make_shared(); + auto cur_child = std::make_shared( + SK_ColorBLACK, SK_ColorBLACK, + 0.0f, // elevation + SkPath::Rect(SkRect::MakeIWH(50, 50), SkPathDirection::kCCW, 0), + Clip::none); + cur_root_layer.get()->Add(cur_child); + layer_tree->set_root_layer(cur_root_layer); + + // Draw Last Frame + std::unique_ptr last_damage = std::make_unique(); + RasterStatus last_raster_status = + compositor_frame->Raster(*last_layer_tree.get(), // layer tree + false, // ignore raster cache + last_damage.get() // frame damage + ); + EXPECT_EQ(last_raster_status, RasterStatus::kSuccess); + + // Draw Current Frame + std::unique_ptr cur_damage = std::make_unique(); + cur_damage->SetPreviousLayerTree(last_layer_tree.get()); + RasterStatus cur_raster_status = + compositor_frame->Raster(*layer_tree.get(), // layer tree + false, // ignore raster cache + cur_damage.get() // frame damage + ); + EXPECT_EQ(cur_raster_status, RasterStatus::kSuccess); + latch.Signal(); + }); + latch.Wait(); +} + } // namespace flutter