From 6440c4f5a186d0faa0163f4c772812df4b1dcfa5 Mon Sep 17 00:00:00 2001 From: Yuqian Li Date: Wed, 12 Aug 2020 21:40:54 -0700 Subject: [PATCH 1/4] Add a service protocol for raster cache memory Related issue: https://github.com/flutter/flutter/issues/56719 --- flow/raster_cache.cc | 30 ++++++----- flow/raster_cache.h | 24 +++++++-- runtime/service_protocol.cc | 4 ++ runtime/service_protocol.h | 1 + shell/common/shell.cc | 22 +++++++++ shell/common/shell.h | 5 ++ shell/common/shell_test.cc | 3 ++ shell/common/shell_test.h | 1 + shell/common/shell_unittests.cc | 88 +++++++++++++++++++++++++++++++++ 9 files changed, 159 insertions(+), 19 deletions(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 1e39e3701a679..9995e0ecd82a5 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -299,35 +299,33 @@ void RasterCache::SetCheckboardCacheImages(bool checkerboard) { void RasterCache::TraceStatsToTimeline() const { #if !FLUTTER_RELEASE + FML_TRACE_COUNTER("flutter", "RasterCache", reinterpret_cast(this), + "LayerCount", layer_cache_.size(), "LayerMBytes", + EstimateLayerCacheByteSize() * 1e-6, "PictureCount", + picture_cache_.size(), "PictureMBytes", + EstimatePictureCacheByteSize() * 1e-6); - size_t layer_cache_count = 0; - size_t layer_cache_bytes = 0; - size_t picture_cache_count = 0; - size_t picture_cache_bytes = 0; +#endif // !FLUTTER_RELEASE +} +size_t RasterCache::EstimateLayerCacheByteSize() const { + size_t layer_cache_bytes = 0; for (const auto& item : layer_cache_) { - layer_cache_count++; if (item.second.image) { layer_cache_bytes += item.second.image->image_bytes(); } } + return layer_cache_bytes; +} +size_t RasterCache::EstimatePictureCacheByteSize() const { + size_t picture_cache_bytes = 0; for (const auto& item : picture_cache_) { - picture_cache_count++; if (item.second.image) { picture_cache_bytes += item.second.image->image_bytes(); } } - - FML_TRACE_COUNTER("flutter", "RasterCache", - reinterpret_cast(this), // - "LayerCount", layer_cache_count, // - "LayerMBytes", layer_cache_bytes * 1e-6, // - "PictureCount", picture_cache_count, // - "PictureMBytes", picture_cache_bytes * 1e-6 // - ); - -#endif // !FLUTTER_RELEASE + return picture_cache_bytes; } } // namespace flutter diff --git a/flow/raster_cache.h b/flow/raster_cache.h index ace5a0c958b58..297a58482f056 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -29,9 +29,7 @@ class RasterCacheResult { }; virtual int64_t image_bytes() const { - return image_ ? image_->dimensions().area() * - image_->imageInfo().bytesPerPixel() - : 0; + return image_ ? image_->imageInfo().computeMinByteSize() : 0; }; private: @@ -170,6 +168,26 @@ class RasterCache { size_t GetPictureCachedEntriesCount() const; + /** + * @brief Estimate how much memory is used by picture raster cache entries in + * bytes. + * + * Only SkImage's memory usage is counted as other objects are often much + * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to + * estimate the SkImage memory usage. + */ + size_t EstimatePictureCacheByteSize() const; + + /** + * @brief Estimate how much memory is used by layer raster cache entries in + * bytes. + * + * Only SkImage's memory usage is counted as other objects are often much + * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to + * estimate the SkImage memory usage. + */ + size_t EstimateLayerCacheByteSize() const; + private: struct Entry { bool used_this_frame = false; diff --git a/runtime/service_protocol.cc b/runtime/service_protocol.cc index b5320f42acb68..ace3039f2f959 100644 --- a/runtime/service_protocol.cc +++ b/runtime/service_protocol.cc @@ -35,6 +35,9 @@ const std::string_view ServiceProtocol::kGetDisplayRefreshRateExtensionName = "_flutter.getDisplayRefreshRate"; const std::string_view ServiceProtocol::kGetSkSLsExtensionName = "_flutter.getSkSLs"; +const std::string_view + ServiceProtocol::kEstimateRasterCacheMemoryExtensionName = + "_flutter.estimateRasterCacheMemory"; static constexpr std::string_view kViewIdPrefx = "_flutterView/"; static constexpr std::string_view kListViewsExtensionName = @@ -53,6 +56,7 @@ ServiceProtocol::ServiceProtocol() kSetAssetBundlePathExtensionName, kGetDisplayRefreshRateExtensionName, kGetSkSLsExtensionName, + kEstimateRasterCacheMemoryExtensionName, }), handlers_mutex_(fml::SharedMutex::Create()) {} diff --git a/runtime/service_protocol.h b/runtime/service_protocol.h index dfc0cd458fbd9..e809b7d833fc4 100644 --- a/runtime/service_protocol.h +++ b/runtime/service_protocol.h @@ -28,6 +28,7 @@ class ServiceProtocol { static const std::string_view kSetAssetBundlePathExtensionName; static const std::string_view kGetDisplayRefreshRateExtensionName; static const std::string_view kGetSkSLsExtensionName; + static const std::string_view kEstimateRasterCacheMemoryExtensionName; class Handler { public: diff --git a/shell/common/shell.cc b/shell/common/shell.cc index d11a14f06b4e2..fc50fa06050a4 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -375,6 +375,11 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) task_runners_.GetIOTaskRunner(), std::bind(&Shell::OnServiceProtocolGetSkSLs, this, std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_ + [ServiceProtocol::kEstimateRasterCacheMemoryExtensionName] = { + task_runners_.GetRasterTaskRunner(), + std::bind(&Shell::OnServiceProtocolEstimateRasterCacheMemory, this, + std::placeholders::_1, std::placeholders::_2)}; } Shell::~Shell() { @@ -1424,6 +1429,23 @@ bool Shell::OnServiceProtocolGetSkSLs( return true; } +bool Shell::OnServiceProtocolEstimateRasterCacheMemory( + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document* response) { + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); + const auto& raster_cache = rasterizer_->compositor_context()->raster_cache(); + response->SetObject(); + response->AddMember("type", "EstimateRasterCacheMemory", + response->GetAllocator()); + response->AddMember("layer_bytes", + raster_cache.EstimateLayerCacheByteSize(), + response->GetAllocator()); + response->AddMember("picture_bytes", + raster_cache.EstimatePictureCacheByteSize(), + response->GetAllocator()); + return true; +} + // Service protocol handler bool Shell::OnServiceProtocolSetAssetBundlePath( const ServiceProtocol::Handler::ServiceProtocolMap& params, diff --git a/shell/common/shell.h b/shell/common/shell.h index 847792e7ef4a1..dd09fad2ae7a6 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -584,6 +584,11 @@ class Shell final : public PlatformView::Delegate, const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response); + // Service protocol handler + bool OnServiceProtocolEstimateRasterCacheMemory( + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document* response); + fml::WeakPtrFactory weak_factory_; // For accessing the Shell via the raster thread, necessary for various diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 86124694f980e..d60c80805ce9d 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -186,6 +186,9 @@ void ShellTest::OnServiceProtocol( case ServiceProtocolEnum::kGetSkSLs: shell->OnServiceProtocolGetSkSLs(params, response); break; + case ServiceProtocolEnum::kEstimateRasterCacheMemory: + shell->OnServiceProtocolEstimateRasterCacheMemory(params, response); + break; case ServiceProtocolEnum::kSetAssetBundlePath: shell->OnServiceProtocolSetAssetBundlePath(params, response); break; diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index e296ba118c73e..e534fcec3cef0 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -80,6 +80,7 @@ class ShellTest : public FixtureTest { enum ServiceProtocolEnum { kGetSkSLs, + kEstimateRasterCacheMemory, kSetAssetBundlePath, kRunInView, }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 6d62bc503be2c..c12a9687b40f2 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1345,5 +1345,93 @@ TEST_F(ShellTest, RasterizerMakeRasterSnapshot) { DestroyShell(std::move(shell), std::move(task_runners)); } +static sk_sp MakeSizedPicture(int width, int height) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = + recorder.beginRecording(SkRect::MakeXYWH(0, 0, width, height)); + recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, width, height), + SkPaint(SkColor4f::FromColor(SK_ColorRED))); + return recorder.finishRecordingAsPicture(); +} + +TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { + Settings settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // 1. Construct a picture and a picture layer to be raster cached. + sk_sp picture = MakeSizedPicture(10, 10); + fml::RefPtr queue = fml::MakeRefCounted( + GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(0, 0), + flutter::SkiaGPUObject({MakeSizedPicture(100, 100), queue}), + false, false); + picture_layer->set_paint_bounds(SkRect::MakeWH(100, 100)); + + // 2. Rasterize the picture and the picture layer in the raster cache. + std::promise rasterized; + shell->GetTaskRunners().GetRasterTaskRunner()->PostTask( + [&shell, &rasterized, &picture, &picture_layer] { + auto* compositor_context = shell->GetRasterizer()->compositor_context(); + auto& raster_cache = compositor_context->raster_cache(); + // 2.1. Rasterize the picture. Call Draw multiple times to pass the + // access threshold (default to 3) so a cache can be generated. + SkCanvas dummy_canvas; + bool picture_cache_generated; + for (int i = 0; i < 4; i += 1) { + picture_cache_generated = + raster_cache.Prepare(nullptr, // GrDirectContext + picture.get(), SkMatrix::I(), + nullptr, // SkColorSpace + true, // isComplex + false // willChange + ); + raster_cache.Draw(*picture, dummy_canvas); + } + ASSERT_TRUE(picture_cache_generated); + + // 2.2. Rasterize the picture layer. + Stopwatch raster_time; + Stopwatch ui_time; + MutatorsStack mutators_stack; + TextureRegistry texture_registry; + PrerollContext preroll_context = { + nullptr, /* raster_cache */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ + mutators_stack, nullptr, /* color_space */ + kGiantRect, /* cull_rect */ + false, /* layer reads from surface */ + raster_time, ui_time, texture_registry, + false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ + 1.0f, /* frame_device_pixel_ratio */ + 0.0f, /* total_elevation */ + false, /* has_platform_view */ + }; + raster_cache.Prepare(&preroll_context, picture_layer.get(), + SkMatrix::I()); + rasterized.set_value(true); + }); + rasterized.get_future().wait(); + + // 3. Call the service protocol and check its output. + ServiceProtocol::Handler::ServiceProtocolMap empty_params; + rapidjson::Document document; + OnServiceProtocol( + shell.get(), ServiceProtocolEnum::kEstimateRasterCacheMemory, + shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + std::string expected_json = + "{\"type\":\"EstimateRasterCacheMemory\",\"layer_bytes\":40000,\"picture_" + "bytes\":400}"; + std::string actual_json = buffer.GetString(); + ASSERT_EQ(actual_json, expected_json); + + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter From d94438da37f489d84b274083e7003920900313de Mon Sep 17 00:00:00 2001 From: Yuqian Li Date: Thu, 13 Aug 2020 14:02:11 -0700 Subject: [PATCH 2/4] Nit fixes --- flow/raster_cache.cc | 7 ++++--- shell/common/shell.cc | 4 ++-- shell/common/shell_unittests.cc | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 9995e0ecd82a5..3a9fdd55e9aa6 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -299,11 +299,12 @@ void RasterCache::SetCheckboardCacheImages(bool checkerboard) { void RasterCache::TraceStatsToTimeline() const { #if !FLUTTER_RELEASE + constexpr double kBytesToMegaBytes = 1e-6; FML_TRACE_COUNTER("flutter", "RasterCache", reinterpret_cast(this), "LayerCount", layer_cache_.size(), "LayerMBytes", - EstimateLayerCacheByteSize() * 1e-6, "PictureCount", - picture_cache_.size(), "PictureMBytes", - EstimatePictureCacheByteSize() * 1e-6); + EstimateLayerCacheByteSize() * kBytesToMegaBytes, + "PictureCount", picture_cache_.size(), "PictureMBytes", + EstimatePictureCacheByteSize() * kBytesToMegaBytes); #endif // !FLUTTER_RELEASE } diff --git a/shell/common/shell.cc b/shell/common/shell.cc index fc50fa06050a4..13bd40638991a 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1437,10 +1437,10 @@ bool Shell::OnServiceProtocolEstimateRasterCacheMemory( response->SetObject(); response->AddMember("type", "EstimateRasterCacheMemory", response->GetAllocator()); - response->AddMember("layer_bytes", + response->AddMember("layerBytes", raster_cache.EstimateLayerCacheByteSize(), response->GetAllocator()); - response->AddMember("picture_bytes", + response->AddMember("pictureBytes", raster_cache.EstimatePictureCacheByteSize(), response->GetAllocator()); return true; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index c12a9687b40f2..95ab0071fdb36 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1425,8 +1425,8 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { rapidjson::Writer writer(buffer); document.Accept(writer); std::string expected_json = - "{\"type\":\"EstimateRasterCacheMemory\",\"layer_bytes\":40000,\"picture_" - "bytes\":400}"; + "{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40000,\"picture" + "Bytes\":400}"; std::string actual_json = buffer.GetString(); ASSERT_EQ(actual_json, expected_json); From b4b4ab4dca0a27d4e9692d5f95683d7bb6c400bc Mon Sep 17 00:00:00 2001 From: Yuqian Li Date: Thu, 13 Aug 2020 14:34:07 -0700 Subject: [PATCH 3/4] 1 << 20 --- flow/raster_cache.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 3a9fdd55e9aa6..a810551eb539d 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -299,12 +299,12 @@ void RasterCache::SetCheckboardCacheImages(bool checkerboard) { void RasterCache::TraceStatsToTimeline() const { #if !FLUTTER_RELEASE - constexpr double kBytesToMegaBytes = 1e-6; + constexpr double kMegaBytes = (1 << 20); FML_TRACE_COUNTER("flutter", "RasterCache", reinterpret_cast(this), "LayerCount", layer_cache_.size(), "LayerMBytes", - EstimateLayerCacheByteSize() * kBytesToMegaBytes, + EstimateLayerCacheByteSize() / kMegaBytes, "PictureCount", picture_cache_.size(), "PictureMBytes", - EstimatePictureCacheByteSize() * kBytesToMegaBytes); + EstimatePictureCacheByteSize() / kMegaBytes); #endif // !FLUTTER_RELEASE } From 095d8d0bfe8f32028cd9e3aa2717b4afeda6f228 Mon Sep 17 00:00:00 2001 From: Yuqian Li Date: Thu, 13 Aug 2020 15:05:24 -0700 Subject: [PATCH 4/4] Format --- flow/raster_cache.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index a810551eb539d..6a45232fce1ca 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -302,8 +302,8 @@ void RasterCache::TraceStatsToTimeline() const { constexpr double kMegaBytes = (1 << 20); FML_TRACE_COUNTER("flutter", "RasterCache", reinterpret_cast(this), "LayerCount", layer_cache_.size(), "LayerMBytes", - EstimateLayerCacheByteSize() / kMegaBytes, - "PictureCount", picture_cache_.size(), "PictureMBytes", + EstimateLayerCacheByteSize() / kMegaBytes, "PictureCount", + picture_cache_.size(), "PictureMBytes", EstimatePictureCacheByteSize() / kMegaBytes); #endif // !FLUTTER_RELEASE