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..03c6ea073950f 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,35 @@ 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] + 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..e8cd572c5f5b1 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -231,6 +231,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..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_, @@ -186,6 +190,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), @@ -233,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 13ccbba3f6906..a5927ff035783 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,35 @@ 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(); + + 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()); +} +#endif // IMPELLER_DEBUG + } // 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;