Skip to content

Commit e1259b8

Browse files
author
Jonah Williams
authored
[iOS] Switch to FlutterMetalLayer by default. (flutter#54086)
For this to work, we need to provide our own capture scope otherwise the default scope won't capture our commands. This is required as part of the work to switch to unmerged threads for PVs (flutter/engine#53826), as I can confirm @knopp 's observations that the performance is much worse with the default CAMetalLayer. Fixes flutter#140901
1 parent 511a8ef commit e1259b8

File tree

7 files changed

+120
-8
lines changed

7 files changed

+120
-8
lines changed

impeller/renderer/backend/metal/context_mtl.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,36 @@
3131

3232
namespace impeller {
3333

34+
/// @brief Creates and manages a Metal capture scope that supports frame capture
35+
/// when using the FlutterMetalLayer backed drawable.
36+
class ImpellerMetalCaptureManager {
37+
public:
38+
/// @brief Construct a new capture manager from the provided Metal device.
39+
explicit ImpellerMetalCaptureManager(id<MTLDevice> device);
40+
41+
~ImpellerMetalCaptureManager() = default;
42+
43+
/// Whether or not the Impeller capture scope is active.
44+
///
45+
/// This is distinct from whether or not there is a session recording the
46+
/// capture. That can be checked with `[[MTLCaptureManager
47+
/// sharedCaptureManager] isCapturing].`
48+
bool CaptureScopeActive() const;
49+
50+
/// @brief Begin a new capture scope, no-op if the scope has already started.
51+
void StartCapture();
52+
53+
/// @brief End the current capture scope.
54+
void FinishCapture();
55+
56+
private:
57+
id<MTLCaptureScope> current_capture_scope_;
58+
bool scope_active_ = false;
59+
60+
ImpellerMetalCaptureManager(const ImpellerMetalCaptureManager&) = default;
61+
ImpellerMetalCaptureManager(ImpellerMetalCaptureManager&&) = delete;
62+
};
63+
3464
class ContextMTL final : public Context,
3565
public BackendCast<ContextMTL, Context>,
3666
public std::enable_shared_from_this<ContextMTL> {
@@ -101,6 +131,8 @@ class ContextMTL final : public Context,
101131

102132
#ifdef IMPELLER_DEBUG
103133
std::shared_ptr<GPUTracerMTL> GetGPUTracer() const;
134+
135+
const std::shared_ptr<ImpellerMetalCaptureManager> GetCaptureManager() const;
104136
#endif // IMPELLER_DEBUG
105137

106138
// |Context|
@@ -125,12 +157,13 @@ class ContextMTL final : public Context,
125157
std::shared_ptr<AllocatorMTL> resource_allocator_;
126158
std::shared_ptr<const Capabilities> device_capabilities_;
127159
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch_;
128-
#ifdef IMPELLER_DEBUG
129-
std::shared_ptr<GPUTracerMTL> gpu_tracer_;
130-
#endif // IMPELLER_DEBUG
131160
std::deque<std::function<void()>> tasks_awaiting_gpu_;
132161
std::unique_ptr<SyncSwitchObserver> sync_switch_observer_;
133162
std::shared_ptr<CommandQueue> command_queue_ip_;
163+
#ifdef IMPELLER_DEBUG
164+
std::shared_ptr<GPUTracerMTL> gpu_tracer_;
165+
std::shared_ptr<ImpellerMetalCaptureManager> capture_manager_;
166+
#endif // IMPELLER_DEBUG
134167
bool is_valid_ = false;
135168

136169
ContextMTL(id<MTLDevice> device,

impeller/renderer/backend/metal/context_mtl.mm

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
#include "impeller/renderer/backend/metal/context_mtl.h"
6+
#include <Metal/Metal.h>
67

78
#include <memory>
89

@@ -134,6 +135,7 @@ static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
134135
command_queue_ip_ = std::make_shared<CommandQueue>();
135136
#ifdef IMPELLER_DEBUG
136137
gpu_tracer_ = std::make_shared<GPUTracerMTL>();
138+
capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
137139
#endif // IMPELLER_DEBUG
138140
is_valid_ = true;
139141
}
@@ -404,4 +406,35 @@ new ContextMTL(device, command_queue,
404406
return command_queue_ip_;
405407
}
406408

409+
#ifdef IMPELLER_DEBUG
410+
const std::shared_ptr<ImpellerMetalCaptureManager>
411+
ContextMTL::GetCaptureManager() const {
412+
return capture_manager_;
413+
}
414+
#endif // IMPELLER_DEBUG
415+
416+
ImpellerMetalCaptureManager::ImpellerMetalCaptureManager(id<MTLDevice> device) {
417+
current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager]
418+
newCaptureScopeWithDevice:device];
419+
[current_capture_scope_ setLabel:@"Impeller Frame"];
420+
}
421+
422+
bool ImpellerMetalCaptureManager::CaptureScopeActive() const {
423+
return scope_active_;
424+
}
425+
426+
void ImpellerMetalCaptureManager::StartCapture() {
427+
if (scope_active_) {
428+
return;
429+
}
430+
scope_active_ = true;
431+
[current_capture_scope_ beginScope];
432+
}
433+
434+
void ImpellerMetalCaptureManager::FinishCapture() {
435+
FML_DCHECK(scope_active_);
436+
[current_capture_scope_ endScope];
437+
scope_active_ = false;
438+
}
439+
407440
} // namespace impeller

