Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1033a1e

Browse files
committed
Add service protocol method to facilitate getting snapshots
1 parent cf3c648 commit 1033a1e

File tree

8 files changed

+175
-22
lines changed

8 files changed

+175
-22
lines changed

runtime/service_protocol.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ const std::string_view ServiceProtocol::kGetSkSLsExtensionName =
3737
const std::string_view
3838
ServiceProtocol::kEstimateRasterCacheMemoryExtensionName =
3939
"_flutter.estimateRasterCacheMemory";
40+
const std::string_view
41+
ServiceProtocol::kRenderFrameWithRasterStatsExtensionName =
42+
"_flutter.renderFrameWithRasterStats";
4043

4144
static constexpr std::string_view kViewIdPrefx = "_flutterView/";
4245
static constexpr std::string_view kListViewsExtensionName =
@@ -56,6 +59,7 @@ ServiceProtocol::ServiceProtocol()
5659
kGetDisplayRefreshRateExtensionName,
5760
kGetSkSLsExtensionName,
5861
kEstimateRasterCacheMemoryExtensionName,
62+
kRenderFrameWithRasterStatsExtensionName,
5963
}),
6064
handlers_mutex_(fml::SharedMutex::Create()) {}
6165

runtime/service_protocol.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class ServiceProtocol {
2929
static const std::string_view kGetDisplayRefreshRateExtensionName;
3030
static const std::string_view kGetSkSLsExtensionName;
3131
static const std::string_view kEstimateRasterCacheMemoryExtensionName;
32+
static const std::string_view kRenderFrameWithRasterStatsExtensionName;
3233

3334
class Handler {
3435
public:

shell/common/fixtures/shell_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,25 @@ void frameCallback(_Image, int) {
299299
// 'ItDoesNotCrashThatSkiaUnrefQueueDrainAfterIOManagerReset'.
300300
// The test is a regression test and doesn't care about images, so it is empty.
301301
}
302+
303+
Picture CreateRedBox(Size size) {
304+
Paint paint = Paint()
305+
..color = Color.fromARGB(255, 255, 0, 0)
306+
..style = PaintingStyle.fill;
307+
PictureRecorder baseRecorder = PictureRecorder();
308+
Canvas canvas = Canvas(baseRecorder);
309+
canvas.drawRect(Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint);
310+
return baseRecorder.endRecording();
311+
}
312+
313+
@pragma('vm:entry-point')
314+
void scene_with_red_box() {
315+
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
316+
SceneBuilder builder = SceneBuilder();
317+
builder.pushOffset(0.0, 0.0);
318+
builder.addPicture(Offset(0.0, 0.0), CreateRedBox(Size(2.0, 2.0)));
319+
builder.pop();
320+
PlatformDispatcher.instance.views.first.render(builder.build());
321+
};
322+
PlatformDispatcher.instance.scheduleFrame();
323+
}

shell/common/shell.cc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#include <cstddef>
6+
#include "flow/layer_snapshot_store.h"
7+
#include "rapidjson/document.h"
58
#define RAPIDJSON_HAS_STDSTRING 1
69
#include "flutter/shell/common/shell.h"
710

@@ -449,6 +452,11 @@ Shell::Shell(DartVMRef vm,
449452
task_runners_.GetRasterTaskRunner(),
450453
std::bind(&Shell::OnServiceProtocolEstimateRasterCacheMemory, this,
451454
std::placeholders::_1, std::placeholders::_2)};
455+
service_protocol_handlers_
456+
[ServiceProtocol::kRenderFrameWithRasterStatsExtensionName] = {
457+
task_runners_.GetRasterTaskRunner(),
458+
std::bind(&Shell::OnServiceProtocolRenderFrameWithRasterStats, this,
459+
std::placeholders::_1, std::placeholders::_2)};
452460
}
453461

