Skip to content

Commit 5176d4a

Browse files
iskakaushikjustinmc
authored andcommitted
Add service protocol method to facilitate getting snapshots (flutter#32628)
1 parent 3119a79 commit 5176d4a

File tree

8 files changed

+180
-22
lines changed

8 files changed

+180
-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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,11 @@ Shell::Shell(DartVMRef vm,
449449
task_runners_.GetRasterTaskRunner(),
450450
std::bind(&Shell::OnServiceProtocolEstimateRasterCacheMemory, this,
451451
std::placeholders::_1, std::placeholders::_2)};
452+
service_protocol_handlers_
453+
[ServiceProtocol::kRenderFrameWithRasterStatsExtensionName] = {
454+
task_runners_.GetRasterTaskRunner(),
455+
std::bind(&Shell::OnServiceProtocolRenderFrameWithRasterStats, this,
456+
std::placeholders::_1, std::placeholders::_2)};
452457
}
453458

454459
Shell::~Shell() {
@@ -1812,6 +1817,75 @@ bool Shell::OnServiceProtocolSetAssetBundlePath(
18121817
return false;
18131818
}
18141819

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

2532+
// ktz
2533+
TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsWorks) {
2534+
auto settings = CreateSettingsForFixture();
2535+
std::unique_ptr<Shell> shell = CreateShell(settings);
2536+
2537+
// Create the surface needed by rasterizer
2538+
PlatformViewNotifyCreated(shell.get());
2539+
2540+
auto configuration = RunConfiguration::InferFromSettings(settings);
2541+
configuration.SetEntrypoint("scene_with_red_box");
2542+
2543+
RunEngine(shell.get(), std::move(configuration));
2544+
PumpOneFrame(shell.get());
2545+
2546+
ServiceProtocol::Handler::ServiceProtocolMap empty_params;
2547+
rapidjson::Document document;
2548+
OnServiceProtocol(
2549+
shell.get(), ServiceProtocolEnum::kRenderFrameWithRasterStats,
2550+
shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document);
2551+
rapidjson::StringBuffer buffer;
2552+
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2553+
document.Accept(writer);
2554+
2555+
// It would be better to parse out the json and check for the validity of
2556+
// fields. Below checks approximate what needs to be checked, this can not be
2557+
// an exact check since duration will not exactly match.
2558+
std::string expected_json =
2559+
"\"snapshot\":[137,80,78,71,13,10,26,10,0,"
2560+
"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,"
2561+
"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,"
2562+
"0,0,13,73,68,65,84,8,153,99,248,207,192,240,31,0,5,0,1,255,171,206,54,"
2563+
"137,0,0,0,0,73,69,78,68,174,66,96,130]";
2564+
std::string actual_json = buffer.GetString();
2565+
2566+
EXPECT_THAT(actual_json, ::testing::HasSubstr(expected_json));
2567+
EXPECT_THAT(actual_json,
2568+
::testing::HasSubstr("{\"type\":\"RenderFrameWithRasterStats\""));
2569+
EXPECT_THAT(actual_json, ::testing::HasSubstr("\"duration_micros\""));
2570+
2571+
PlatformViewNotifyDestroyed(shell.get());
2572+
DestroyShell(std::move(shell));
2573+
}
2574+
25322575
// TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to
25332576
// flakiness.
25342577
// TODO(https://github.com/flutter/flutter/issues/100299): Fix it when
@@ -2918,8 +2961,8 @@ TEST_F(ShellTest, AssetManagerMultiSubdir) {
29182961

29192962
std::vector<std::string> filenames = {
29202963
"bad0",
2921-
"notgood", // this is to make sure the pattern (.*)good(.*) only matches
2922-
// things in the subdirectory
2964+
"notgood", // this is to make sure the pattern (.*)good(.*) only
2965+
// matches things in the subdirectory
29232966
};
29242967

29252968
std::vector<std::string> subdir_filenames = {
@@ -2994,8 +3037,9 @@ TEST_F(ShellTest, Spawn) {
29943037
// Fulfill native function for the second Shell's entrypoint.
29953038
fml::CountDownLatch second_latch(2);
29963039
AddNativeCallback(
2997-
// The Dart native function names aren't very consistent but this is just
2998-
// the native function name of the second vm entrypoint in the fixture.
3040+
// The Dart native function names aren't very consistent but this is
3041+
// just the native function name of the second vm entrypoint in the
3042+
// fixture.
29993043
"NotifyNative",
30003044
CREATE_NATIVE_ENTRY([&](auto args) { second_latch.CountDown(); }));
30013045

0 commit comments

Comments
 (0)