From 3d66240f71c51cf7d25875b96c7034dc1589745f Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 24 Jul 2024 12:51:43 -0700 Subject: [PATCH 1/3] [iOS] switch to FlutterMetalLayer and add Impeller capture scope. --- impeller/renderer/backend/metal/context_mtl.h | 39 +++++++++++++++++-- .../renderer/backend/metal/context_mtl.mm | 30 ++++++++++++++ impeller/renderer/backend/metal/surface_mtl.h | 5 +++ .../renderer/backend/metal/surface_mtl.mm | 12 +++++- shell/gpu/gpu_surface_metal_impeller.mm | 1 + .../gpu_surface_metal_impeller_unittests.mm | 30 +++++++++++++- .../ios/framework/Source/FlutterMetalLayer.mm | 7 ++-- 7 files changed, 115 insertions(+), 9 deletions(-) diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index d1669206f0351..b09933c752dfc 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -31,6 +31,36 @@ namespace impeller { +/// @brief Creates and manages a Metal capture scope that supports frame capture +/// when using the FlutterMetalLayer backed drawable. +class ImpellerMetalCaptureManager { + public: + /// @brief Construct a new capture manager from the provided Metal device. + explicit ImpellerMetalCaptureManager(id device); + + ~ImpellerMetalCaptureManager() = default; + + /// Whether or not the Impeller capture scope is active. + /// + /// This is distinct from whether or not there is a session recording the + /// capture. That can be checked with `[[MTLCaptureManager + /// sharedCaptureManager] isCapturing].` + bool CaptureScopeActive() const; + + /// @brief Begin a new capture scope, no-op if the scope has already started. + void StartCapture(); + + /// @brief End the current capture scope. + void FinishCapture(); + + private: + id current_capture_scope_; + bool scope_active_ = false; + + ImpellerMetalCaptureManager(const ImpellerMetalCaptureManager&) = default; + ImpellerMetalCaptureManager(ImpellerMetalCaptureManager&&) = delete; +}; + class ContextMTL final : public Context, public BackendCast, public std::enable_shared_from_this { @@ -101,6 +131,8 @@ class ContextMTL final : public Context, #ifdef IMPELLER_DEBUG std::shared_ptr GetGPUTracer() const; + + const std::shared_ptr GetCaptureManager() const; #endif // IMPELLER_DEBUG // |Context| @@ -125,12 +157,13 @@ class ContextMTL final : public Context, std::shared_ptr resource_allocator_; std::shared_ptr device_capabilities_; std::shared_ptr is_gpu_disabled_sync_switch_; -#ifdef IMPELLER_DEBUG - std::shared_ptr gpu_tracer_; -#endif // IMPELLER_DEBUG std::deque> tasks_awaiting_gpu_; std::unique_ptr sync_switch_observer_; std::shared_ptr command_queue_ip_; +#ifdef IMPELLER_DEBUG + std::shared_ptr gpu_tracer_; + std::shared_ptr capture_manager_; +#endif // IMPELLER_DEBUG bool is_valid_ = false; ContextMTL(id device, diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 22882ea0bd666..473a6016723ec 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "impeller/renderer/backend/metal/context_mtl.h" +#include #include @@ -134,6 +135,7 @@ static bool DeviceSupportsComputeSubgroups(id device) { command_queue_ip_ = std::make_shared(); #ifdef IMPELLER_DEBUG gpu_tracer_ = std::make_shared(); + capture_manager_ = std::make_shared(device_); #endif // IMPELLER_DEBUG is_valid_ = true; } @@ -404,4 +406,32 @@ new ContextMTL(device, command_queue, return command_queue_ip_; } +const std::shared_ptr ContextMTL::GetCaptureManager() const { + return capture_manager_; +} + +ImpellerMetalCaptureManager::ImpellerMetalCaptureManager(id device) { + current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager] + newCaptureScopeWithDevice:device]; + [current_capture_scope_ setLabel:@"Impeller Frame"]; +} + +bool ImpellerMetalCaptureManager::CaptureScopeActive() const { + return scope_active_; +} + +void ImpellerMetalCaptureManager::StartCapture() { + if (scope_active_) { + return; + } + scope_active_ = true; + [current_capture_scope_ beginScope]; +} + +void ImpellerMetalCaptureManager::FinishCapture() { + FML_DCHECK(scope_active_); + [current_capture_scope_ endScope]; + scope_active_ = false; +} + } // namespace impeller diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 5ddc6973d06dd..dd028266a4aa7 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -61,6 +61,10 @@ class SurfaceMTL final : public Surface { // |Surface| bool Present() const override; + void SetFrameBoundary(bool frame_boundary) { + frame_boundary_ = frame_boundary; + } + private: std::weak_ptr context_; std::shared_ptr resolve_texture_; @@ -69,6 +73,7 @@ class SurfaceMTL final : public Surface { std::shared_ptr destination_texture_; bool requires_blit_ = false; std::optional clip_rect_; + bool frame_boundary_ = false; static bool ShouldPerformPartialRepaint(std::optional damage_rect); diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 9219aca941278..5c65b6cfea32c 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -197,7 +197,14 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; source_texture_(std::move(source_texture)), destination_texture_(std::move(destination_texture)), requires_blit_(requires_blit), - clip_rect_(clip_rect) {} + clip_rect_(clip_rect) { +#ifdef IMPELLER_DEBUG + auto mtl_context = context_.lock(); + if (mtl_context) { + ContextMTL::Cast(mtl_context.get())->GetCaptureManager()->StartCapture(); + } +#endif // IMPELLER_DEBUG +} // |Surface| SurfaceMTL::~SurfaceMTL() = default; @@ -231,6 +238,9 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; #ifdef IMPELLER_DEBUG context->GetResourceAllocator()->DebugTraceMemoryStatistics(); + if (frame_boundary_) { + ContextMTL::Cast(context.get())->GetCaptureManager()->FinishCapture(); + } #endif // IMPELLER_DEBUG if (requires_blit_) { diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index bc9bfd78d2ffb..9d666838f69f9 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -186,6 +186,7 @@ display_list->Dispatch(impeller_dispatcher, sk_cull_rect); auto picture = impeller_dispatcher.EndRecordingAsPicture(); const bool reset_host_buffer = surface_frame.submit_info().frame_boundary; + surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary); return renderer->Render( std::move(surface), diff --git a/shell/gpu/gpu_surface_metal_impeller_unittests.mm b/shell/gpu/gpu_surface_metal_impeller_unittests.mm index 13ccbba3f6906..45c482cc1cca1 100644 --- a/shell/gpu/gpu_surface_metal_impeller_unittests.mm +++ b/shell/gpu/gpu_surface_metal_impeller_unittests.mm @@ -103,7 +103,7 @@ GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override auto context = CreateImpellerContext(); std::unique_ptr surface = - std::make_unique(delegate.get(), CreateImpellerContext()); + std::make_unique(delegate.get(), context); ASSERT_TRUE(surface->IsValid()); @@ -124,5 +124,33 @@ GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 1u); } +TEST(GPUSurfaceMetalImpeller, CreatesImpellerCaptureScope) { + auto delegate = std::make_shared(); + delegate->SetDevice(); + + auto context = CreateImpellerContext(); + + EXPECT_FALSE(context->GetCaptureManager()->CaptureScopeActive()); + + std::unique_ptr surface = + std::make_unique(delegate.get(), context); + auto frame_1 = surface->AcquireFrame(SkISize::Make(100, 100)); + frame_1->set_submit_info({.frame_boundary = false}); + + EXPECT_TRUE(context->GetCaptureManager()->CaptureScopeActive()); + + std::unique_ptr surface_2 = + std::make_unique(delegate.get(), context); + auto frame_2 = surface->AcquireFrame(SkISize::Make(100, 100)); + frame_2->set_submit_info({.frame_boundary = true}); + + EXPECT_TRUE(context->GetCaptureManager()->CaptureScopeActive()); + + ASSERT_TRUE(frame_1->Submit()); + EXPECT_TRUE(context->GetCaptureManager()->CaptureScopeActive()); + ASSERT_TRUE(frame_2->Submit()); + EXPECT_FALSE(context->GetCaptureManager()->CaptureScopeActive()); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/darwin/ios/framework/Source/FlutterMetalLayer.mm b/shell/platform/darwin/ios/framework/Source/FlutterMetalLayer.mm index 7a3a7d99e28ab..8a7551dca42c0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterMetalLayer.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterMetalLayer.mm @@ -424,15 +424,14 @@ - (void)returnTexture:(FlutterTexture*)texture { } + (BOOL)enabled { - static BOOL enabled = NO; + static BOOL enabled = YES; static BOOL didCheckInfoPlist = NO; if (!didCheckInfoPlist) { didCheckInfoPlist = YES; NSNumber* use_flutter_metal_layer = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"]; - if (use_flutter_metal_layer != nil && [use_flutter_metal_layer boolValue]) { - enabled = YES; - FML_LOG(WARNING) << "Using FlutterMetalLayer. This is an experimental feature."; + if (use_flutter_metal_layer != nil && ![use_flutter_metal_layer boolValue]) { + enabled = NO; } } return enabled; From ef5e381c1afe28704fc4619b8988990b42082cf3 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 24 Jul 2024 12:52:25 -0700 Subject: [PATCH 2/3] ++ --- impeller/renderer/backend/metal/context_mtl.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 473a6016723ec..d932bc3c3e828 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -406,7 +406,8 @@ new ContextMTL(device, command_queue, return command_queue_ip_; } -const std::shared_ptr ContextMTL::GetCaptureManager() const { +const std::shared_ptr +ContextMTL::GetCaptureManager() const { return capture_manager_; } From 4b68381771cd510feeaf20101f24ca5383de7785 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 24 Jul 2024 14:12:22 -0700 Subject: [PATCH 3/3] ++ --- impeller/renderer/backend/metal/context_mtl.mm | 2 ++ impeller/renderer/backend/metal/surface_mtl.mm | 9 +-------- shell/gpu/gpu_surface_metal_impeller.mm | 8 ++++++++ shell/gpu/gpu_surface_metal_impeller_unittests.mm | 2 ++ 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index d932bc3c3e828..03c6ea073950f 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -406,10 +406,12 @@ new ContextMTL(device, command_queue, return command_queue_ip_; } +#ifdef IMPELLER_DEBUG const std::shared_ptr ContextMTL::GetCaptureManager() const { return capture_manager_; } +#endif // IMPELLER_DEBUG ImpellerMetalCaptureManager::ImpellerMetalCaptureManager(id device) { current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager] diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 5c65b6cfea32c..e8cd572c5f5b1 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -197,14 +197,7 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; source_texture_(std::move(source_texture)), destination_texture_(std::move(destination_texture)), requires_blit_(requires_blit), - clip_rect_(clip_rect) { -#ifdef IMPELLER_DEBUG - auto mtl_context = context_.lock(); - if (mtl_context) { - ContextMTL::Cast(mtl_context.get())->GetCaptureManager()->StartCapture(); - } -#endif // IMPELLER_DEBUG -} + clip_rect_(clip_rect) {} // |Surface| SurfaceMTL::~SurfaceMTL() = default; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 9d666838f69f9..e14ac5c4a224b 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -106,6 +106,10 @@ last_texture_.reset([drawable.texture retain]); } +#ifdef IMPELLER_DEBUG + impeller::ContextMTL::Cast(*impeller_renderer_->GetContext()).GetCaptureManager()->StartCapture(); +#endif // IMPELLER_DEBUG + id last_texture = static_cast>(last_texture_); SurfaceFrame::SubmitCallback submit_callback = fml::MakeCopyable([damage = damage_, @@ -234,6 +238,10 @@ last_texture_.reset([mtl_texture retain]); } +#ifdef IMPELLER_DEBUG + impeller::ContextMTL::Cast(*impeller_renderer_->GetContext()).GetCaptureManager()->StartCapture(); +#endif // IMPELLER_DEBUG + SurfaceFrame::SubmitCallback submit_callback = fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, // damage = damage_, diff --git a/shell/gpu/gpu_surface_metal_impeller_unittests.mm b/shell/gpu/gpu_surface_metal_impeller_unittests.mm index 45c482cc1cca1..a5927ff035783 100644 --- a/shell/gpu/gpu_surface_metal_impeller_unittests.mm +++ b/shell/gpu/gpu_surface_metal_impeller_unittests.mm @@ -124,6 +124,7 @@ GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 1u); } +#ifdef IMPELLER_DEBUG TEST(GPUSurfaceMetalImpeller, CreatesImpellerCaptureScope) { auto delegate = std::make_shared(); delegate->SetDevice(); @@ -151,6 +152,7 @@ GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override ASSERT_TRUE(frame_2->Submit()); EXPECT_FALSE(context->GetCaptureManager()->CaptureScopeActive()); } +#endif // IMPELLER_DEBUG } // namespace testing } // namespace flutter