impeller/renderer/backend/metal/surface_mtl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ class SurfaceMTL final : public Surface {
6161
// |Surface|
6262
bool Present() const override;
6363

64+
void SetFrameBoundary(bool frame_boundary) {
65+
frame_boundary_ = frame_boundary;
66+
}
67+
6468
private:
6569
std::weak_ptr<Context> context_;
6670
std::shared_ptr<Texture> resolve_texture_;
@@ -69,6 +73,7 @@ class SurfaceMTL final : public Surface {
6973
std::shared_ptr<Texture> destination_texture_;
7074
bool requires_blit_ = false;
7175
std::optional<IRect> clip_rect_;
76+
bool frame_boundary_ = false;
7277

7378
static bool ShouldPerformPartialRepaint(std::optional<IRect> damage_rect);
7479

impeller/renderer/backend/metal/surface_mtl.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ - (void)flutterPrepareForPresent:(nonnull id<MTLCommandBuffer>)commandBuffer;
231231

232232
#ifdef IMPELLER_DEBUG
233233
context->GetResourceAllocator()->DebugTraceMemoryStatistics();
234+
if (frame_boundary_) {
235+
ContextMTL::Cast(context.get())->GetCaptureManager()->FinishCapture();
236+
}
234237
#endif // IMPELLER_DEBUG
235238

236239
if (requires_blit_) {

shell/gpu/gpu_surface_metal_impeller.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@
106106
last_texture_.reset([drawable.texture retain]);
107107
}
108108

109+
#ifdef IMPELLER_DEBUG
110+
impeller::ContextMTL::Cast(*impeller_renderer_->GetContext()).GetCaptureManager()->StartCapture();
111+
#endif // IMPELLER_DEBUG
112+
109113
id<MTLTexture> last_texture = static_cast<id<MTLTexture>>(last_texture_);
110114
SurfaceFrame::SubmitCallback submit_callback =
111115
fml::MakeCopyable([damage = damage_,
@@ -186,6 +190,7 @@
186190
display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
187191
auto picture = impeller_dispatcher.EndRecordingAsPicture();
188192
const bool reset_host_buffer = surface_frame.submit_info().frame_boundary;
193+
surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary);
189194

190195
return renderer->Render(
191196
std::move(surface),
@@ -233,6 +238,10 @@
233238
last_texture_.reset([mtl_texture retain]);
234239
}
235240

241+
#ifdef IMPELLER_DEBUG
242+
impeller::ContextMTL::Cast(*impeller_renderer_->GetContext()).GetCaptureManager()->StartCapture();
243+
#endif // IMPELLER_DEBUG
244+
236245
SurfaceFrame::SubmitCallback submit_callback =
237246
fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
238247
damage = damage_,

shell/gpu/gpu_surface_metal_impeller_unittests.mm

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override
103103

104104
auto context = CreateImpellerContext();
105105
std::unique_ptr<Surface> surface =
106-
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), CreateImpellerContext());
106+
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), context);
107107

108108
ASSERT_TRUE(surface->IsValid());
109109

@@ -124,5 +124,35 @@ GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override
124124
EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 1u);
125125
}
126126

127+
#ifdef IMPELLER_DEBUG
128+
TEST(GPUSurfaceMetalImpeller, CreatesImpellerCaptureScope) {
129+
auto delegate = std::make_shared<TestGPUSurfaceMetalDelegate>();
130+
delegate->SetDevice();
131+
132+
auto context = CreateImpellerContext();
133+
134+
EXPECT_FALSE(context->GetCaptureManager()->CaptureScopeActive());
135+
136+
std::unique_ptr<Surface> surface =
137+
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), context);
138+
auto frame_1 = surface->AcquireFrame(SkISize::Make(100, 100));
139+
frame_1->set_submit_info({.frame_boundary = false});
140+
141+
EXPECT_TRUE(context->GetCaptureManager()->CaptureScopeActive());
142+
143+
std::unique_ptr<Surface> surface_2 =
144+
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), context);
145+
auto frame_2 = surface->AcquireFrame(SkISize::Make(100, 100));
146+
frame_2->set_submit_info({.frame_boundary = true});
147+
148+
EXPECT_TRUE(context->GetCaptureManager()->CaptureScopeActive());
149+
150+
ASSERT_TRUE(frame_1->Submit());
151+
EXPECT_TRUE(context->GetCaptureManager()->CaptureScopeActive());
152+
ASSERT_TRUE(frame_2->Submit());
153+
EXPECT_FALSE(context->GetCaptureManager()->CaptureScopeActive());
154+
}
155+
#endif // IMPELLER_DEBUG
156+
127157
} // namespace testing
128158
} // namespace flutter

shell/platform/darwin/ios/framework/Source/FlutterMetalLayer.mm

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,15 +424,14 @@ - (void)returnTexture:(FlutterTexture*)texture {
424424
}
425425

426426
+ (BOOL)enabled {
427-
static BOOL enabled = NO;
427+
static BOOL enabled = YES;
428428
static BOOL didCheckInfoPlist = NO;
429429
if (!didCheckInfoPlist) {
430430
didCheckInfoPlist = YES;
431431
NSNumber* use_flutter_metal_layer =
432432
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
433-
if (use_flutter_metal_layer != nil && [use_flutter_metal_layer boolValue]) {
434-
enabled = YES;
435-
FML_LOG(WARNING) << "Using FlutterMetalLayer. This is an experimental feature.";
433+
if (use_flutter_metal_layer != nil && ![use_flutter_metal_layer boolValue]) {
434+
enabled = NO;
436435
}
437436
}
438437
return enabled;

0 commit comments

Comments
 (0)