[iOS] Switch to FlutterMetalLayer by default. (flutter/engine#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 (https://github.com/flutter/engine/pull/53826), as I can confirm @knopp 's observations that the performance is much worse with the default CAMetalLayer. Fixes https://github.com/flutter/flutter/issues/140901
This commit is contained in:
parent
274cf5f243
commit
80fd1f7fbf
@ -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<MTLDevice> 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<MTLCaptureScope> current_capture_scope_;
|
||||
bool scope_active_ = false;
|
||||
|
||||
ImpellerMetalCaptureManager(const ImpellerMetalCaptureManager&) = default;
|
||||
ImpellerMetalCaptureManager(ImpellerMetalCaptureManager&&) = delete;
|
||||
};
|
||||
|
||||
class ContextMTL final : public Context,
|
||||
public BackendCast<ContextMTL, Context>,
|
||||
public std::enable_shared_from_this<ContextMTL> {
|
||||
@ -101,6 +131,8 @@ class ContextMTL final : public Context,
|
||||
|
||||
#ifdef IMPELLER_DEBUG
|
||||
std::shared_ptr<GPUTracerMTL> GetGPUTracer() const;
|
||||
|
||||
const std::shared_ptr<ImpellerMetalCaptureManager> GetCaptureManager() const;
|
||||
#endif // IMPELLER_DEBUG
|
||||
|
||||
// |Context|
|
||||
@ -125,12 +157,13 @@ class ContextMTL final : public Context,
|
||||
std::shared_ptr<AllocatorMTL> resource_allocator_;
|
||||
std::shared_ptr<const Capabilities> device_capabilities_;
|
||||
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch_;
|
||||
#ifdef IMPELLER_DEBUG
|
||||
std::shared_ptr<GPUTracerMTL> gpu_tracer_;
|
||||
#endif // IMPELLER_DEBUG
|
||||
std::deque<std::function<void()>> tasks_awaiting_gpu_;
|
||||
std::unique_ptr<SyncSwitchObserver> sync_switch_observer_;
|
||||
std::shared_ptr<CommandQueue> command_queue_ip_;
|
||||
#ifdef IMPELLER_DEBUG
|
||||
std::shared_ptr<GPUTracerMTL> gpu_tracer_;
|
||||
std::shared_ptr<ImpellerMetalCaptureManager> capture_manager_;
|
||||
#endif // IMPELLER_DEBUG
|
||||
bool is_valid_ = false;
|
||||
|
||||
ContextMTL(id<MTLDevice> device,
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/renderer/backend/metal/context_mtl.h"
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -134,6 +135,7 @@ ContextMTL::ContextMTL(
|
||||
command_queue_ip_ = std::make_shared<CommandQueue>();
|
||||
#ifdef IMPELLER_DEBUG
|
||||
gpu_tracer_ = std::make_shared<GPUTracerMTL>();
|
||||
capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
|
||||
#endif // IMPELLER_DEBUG
|
||||
is_valid_ = true;
|
||||
}
|
||||
@ -404,4 +406,35 @@ std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
|
||||
return command_queue_ip_;
|
||||
}
|
||||
|
||||
#ifdef IMPELLER_DEBUG
|
||||
const std::shared_ptr<ImpellerMetalCaptureManager>
|
||||
ContextMTL::GetCaptureManager() const {
|
||||
return capture_manager_;
|
||||
}
|
||||
#endif // IMPELLER_DEBUG
|
||||
|
||||
ImpellerMetalCaptureManager::ImpellerMetalCaptureManager(id<MTLDevice> 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
|
||||
|
@ -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> context_;
|
||||
std::shared_ptr<Texture> resolve_texture_;
|
||||
@ -69,6 +73,7 @@ class SurfaceMTL final : public Surface {
|
||||
std::shared_ptr<Texture> destination_texture_;
|
||||
bool requires_blit_ = false;
|
||||
std::optional<IRect> clip_rect_;
|
||||
bool frame_boundary_ = false;
|
||||
|
||||
static bool ShouldPerformPartialRepaint(std::optional<IRect> damage_rect);
|
||||
|
||||
|
@ -231,6 +231,9 @@ bool SurfaceMTL::Present() const {
|
||||
|
||||
#ifdef IMPELLER_DEBUG
|
||||
context->GetResourceAllocator()->DebugTraceMemoryStatistics();
|
||||
if (frame_boundary_) {
|
||||
ContextMTL::Cast(context.get())->GetCaptureManager()->FinishCapture();
|
||||
}
|
||||
#endif // IMPELLER_DEBUG
|
||||
|
||||
if (requires_blit_) {
|
||||
|
@ -106,6 +106,10 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
|
||||
last_texture_.reset([drawable.texture retain]);
|
||||
}
|
||||
|
||||
#ifdef IMPELLER_DEBUG
|
||||
impeller::ContextMTL::Cast(*impeller_renderer_->GetContext()).GetCaptureManager()->StartCapture();
|
||||
#endif // IMPELLER_DEBUG
|
||||
|
||||
id<MTLTexture> last_texture = static_cast<id<MTLTexture>>(last_texture_);
|
||||
SurfaceFrame::SubmitCallback submit_callback =
|
||||
fml::MakeCopyable([damage = damage_,
|
||||
@ -186,6 +190,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
|
||||
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 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur
|
||||
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_,
|
||||
|
@ -103,7 +103,7 @@ TEST(GPUSurfaceMetalImpeller, ResetHostBufferBasedOnFrameBoundary) {
|
||||
|
||||
auto context = CreateImpellerContext();
|
||||
std::unique_ptr<Surface> surface =
|
||||
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), CreateImpellerContext());
|
||||
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), context);
|
||||
|
||||
ASSERT_TRUE(surface->IsValid());
|
||||
|
||||
@ -124,5 +124,35 @@ TEST(GPUSurfaceMetalImpeller, ResetHostBufferBasedOnFrameBoundary) {
|
||||
EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 1u);
|
||||
}
|
||||
|
||||
#ifdef IMPELLER_DEBUG
|
||||
TEST(GPUSurfaceMetalImpeller, CreatesImpellerCaptureScope) {
|
||||
auto delegate = std::make_shared<TestGPUSurfaceMetalDelegate>();
|
||||
delegate->SetDevice();
|
||||
|
||||
auto context = CreateImpellerContext();
|
||||
|
||||
EXPECT_FALSE(context->GetCaptureManager()->CaptureScopeActive());
|
||||
|
||||
std::unique_ptr<Surface> surface =
|
||||
std::make_unique<GPUSurfaceMetalImpeller>(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> surface_2 =
|
||||
std::make_unique<GPUSurfaceMetalImpeller>(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
|
||||
|
@ -424,15 +424,14 @@ extern CFTimeInterval display_link_target;
|
||||
}
|
||||
|
||||
+ (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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user