diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index baeb05a703512..0c350100bfb21 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -257,6 +257,7 @@ ../../../flutter/shell/common/animator_unittests.cc ../../../flutter/shell/common/base64_unittests.cc ../../../flutter/shell/common/context_options_unittests.cc +../../../flutter/shell/common/display_list_debugger_unittests.cc ../../../flutter/shell/common/dl_op_spy_unittests.cc ../../../flutter/shell/common/engine_animator_unittests.cc ../../../flutter/shell/common/engine_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b8c15596edb2c..486606e92ac2f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -41507,6 +41507,8 @@ ORIGIN: ../../../flutter/shell/common/context_options.h + ../../../flutter/LICEN ORIGIN: ../../../flutter/shell/common/dart_native_benchmarks.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/common/display_list_debugger.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/common/display_list_debugger.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display_manager.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display_manager.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/dl_op_spy.cc + ../../../flutter/LICENSE @@ -44395,6 +44397,8 @@ FILE: ../../../flutter/shell/common/context_options.h FILE: ../../../flutter/shell/common/dart_native_benchmarks.cc FILE: ../../../flutter/shell/common/display.cc FILE: ../../../flutter/shell/common/display.h +FILE: ../../../flutter/shell/common/display_list_debugger.cc +FILE: ../../../flutter/shell/common/display_list_debugger.h FILE: ../../../flutter/shell/common/display_manager.cc FILE: ../../../flutter/shell/common/display_manager.h FILE: ../../../flutter/shell/common/dl_op_spy.cc diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 8e2d9f2174b3a..ae6a999dd2597 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -86,6 +86,8 @@ source_set("common") { "animator.h", "context_options.cc", "context_options.h", + "display_list_debugger.cc", + "display_list_debugger.h", "display_manager.cc", "display_manager.h", "dl_op_spy.cc", @@ -312,6 +314,7 @@ if (enable_unittests) { "animator_unittests.cc", "base64_unittests.cc", "context_options_unittests.cc", + "display_list_debugger_unittests.cc", "dl_op_spy_unittests.cc", "engine_animator_unittests.cc", "engine_unittests.cc", diff --git a/shell/common/display_list_debugger.cc b/shell/common/display_list_debugger.cc new file mode 100644 index 0000000000000..2b4ddc71f1e56 --- /dev/null +++ b/shell/common/display_list_debugger.cc @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/common/display_list_debugger.h" + +#include + +#include "flutter/fml/logging.h" + +namespace { +std::atomic saveDisplayListPath; +template +using FreePtr = std::unique_ptr; +} // namespace + +namespace flutter { +void DisplayListDebugger::HandleMessage( + std::unique_ptr message) { + const fml::MallocMapping& data = message->data(); + FreePtr(saveDisplayListPath.exchange( + strdup(reinterpret_cast(data.GetMapping()))), + &free); +} + +fml::Status DisplayListDebugger::SaveDisplayList( + const sk_sp& display_list) { + if (FreePtr path = + FreePtr(saveDisplayListPath.exchange(nullptr), &free)) { + fml::UniqueFD dir = fml::OpenDirectory( + path.get(), /*create_if_necessary=*/false, fml::FilePermission::kWrite); + // DisplayList doesn't have an accessor for the byte size of the storage, + // adding one may confuse the api so we derive it with bytes(). + size_t size = display_list->bytes(/*nested=*/false) - sizeof(DisplayList); + fml::NonOwnedMapping mapping(display_list->GetStorage().get(), size); + bool success = fml::WriteAtomically(dir, "display_list.dat", mapping); + FML_LOG(ERROR) << "store display_list (" << success << "):" << path.get(); + return success ? fml::Status() + : fml::Status(fml::StatusCode::kUnknown, "write failed"); + } + return fml::Status(fml::StatusCode::kUnavailable, "unavailable"); +} +} // namespace flutter diff --git a/shell/common/display_list_debugger.h b/shell/common/display_list_debugger.h new file mode 100644 index 0000000000000..1c8e622e5f9c5 --- /dev/null +++ b/shell/common/display_list_debugger.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_DISPLAY_LIST_DEBUGGER_H_ +#define FLUTTER_SHELL_COMMON_DISPLAY_LIST_DEBUGGER_H_ + +#include "flutter/display_list/display_list.h" +#include "flutter/fml/status.h" +#include "flutter/lib/ui/window/platform_message.h" + +namespace flutter { + +/// Utility for saving display lists to disk in response to platform messages. +class DisplayListDebugger { + public: + static constexpr char kChannelName[] = "flutter/display_list_debugger"; + static void HandleMessage(std::unique_ptr message); + static fml::Status SaveDisplayList(const sk_sp& display_list); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_COMMON_DISPLAY_LIST_DEBUGGER_H_ diff --git a/shell/common/display_list_debugger_unittests.cc b/shell/common/display_list_debugger_unittests.cc new file mode 100644 index 0000000000000..6cf94b2c62020 --- /dev/null +++ b/shell/common/display_list_debugger_unittests.cc @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/display_list/dl_builder.h" +#include "flutter/shell/common/display_list_debugger.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(DisplayListDebuggerTest, Noop) { + DisplayListBuilder builder; + sk_sp display_list = builder.Build(); + EXPECT_FALSE(DisplayListDebugger::SaveDisplayList(display_list).ok()); +} + +TEST(DisplayListDebuggerTest, Simple) { + DisplayListBuilder builder; + builder.Scale(0.2, 0.2); + sk_sp display_list = builder.Build(); + fml::ScopedTemporaryDirectory temp_dir; + auto message = std::make_unique( + DisplayListDebugger::kChannelName, + fml::MallocMapping::Copy(temp_dir.path().c_str(), + temp_dir.path().size() + 1), + /*response=*/nullptr); + DisplayListDebugger::HandleMessage(std::move(message)); + EXPECT_TRUE(DisplayListDebugger::SaveDisplayList(display_list).ok()); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 9cb5735c6a14c..7f8ac70bd12e1 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -24,6 +24,7 @@ #include "flutter/fml/trace_event.h" #include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/base64.h" +#include "flutter/shell/common/display_list_debugger.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" @@ -1328,6 +1329,11 @@ void Shell::OnEngineHandlePlatformMessage( return; } + if (message->channel() == DisplayListDebugger::kChannelName) { + DisplayListDebugger::HandleMessage(std::move(message)); + return; + } + if (platform_message_handler_) { if (route_messages_through_platform_thread_ && !platform_message_handler_ diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 01093fe37612f..5dac3e2565631 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -10,7 +10,9 @@ #include "flutter/common/settings.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/display_list_debugger.h" #include "impeller/display_list/dl_dispatcher.h" #include "impeller/renderer/backend/metal/surface_mtl.h" #include "impeller/typographer/backends/skia/typographer_context_skia.h" @@ -127,6 +129,10 @@ return false; } +#ifndef NDEBUG + DisplayListDebugger::SaveDisplayList(display_list); +#endif + if (!disable_partial_repaint && damage) { uintptr_t texture = reinterpret_cast(last_texture);