[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 {
|
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,
|
class ContextMTL final : public Context,
|
||||||
public BackendCast<ContextMTL, Context>,
|
public BackendCast<ContextMTL, Context>,
|
||||||
public std::enable_shared_from_this<ContextMTL> {
|
public std::enable_shared_from_this<ContextMTL> {
|
||||||
@ -101,6 +131,8 @@ class ContextMTL final : public Context,
|
|||||||
|
|
||||||
#ifdef IMPELLER_DEBUG
|
#ifdef IMPELLER_DEBUG
|
||||||
std::shared_ptr<GPUTracerMTL> GetGPUTracer() const;
|
std::shared_ptr<GPUTracerMTL> GetGPUTracer() const;
|
||||||
|
|
||||||
|
const std::shared_ptr<ImpellerMetalCaptureManager> GetCaptureManager() const;
|
||||||
#endif // IMPELLER_DEBUG
|
#endif // IMPELLER_DEBUG
|
||||||
|
|
||||||
// |Context|
|
// |Context|
|
||||||
@ -125,12 +157,13 @@ class ContextMTL final : public Context,
|
|||||||
std::shared_ptr<AllocatorMTL> resource_allocator_;
|
std::shared_ptr<AllocatorMTL> resource_allocator_;
|
||||||
std::shared_ptr<const Capabilities> device_capabilities_;
|
std::shared_ptr<const Capabilities> device_capabilities_;
|
||||||
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch_;
|
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::deque<std::function<void()>> tasks_awaiting_gpu_;
|
||||||
std::unique_ptr<SyncSwitchObserver> sync_switch_observer_;
|
std::unique_ptr<SyncSwitchObserver> sync_switch_observer_;
|
||||||
std::shared_ptr<CommandQueue> command_queue_ip_;
|
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;
|
bool is_valid_ = false;
|
||||||
|
|
||||||
ContextMTL(id<MTLDevice> device,
|
ContextMTL(id<MTLDevice> device,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#include "impeller/renderer/backend/metal/context_mtl.h"
|
#include "impeller/renderer/backend/metal/context_mtl.h"
|
||||||
|
#include <Metal/Metal.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -134,6 +135,7 @@ ContextMTL::ContextMTL(
|
|||||||
command_queue_ip_ = std::make_shared<CommandQueue>();
|
command_queue_ip_ = std::make_shared<CommandQueue>();
|
||||||
#ifdef IMPELLER_DEBUG
|
#ifdef IMPELLER_DEBUG
|
||||||
gpu_tracer_ = std::make_shared<GPUTracerMTL>();
|
gpu_tracer_ = std::make_shared<GPUTracerMTL>();
|
||||||
|
capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
|
||||||
#endif // IMPELLER_DEBUG
|
#endif // IMPELLER_DEBUG
|
||||||
is_valid_ = true;
|
is_valid_ = true;
|
||||||
}
|
}
|
||||||
@ -404,4 +406,35 @@ std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
|
|||||||
return command_queue_ip_;
|
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
|
} // namespace impeller
|
||||||
|
@ -61,6 +61,10 @@ class SurfaceMTL final : public Surface {
|
|||||||
// |Surface|
|
// |Surface|
|
||||||
bool Present() const override;
|
bool Present() const override;
|
||||||
|
|
||||||
|
void SetFrameBoundary(bool frame_boundary) {
|
||||||
|
frame_boundary_ = frame_boundary;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::weak_ptr<Context> context_;
|
std::weak_ptr<Context> context_;
|
||||||
std::shared_ptr<Texture> resolve_texture_;
|
std::shared_ptr<Texture> resolve_texture_;
|
||||||
@ -69,6 +73,7 @@ class SurfaceMTL final : public Surface {
|
|||||||
std::shared_ptr<Texture> destination_texture_;
|
std::shared_ptr<Texture> destination_texture_;
|
||||||
bool requires_blit_ = false;
|
bool requires_blit_ = false;
|
||||||
std::optional<IRect> clip_rect_;
|
std::optional<IRect> clip_rect_;
|
||||||
|
bool frame_boundary_ = false;
|
||||||
|
|
||||||
static bool ShouldPerformPartialRepaint(std::optional<IRect> damage_rect);
|
static bool ShouldPerformPartialRepaint(std::optional<IRect> damage_rect);
|
||||||
|
|
||||||
|
@ -231,6 +231,9 @@ bool SurfaceMTL::Present() const {
|
|||||||
|
|
||||||
#ifdef IMPELLER_DEBUG
|
#ifdef IMPELLER_DEBUG
|
||||||
context->GetResourceAllocator()->DebugTraceMemoryStatistics();
|
context->GetResourceAllocator()->DebugTraceMemoryStatistics();
|
||||||
|
if (frame_boundary_) {
|
||||||
|
ContextMTL::Cast(context.get())->GetCaptureManager()->FinishCapture();
|
||||||
|
}
|
||||||
#endif // IMPELLER_DEBUG
|
#endif // IMPELLER_DEBUG
|
||||||
|
|
||||||
if (requires_blit_) {
|
if (requires_blit_) {
|
||||||
|
@ -106,6 +106,10 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
|
|||||||
last_texture_.reset([drawable.texture retain]);
|
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_);
|
id<MTLTexture> last_texture = static_cast<id<MTLTexture>>(last_texture_);
|
||||||
SurfaceFrame::SubmitCallback submit_callback =
|
SurfaceFrame::SubmitCallback submit_callback =
|
||||||
fml::MakeCopyable([damage = damage_,
|
fml::MakeCopyable([damage = damage_,
|
||||||
@ -186,6 +190,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
|
|||||||
display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
|
display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
|
||||||
auto picture = impeller_dispatcher.EndRecordingAsPicture();
|
auto picture = impeller_dispatcher.EndRecordingAsPicture();
|
||||||
const bool reset_host_buffer = surface_frame.submit_info().frame_boundary;
|
const bool reset_host_buffer = surface_frame.submit_info().frame_boundary;
|
||||||
|
surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary);
|
||||||
|
|
||||||
return renderer->Render(
|
return renderer->Render(
|
||||||
std::move(surface),
|
std::move(surface),
|
||||||
@ -233,6 +238,10 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur
|
|||||||
last_texture_.reset([mtl_texture retain]);
|
last_texture_.reset([mtl_texture retain]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef IMPELLER_DEBUG
|
||||||
|
impeller::ContextMTL::Cast(*impeller_renderer_->GetContext()).GetCaptureManager()->StartCapture();
|
||||||
|
#endif // IMPELLER_DEBUG
|
||||||
|
|
||||||
SurfaceFrame::SubmitCallback submit_callback =
|
SurfaceFrame::SubmitCallback submit_callback =
|
||||||
fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
|
fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
|
||||||
damage = damage_,
|
damage = damage_,
|
||||||
|
@ -103,7 +103,7 @@ TEST(GPUSurfaceMetalImpeller, ResetHostBufferBasedOnFrameBoundary) {
|
|||||||
|
|
||||||
auto context = CreateImpellerContext();
|
auto context = CreateImpellerContext();
|
||||||
std::unique_ptr<Surface> surface =
|
std::unique_ptr<Surface> surface =
|
||||||
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), CreateImpellerContext());
|
std::make_unique<GPUSurfaceMetalImpeller>(delegate.get(), context);
|
||||||
|
|
||||||
ASSERT_TRUE(surface->IsValid());
|
ASSERT_TRUE(surface->IsValid());
|
||||||
|
|
||||||
@ -124,5 +124,35 @@ TEST(GPUSurfaceMetalImpeller, ResetHostBufferBasedOnFrameBoundary) {
|
|||||||
EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 1u);
|
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 testing
|
||||||
} // namespace flutter
|
} // namespace flutter
|
||||||
|
@ -424,15 +424,14 @@ extern CFTimeInterval display_link_target;
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)enabled {
|
+ (BOOL)enabled {
|
||||||
static BOOL enabled = NO;
|
static BOOL enabled = YES;
|
||||||
static BOOL didCheckInfoPlist = NO;
|
static BOOL didCheckInfoPlist = NO;
|
||||||
if (!didCheckInfoPlist) {
|
if (!didCheckInfoPlist) {
|
||||||
didCheckInfoPlist = YES;
|
didCheckInfoPlist = YES;
|
||||||
NSNumber* use_flutter_metal_layer =
|
NSNumber* use_flutter_metal_layer =
|
||||||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
|
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
|
||||||
if (use_flutter_metal_layer != nil && [use_flutter_metal_layer boolValue]) {
|
if (use_flutter_metal_layer != nil && ![use_flutter_metal_layer boolValue]) {
|
||||||
enabled = YES;
|
enabled = NO;
|
||||||
FML_LOG(WARNING) << "Using FlutterMetalLayer. This is an experimental feature.";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return enabled;
|
return enabled;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user