454462
Shell::~Shell() {
@@ -1812,6 +1820,75 @@ bool Shell::OnServiceProtocolSetAssetBundlePath(
18121820
return false;
18131821
}
18141822

1823+
static rapidjson::Value SerializeLayerSnapshot(
1824+
const LayerSnapshotData& snapshot,
1825+
rapidjson::Document* response) {
1826+
auto& allocator = response->GetAllocator();
1827+
rapidjson::Value result;
1828+
result.SetObject();
1829+
result.AddMember("layer_unique_id", snapshot.GetLayerUniqueId(), allocator);
1830+
result.AddMember("duration_micros", snapshot.GetDuration().ToMicroseconds(),
1831+
allocator);
1832+
sk_sp<SkData> snapshot_bytes = snapshot.GetSnapshot();
1833+
if (snapshot_bytes) {
1834+
rapidjson::Value image;
1835+
image.SetArray();
1836+
const uint8_t* data =
1837+
reinterpret_cast<const uint8_t*>(snapshot_bytes->data());
1838+
for (size_t i = 0; i < snapshot_bytes->size(); i++) {
1839+
image.PushBack(data[i], allocator);
1840+
}
1841+
result.AddMember("snapshot", image, allocator);
1842+
}
1843+
return result;
1844+
}
1845+
1846+
bool Shell::OnServiceProtocolRenderFrameWithRasterStats(
1847+
const ServiceProtocol::Handler::ServiceProtocolMap& params,
1848+
rapidjson::Document* response) {
1849+
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1850+
1851+
if (auto last_layer_tree = rasterizer_->GetLastLayerTree()) {
1852+
auto& allocator = response->GetAllocator();
1853+
response->SetObject();
1854+
response->AddMember("type", "RenderFrameWithRasterStats", allocator);
1855+
1856+
// When rendering the last layer tree, we do not need to build a frame,
1857+
// invariants in FrameTimingRecorder enforce that raster timings can not be
1858+
// set before build-end.
1859+
auto frame_timings_recorder = std::make_unique<FrameTimingsRecorder>();
1860+
const auto now = fml::TimePoint::Now();
1861+
frame_timings_recorder->RecordVsync(now, now);
1862+
frame_timings_recorder->RecordBuildStart(now);
1863+
frame_timings_recorder->RecordBuildEnd(now);
1864+
1865+
last_layer_tree->enable_leaf_layer_tracing(true);
1866+
rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder));
1867+
last_layer_tree->enable_leaf_layer_tracing(false);
1868+
1869+
rapidjson::Value snapshots;
1870+
snapshots.SetArray();
1871+
1872+
LayerSnapshotStore& store =
1873+
rasterizer_->compositor_context()->snapshot_store();
1874+
for (const LayerSnapshotData& data : store) {
1875+
snapshots.PushBack(SerializeLayerSnapshot(data, response), allocator);
1876+
}
1877+
1878+
response->AddMember("snapshots", snapshots, allocator);
1879+
return true;
1880+
} else {
1881+
const char* error =
1882+
"Failed to render the last frame with raster stats."
1883+
" Rasterizer does not hold a valid last layer tree."
1884+
" This could happen if this method was invoked before a frame was "
1885+
"rendered";
1886+
FML_DLOG(ERROR) << error;
1887+
ServiceProtocolFailureError(response, error);
1888+
return false;
1889+
}
1890+
}
1891+
18151892
Rasterizer::Screenshot Shell::Screenshot(
18161893
Rasterizer::ScreenshotType screenshot_type,
18171894
bool base64_encode) {

shell/common/shell.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,15 @@ class Shell final : public PlatformView::Delegate,
706706
const ServiceProtocol::Handler::ServiceProtocolMap& params,
707707
rapidjson::Document* response);
708708

709+
// Service protocol handler
710+
//
711+
// Renders a frame and responds with various statistics pertaining to the
712+
// raster call. These include time taken to raster every leaf layer and also
713+
// leaf layer snapshots.
714+
bool OnServiceProtocolRenderFrameWithRasterStats(
715+
const ServiceProtocol::Handler::ServiceProtocolMap& params,
716+
rapidjson::Document* response);
717+
709718
// |ResourceCacheLimitItem|
710719
size_t GetResourceCacheLimit() override { return resource_cache_limit_; };
711720

shell/common/shell_test.cc

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -256,24 +256,27 @@ void ShellTest::OnServiceProtocol(
256256
const ServiceProtocol::Handler::ServiceProtocolMap& params,
257257
rapidjson::Document* response) {
258258
std::promise<bool> finished;
259-
fml::TaskRunner::RunNowOrPostTask(
260-
task_runner, [shell, some_protocol, params, response, &finished]() {
261-
switch (some_protocol) {
262-
case ServiceProtocolEnum::kGetSkSLs:
263-
shell->OnServiceProtocolGetSkSLs(params, response);
264-
break;
265-
case ServiceProtocolEnum::kEstimateRasterCacheMemory:
266-
shell->OnServiceProtocolEstimateRasterCacheMemory(params, response);
267-
break;
268-
case ServiceProtocolEnum::kSetAssetBundlePath:
269-
shell->OnServiceProtocolSetAssetBundlePath(params, response);
270-
break;
271-
case ServiceProtocolEnum::kRunInView:
272-
shell->OnServiceProtocolRunInView(params, response);
273-
break;
274-
}
275-
finished.set_value(true);
276-
});
259+
fml::TaskRunner::RunNowOrPostTask(task_runner, [shell, some_protocol, params,
260+
response, &finished]() {
261+
switch (some_protocol) {
262+
case ServiceProtocolEnum::kGetSkSLs:
263+
shell->OnServiceProtocolGetSkSLs(params, response);
264+
break;
265+
case ServiceProtocolEnum::kEstimateRasterCacheMemory:
266+
shell->OnServiceProtocolEstimateRasterCacheMemory(params, response);
267+
break;
268+
case ServiceProtocolEnum::kSetAssetBundlePath:
269+
shell->OnServiceProtocolSetAssetBundlePath(params, response);
270+
break;
271+
case ServiceProtocolEnum::kRunInView:
272+
shell->OnServiceProtocolRunInView(params, response);
273+
break;
274+
case ServiceProtocolEnum::kRenderFrameWithRasterStats:
275+
shell->OnServiceProtocolRenderFrameWithRasterStats(params, response);
276+
break;
277+
}
278+
finished.set_value(true);
279+
});
277280
finished.get_future().wait();
278281
}
279282

shell/common/shell_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class ShellTest : public FixtureTest {
106106
kEstimateRasterCacheMemory,
107107
kSetAssetBundlePath,
108108
kRunInView,
109+
kRenderFrameWithRasterStats,
109110
};
110111

111112
// Helper method to test private method Shell::OnServiceProtocolGetSkSLs.

shell/common/shell_unittests.cc

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,6 +2524,41 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) {
25242524
DestroyShell(std::move(shell));
25252525
}
25262526

2527+
// ktz
2528+
TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsWorks) {
2529+
auto settings = CreateSettingsForFixture();
2530+
std::unique_ptr<Shell> shell = CreateShell(settings);
2531+
2532+
// Create the surface needed by rasterizer
2533+
PlatformViewNotifyCreated(shell.get());
2534+
2535+
auto configuration = RunConfiguration::InferFromSettings(settings);
2536+
configuration.SetEntrypoint("scene_with_red_box");
2537+
2538+
RunEngine(shell.get(), std::move(configuration));
2539+
PumpOneFrame(shell.get());
2540+
2541+
ServiceProtocol::Handler::ServiceProtocolMap empty_params;
2542+
rapidjson::Document document;
2543+
OnServiceProtocol(
2544+
shell.get(), ServiceProtocolEnum::kRenderFrameWithRasterStats,
2545+
shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document);
2546+
rapidjson::StringBuffer buffer;
2547+
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2548+
document.Accept(writer);
2549+
std::string expected_json =
2550+
"\"snapshot\":[137,80,78,71,13,10,26,10,0,"
2551+
"0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,6,0,0,0,31,21,196,137,0,0,0,1,115,"
2552+
"82,71,66,0,174,206,28,233,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,"
2553+
"0,0,13,73,68,65,84,8,153,99,248,207,192,240,31,0,5,0,1,255,171,206,54,"
2554+
"137,0,0,0,0,73,69,78,68,174,66,96,130]";
2555+
std::string actual_json = buffer.GetString();
2556+
EXPECT_THAT(actual_json, ::testing::HasSubstr(expected_json));
2557+
2558+
PlatformViewNotifyDestroyed(shell.get());
2559+
DestroyShell(std::move(shell));
2560+
}
2561+
25272562
// TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to
25282563
// flakiness.
25292564
// TODO(https://github.com/flutter/flutter/issues/100299): Fix it when
@@ -2913,8 +2948,8 @@ TEST_F(ShellTest, AssetManagerMultiSubdir) {
29132948

29142949
std::vector<std::string> filenames = {
29152950
"bad0",
2916-
"notgood", // this is to make sure the pattern (.*)good(.*) only matches
2917-
// things in the subdirectory
2951+
"notgood", // this is to make sure the pattern (.*)good(.*) only
2952+
// matches things in the subdirectory
29182953
};
29192954

29202955
std::vector<std::string> subdir_filenames = {
@@ -2989,8 +3024,9 @@ TEST_F(ShellTest, Spawn) {
29893024
// Fulfill native function for the second Shell's entrypoint.
29903025
fml::CountDownLatch second_latch(2);
29913026
AddNativeCallback(
2992-
// The Dart native function names aren't very consistent but this is just
2993-
// the native function name of the second vm entrypoint in the fixture.
3027+
// The Dart native function names aren't very consistent but this is
3028+
// just the native function name of the second vm entrypoint in the
3029+
// fixture.
29943030
"NotifyNative",
29953031
CREATE_NATIVE_ENTRY([&](auto args) { second_latch.CountDown(); }));
29963032

0 commit comments

Comments
 (0)