From 5bf6533538e034bbfecd39680186e559c8a90da3 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 25 Nov 2020 13:46:38 -0800 Subject: [PATCH] Introduce a delegate class for gpu metal rendering (#22611) --- ci/licenses_golden/licenses_flutter | 4 + shell/common/animator.cc | 6 +- shell/gpu/BUILD.gn | 2 + shell/gpu/gpu_surface_metal.h | 29 ++-- shell/gpu/gpu_surface_metal.mm | 133 +++++++++++------- shell/gpu/gpu_surface_metal_delegate.cc | 19 +++ shell/gpu/gpu_surface_metal_delegate.h | 100 +++++++++++++ shell/platform/darwin/graphics/BUILD.gn | 26 ++++ .../graphics/FlutterDarwinContextMetal.h | 57 ++++++++ .../graphics/FlutterDarwinContextMetal.mm | 70 +++++++++ shell/platform/darwin/ios/BUILD.gn | 19 ++- shell/platform/darwin/ios/ios_context.mm | 8 +- shell/platform/darwin/ios/ios_context_metal.h | 13 +- .../platform/darwin/ios/ios_context_metal.mm | 66 ++------- shell/platform/darwin/ios/ios_surface.mm | 8 +- shell/platform/darwin/ios/ios_surface_metal.h | 18 ++- .../platform/darwin/ios/ios_surface_metal.mm | 77 ++++++++-- .../darwin/ios/rendering_api_selection.mm | 14 +- 18 files changed, 505 insertions(+), 164 deletions(-) create mode 100644 shell/gpu/gpu_surface_metal_delegate.cc create mode 100644 shell/gpu/gpu_surface_metal_delegate.h create mode 100644 shell/platform/darwin/graphics/BUILD.gn create mode 100644 shell/platform/darwin/graphics/FlutterDarwinContextMetal.h create mode 100644 shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4dcdfaf9f9..bfa8e24845 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -673,6 +673,8 @@ FILE: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.cc FILE: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.h FILE: ../../../flutter/shell/gpu/gpu_surface_metal.h FILE: ../../../flutter/shell/gpu/gpu_surface_metal.mm +FILE: ../../../flutter/shell/gpu/gpu_surface_metal_delegate.cc +FILE: ../../../flutter/shell/gpu/gpu_surface_metal_delegate.h FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc FILE: ../../../flutter/shell/gpu/gpu_surface_software.h FILE: ../../../flutter/shell/gpu/gpu_surface_software_delegate.cc @@ -932,6 +934,8 @@ FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStan FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm +FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h +FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Flutter.podspec FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/Flutter.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 8cc8926ca4..cacac38c12 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -29,9 +29,9 @@ Animator::Animator(Delegate& delegate, last_vsync_start_time_(), last_frame_target_time_(), dart_frame_deadline_(0), -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL layer_tree_pipeline_(fml::MakeRefCounted(2)), -#else // FLUTTER_SHELL_ENABLE_METAL +#else // SHELL_ENABLE_METAL // TODO(dnfield): We should remove this logic and set the pipeline depth // back to 2 in this case. See // https://github.com/flutter/engine/pull/9132 for discussion. @@ -40,7 +40,7 @@ Animator::Animator(Delegate& delegate, task_runners.GetRasterTaskRunner() ? 1 : 2)), -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL pending_frame_semaphore_(1), frame_number_(1), paused_(false), diff --git a/shell/gpu/BUILD.gn b/shell/gpu/BUILD.gn index 606de7ed52..9687b94fbb 100644 --- a/shell/gpu/BUILD.gn +++ b/shell/gpu/BUILD.gn @@ -51,6 +51,8 @@ source_set("gpu_surface_metal") { sources = [ "gpu_surface_metal.h", "gpu_surface_metal.mm", + "gpu_surface_metal_delegate.cc", + "gpu_surface_metal_delegate.h", ] deps = gpu_common_deps diff --git a/shell/gpu/gpu_surface_metal.h b/shell/gpu/gpu_surface_metal.h index b56e4bafd6..0ef1b8c4a1 100644 --- a/shell/gpu/gpu_surface_metal.h +++ b/shell/gpu/gpu_surface_metal.h @@ -5,36 +5,31 @@ #ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ #define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ -#include - #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" -#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/mtl/GrMtlTypes.h" -@class CAMetalLayer; - namespace flutter { class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface { public: - GPUSurfaceMetal(fml::scoped_nsobject layer, - sk_sp context, - fml::scoped_nsprotocol> command_queue); + GPUSurfaceMetal(GPUSurfaceMetalDelegate* delegate, + sk_sp context); // |Surface| ~GPUSurfaceMetal(); - private: - fml::scoped_nsobject layer_; - sk_sp context_; - fml::scoped_nsprotocol> command_queue_; - GrMTLHandle next_drawable_ = nullptr; - // |Surface| bool IsValid() override; + private: + const GPUSurfaceMetalDelegate* delegate_; + const MTLRenderTargetType render_target_type_; + GrMTLHandle next_drawable_ = nullptr; + sk_sp context_; + // |Surface| std::unique_ptr AcquireFrame(const SkISize& size) override; @@ -47,6 +42,12 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface { // |Surface| std::unique_ptr MakeRenderContextCurrent() override; + std::unique_ptr AcquireFrameFromCAMetalLayer( + const SkISize& frame_info); + + std::unique_ptr AcquireFrameFromMTLTexture( + const SkISize& frame_info); + void ReleaseUnusedDrawableIfNecessary(); FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetal); diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 195631984e..b072bb5549 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -4,9 +4,12 @@ #include "flutter/shell/gpu/gpu_surface_metal.h" -#include +#import +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/ports/SkCFObject.h" @@ -15,17 +18,10 @@ static_assert(!__has_feature(objc_arc), "ARC must be disabled."); namespace flutter { -GPUSurfaceMetal::GPUSurfaceMetal(fml::scoped_nsobject layer, - sk_sp context, - fml::scoped_nsprotocol> command_queue) - : layer_(std::move(layer)), - context_(std::move(context)), - command_queue_(std::move(command_queue)) { - layer_.get().pixelFormat = MTLPixelFormatBGRA8Unorm; - // Flutter needs to read from the color attachment in cases where there are effects such as - // backdrop filters. - layer_.get().framebufferOnly = NO; -} +GPUSurfaceMetal::GPUSurfaceMetal(GPUSurfaceMetalDelegate* delegate, sk_sp context) + : delegate_(delegate), + render_target_type_(delegate->GetRenderTargetType()), + context_(std::move(context)) {} GPUSurfaceMetal::~GPUSurfaceMetal() { ReleaseUnusedDrawableIfNecessary(); @@ -33,7 +29,7 @@ GPUSurfaceMetal::~GPUSurfaceMetal() { // |Surface| bool GPUSurfaceMetal::IsValid() { - return layer_ && context_ && command_queue_; + return context_ != nullptr; } // |Surface| @@ -48,36 +44,91 @@ std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& frame return nullptr; } - const auto drawable_size = CGSizeMake(frame_size.width(), frame_size.height()); + switch (render_target_type_) { + case MTLRenderTargetType::kCAMetalLayer: + return AcquireFrameFromCAMetalLayer(frame_size); + case MTLRenderTargetType::kMTLTexture: + return AcquireFrameFromMTLTexture(frame_size); + default: + FML_CHECK(false) << "Unknown MTLRenderTargetType type."; + } + + return nullptr; +} - if (!CGSizeEqualToSize(drawable_size, layer_.get().drawableSize)) { - layer_.get().drawableSize = drawable_size; +std::unique_ptr GPUSurfaceMetal::AcquireFrameFromCAMetalLayer( + const SkISize& frame_info) { + auto layer = delegate_->GetCAMetalLayer(frame_info); + if (!layer) { + FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder."; + return nullptr; } ReleaseUnusedDrawableIfNecessary(); + sk_sp surface = + SkSurface::MakeFromCAMetalLayer(context_.get(), // context + layer, // layer + kTopLeft_GrSurfaceOrigin, // origin + 1, // sample count + kBGRA_8888_SkColorType, // color type + nullptr, // colorspace + nullptr, // surface properties + &next_drawable_ // drawable (transfer out) + ); + + if (!surface) { + FML_LOG(ERROR) << "Could not create the SkSurface from the CAMetalLayer."; + return nullptr; + } + + auto submit_callback = + fml::MakeCopyable([drawable = next_drawable_, delegate = delegate_]( + const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool { + TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit"); + if (canvas == nullptr) { + FML_DLOG(ERROR) << "Canvas not available."; + return false; + } + + canvas->flush(); - // When there are platform views in the scene, the drawable needs to be presented in the same - // transaction as the one created for platform views. When the drawable are being presented from - // the raster thread, there is no such transaction. - layer_.get().presentsWithTransaction = [[NSThread currentThread] isMainThread]; - - auto surface = SkSurface::MakeFromCAMetalLayer(context_.get(), // context - layer_.get(), // layer - kTopLeft_GrSurfaceOrigin, // origin - 1, // sample count - kBGRA_8888_SkColorType, // color type - nullptr, // colorspace - nullptr, // surface properties - &next_drawable_ // drawable (transfer out) - ); + if (!drawable) { + FML_DLOG(ERROR) << "Unable to obtain a metal drawable."; + return false; + } + + return delegate->PresentDrawable(drawable); + }); + + return std::make_unique(std::move(surface), true, submit_callback); +} + +std::unique_ptr GPUSurfaceMetal::AcquireFrameFromMTLTexture( + const SkISize& frame_info) { + GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info); + id mtl_texture = (id)(texture.texture); + + if (!mtl_texture) { + FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder."; + return nullptr; + } + + GrMtlTextureInfo info; + info.fTexture.reset([mtl_texture retain]); + GrBackendTexture backend_texture(frame_info.width(), frame_info.height(), GrMipmapped::kNo, info); + + sk_sp surface = + SkSurface::MakeFromBackendTexture(context_.get(), backend_texture, kTopLeft_GrSurfaceOrigin, + 1, kBGRA_8888_SkColorType, nullptr, nullptr); if (!surface) { FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture."; return nullptr; } - auto submit_callback = [this](const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool { - TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit"); + auto submit_callback = [texture_id = texture.texture_id, delegate = delegate_]( + const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool { + TRACE_EVENT0("flutter", "GPUSurfaceMetal::PresentTexture"); if (canvas == nullptr) { FML_DLOG(ERROR) << "Canvas not available."; return false; @@ -85,23 +136,7 @@ std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& frame canvas->flush(); - if (next_drawable_ == nullptr) { - FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface."; - return false; - } - - auto command_buffer = - fml::scoped_nsprotocol>([[command_queue_.get() commandBuffer] retain]); - - fml::scoped_nsprotocol> drawable( - reinterpret_cast>(next_drawable_)); - next_drawable_ = nullptr; - - [command_buffer.get() commit]; - [command_buffer.get() waitUntilScheduled]; - [drawable.get() present]; - - return true; + return delegate->PresentTexture(texture_id); }; return std::make_unique(std::move(surface), true, submit_callback); diff --git a/shell/gpu/gpu_surface_metal_delegate.cc b/shell/gpu/gpu_surface_metal_delegate.cc new file mode 100644 index 0000000000..427614e1b4 --- /dev/null +++ b/shell/gpu/gpu_surface_metal_delegate.cc @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" + +namespace flutter { + +GPUSurfaceMetalDelegate::GPUSurfaceMetalDelegate( + MTLRenderTargetType render_target_type) + : render_target_type_(render_target_type) {} + +GPUSurfaceMetalDelegate::~GPUSurfaceMetalDelegate() = default; + +MTLRenderTargetType GPUSurfaceMetalDelegate::GetRenderTargetType() { + return render_target_type_; +} + +} // namespace flutter diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h new file mode 100644 index 0000000000..aa88ecfda4 --- /dev/null +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -0,0 +1,100 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_DELEGATE_H_ +#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_DELEGATE_H_ + +#include + +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/mtl/GrMtlTypes.h" + +namespace flutter { + +// expected to be id +typedef void* GPUMTLDeviceHandle; + +// expected to be id +typedef void* GPUMTLCommandQueueHandle; + +// expected to be CAMetalLayer* +typedef void* GPUCAMetalLayerHandle; + +// expected to be id +typedef void* GPUMTLTextureHandle; + +struct GPUMTLTextureInfo { + intptr_t texture_id; + GPUMTLTextureHandle texture; +}; + +enum class MTLRenderTargetType { kMTLTexture, kCAMetalLayer }; + +//------------------------------------------------------------------------------ +/// @brief Interface implemented by all platform surfaces that can present +/// a metal backing store to the "screen". The GPU surface +/// abstraction (which abstracts the client rendering API) uses this +/// delegation pattern to tell the platform surface (which abstracts +/// how backing stores fulfilled by the selected client rendering +/// API end up on the "screen" on a particular platform) when the +/// rasterizer needs to allocate and present the software backing +/// store. +/// +/// @see |IOSurfaceMetal| and |EmbedderSurfaceMetal|. +/// +class GPUSurfaceMetalDelegate { + public: + //------------------------------------------------------------------------------ + /// @brief Construct a new GPUSurfaceMetalDelegate object with the specified + /// render_target type. + /// + /// @see |MTLRenderTargetType| + /// + explicit GPUSurfaceMetalDelegate(MTLRenderTargetType render_target); + + virtual ~GPUSurfaceMetalDelegate(); + + //------------------------------------------------------------------------------ + /// @brief Returns the handle to the CAMetalLayer to render to. This is only + /// called when the specifed render target type is `kCAMetalLayer`. + /// + virtual GPUCAMetalLayerHandle GetCAMetalLayer( + const SkISize& frame_info) const = 0; + + //------------------------------------------------------------------------------ + /// @brief Presents the drawable to the "screen". The drawable is obtained + /// from the CAMetalLayer that given by `GetCAMetalLayer` call. This is only + /// called when the specified render target type in `kCAMetalLayer`. + /// + /// @see |GPUSurfaceMetalDelegate::GetCAMetalLayer| + /// + virtual bool PresentDrawable(GrMTLHandle drawable) const = 0; + + //------------------------------------------------------------------------------ + /// @brief Returns the handle to the MTLTexture to render to. This is only + /// called when the specefied render target type is `kMTLTexture`. + /// + virtual GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const = 0; + + //------------------------------------------------------------------------------ + /// @brief Presents the texture with `texture_id` to the "screen". + /// `texture_id` corresponds to a texture that has been obtained by an earlier + /// call to `GetMTLTexture`. This is only called when the specefied render + /// target type is `kMTLTexture`. + /// + /// @see |GPUSurfaceMetalDelegate::GetMTLTexture| + /// + virtual bool PresentTexture(intptr_t texture_id) const = 0; + + MTLRenderTargetType GetRenderTargetType(); + + private: + const MTLRenderTargetType render_target_type_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_DELEGATE_H_ diff --git a/shell/platform/darwin/graphics/BUILD.gn b/shell/platform/darwin/graphics/BUILD.gn new file mode 100644 index 0000000000..5d1cd0a0e9 --- /dev/null +++ b/shell/platform/darwin/graphics/BUILD.gn @@ -0,0 +1,26 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +assert(is_ios || is_mac) + +import("//flutter/common/config.gni") + +source_set("graphics") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + + sources = [ + "FlutterDarwinContextMetal.h", + "FlutterDarwinContextMetal.mm", + ] + + deps = [ + "//flutter/common/graphics", + "//flutter/fml", + ] + + public_deps = [ "//third_party/skia" ] + + public_configs = [ "//flutter:config" ] +} diff --git a/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h b/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h new file mode 100644 index 0000000000..65ee99dfb8 --- /dev/null +++ b/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHELL_PLATFORM_DARWIN_GRAPHICS_DARWIN_CONTEXT_METAL_H_ +#define SHELL_PLATFORM_DARWIN_GRAPHICS_DARWIN_CONTEXT_METAL_H_ + +#import +#import + +#include "third_party/skia/include/gpu/GrDirectContext.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Provides skia GrContexts that are shared between iOS and macOS embeddings. + */ +@interface FlutterDarwinContextMetal : NSObject + +/** + * Initializes a FlutterDarwinContextMetal with the system default MTLDevice and a new + * MTLCommandQueue. + */ +- (instancetype)initWithDefaultMTLDevice; + +/** + * Initializes a FlutterDarwinContextMetal with provided MTLDevice and MTLCommandQueue. + */ +- (instancetype)initWithMTLDevice:(id)mtlDevice + commandQueue:(id)commandQueue; + +/** + * MTLDevice that is backing this context.s + */ +@property(nonatomic, readonly) id mtlDevice; + +/** + * MTLCommandQueue that is acquired from the `mtlDevice`. This queue is used both for rendering and + * resource related commands. + */ +@property(nonatomic, readonly) id mtlCommandQueue; + +/** + * Skia GrContext that is used for rendering. + */ +@property(nonatomic, readonly) sk_sp mainContext; + +/** + * Skia GrContext that is used for resources (uploading textures etc). + */ +@property(nonatomic, readonly) sk_sp resourceContext; + +@end + +NS_ASSUME_NONNULL_END + +#endif // SHELL_PLATFORM_DARWIN_GRAPHICS_DARWIN_CONTEXT_METAL_H_ diff --git a/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm b/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm new file mode 100644 index 0000000000..d609cb90f8 --- /dev/null +++ b/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h" + +#include "flutter/common/graphics/persistent_cache.h" +#include "flutter/fml/logging.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" + +static GrContextOptions CreateMetalGrContextOptions() { + GrContextOptions options = {}; + if (flutter::PersistentCache::cache_sksl()) { + options.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kSkSL; + } + flutter::PersistentCache::MarkStrategySet(); + options.fPersistentCache = flutter::PersistentCache::GetCacheForProcess(); + return options; +} + +@implementation FlutterDarwinContextMetal + +- (instancetype)initWithDefaultMTLDevice { + id mtlDevice = MTLCreateSystemDefaultDevice(); + return [self initWithMTLDevice:mtlDevice commandQueue:[mtlDevice newCommandQueue]]; +} + +- (instancetype)initWithMTLDevice:(id)mtlDevice + commandQueue:(id)commandQueue { + self = [super init]; + if (self != nil) { + _mtlDevice = mtlDevice; + + if (!_mtlDevice) { + FML_DLOG(ERROR) << "Could not acquire Metal device."; + [self release]; + return nil; + } + + _mtlCommandQueue = commandQueue; + + if (!_mtlCommandQueue) { + FML_DLOG(ERROR) << "Could not create Metal command queue."; + [self release]; + return nil; + } + + [_mtlCommandQueue setLabel:@"Flutter Main Queue"]; + + auto contextOptions = CreateMetalGrContextOptions(); + + // Skia expect arguments to `MakeMetal` transfer ownership of the reference in for release later + // when the GrDirectContext is collected. + _mainContext = + GrDirectContext::MakeMetal([_mtlDevice retain], [_mtlCommandQueue retain], contextOptions); + _resourceContext = + GrDirectContext::MakeMetal([_mtlDevice retain], [_mtlCommandQueue retain], contextOptions); + + if (!_mainContext || !_resourceContext) { + FML_DLOG(ERROR) << "Could not create Skia Metal contexts."; + [self release]; + return nil; + } + + _resourceContext->setResourceCacheLimits(0u, 0u); + } + return self; +} + +@end diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 897fe374b5..4087f88b67 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -42,6 +42,8 @@ source_set("flutter_framework_source") { cflags_objc = flutter_cflags_objc cflags_objcc = flutter_cflags_objcc + deps = [] + sources = [ "framework/Source/FlutterAppDelegate.mm", "framework/Source/FlutterBinaryMessengerRelay.mm", @@ -116,8 +118,6 @@ source_set("flutter_framework_source") { defines = [ "FLUTTER_FRAMEWORK=1" ] if (shell_enable_metal) { - defines += [ "FLUTTER_SHELL_ENABLE_METAL=1" ] - sources += [ "ios_context_metal.h", "ios_context_metal.mm", @@ -126,9 +126,11 @@ source_set("flutter_framework_source") { "ios_surface_metal.h", "ios_surface_metal.mm", ] + + deps += [ "//flutter/shell/platform/darwin/graphics" ] } - deps = [ + deps += [ ":ios_gpu_configuration", "//flutter/common", "//flutter/common/graphics", @@ -144,7 +146,10 @@ source_set("flutter_framework_source") { "//third_party/skia", ] - public_configs = [ "//flutter:config" ] + public_configs = [ + ":ios_gpu_configuration_config", + "//flutter:config", + ] libs = [ "AudioToolbox.framework", @@ -219,6 +224,7 @@ shared_library("ios_test_flutter") { ] deps = [ ":flutter_framework_source", + ":ios_gpu_configuration", ":ios_test_flutter_mrc", "//flutter/common:common", "//flutter/shell/platform/darwin/common:framework_shared", @@ -228,7 +234,10 @@ shared_library("ios_test_flutter") { "//third_party/rapidjson", "//third_party/skia", ] - public_configs = [ "//flutter:config" ] + public_configs = [ + ":ios_gpu_configuration_config", + "//flutter:config", + ] } shared_library("create_flutter_framework_dylib") { diff --git a/shell/platform/darwin/ios/ios_context.mm b/shell/platform/darwin/ios/ios_context.mm index 0472ba6fc8..3752235cbd 100644 --- a/shell/platform/darwin/ios/ios_context.mm +++ b/shell/platform/darwin/ios/ios_context.mm @@ -8,9 +8,9 @@ #import "flutter/shell/platform/darwin/ios/ios_context_gl.h" #import "flutter/shell/platform/darwin/ios/ios_context_software.h" -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL #import "flutter/shell/platform/darwin/ios/ios_context_metal.h" -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL namespace flutter { @@ -24,10 +24,10 @@ std::unique_ptr IOSContext::Create(IOSRenderingAPI rendering_api) { return std::make_unique(); case IOSRenderingAPI::kSoftware: return std::make_unique(); -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL case IOSRenderingAPI::kMetal: return std::make_unique(); -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL default: break; } diff --git a/shell/platform/darwin/ios/ios_context_metal.h b/shell/platform/darwin/ios/ios_context_metal.h index f1928528ac..35fecb0507 100644 --- a/shell/platform/darwin/ios/ios_context_metal.h +++ b/shell/platform/darwin/ios/ios_context_metal.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h" #import "flutter/shell/platform/darwin/ios/ios_context.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -21,21 +22,15 @@ class IOSContextMetal final : public IOSContext { ~IOSContextMetal(); - fml::scoped_nsprotocol> GetDevice() const; - - fml::scoped_nsprotocol> GetMainCommandQueue() const; - - fml::scoped_nsprotocol> GetResourceCommandQueue() const; + fml::scoped_nsobject GetDarwinContext() const; sk_sp GetMainContext() const; sk_sp GetResourceContext() const; private: - fml::scoped_nsprotocol> device_; - fml::scoped_nsprotocol> main_queue_; - sk_sp main_context_; - sk_sp resource_context_; + fml::scoped_nsobject darwin_context_metal_; + fml::scoped_nsprotocol> main_command_queue_; fml::CFRef texture_cache_; // |IOSContext| diff --git a/shell/platform/darwin/ios/ios_context_metal.mm b/shell/platform/darwin/ios/ios_context_metal.mm index 21a7f7e66f..1d24c325a8 100644 --- a/shell/platform/darwin/ios/ios_context_metal.mm +++ b/shell/platform/darwin/ios/ios_context_metal.mm @@ -6,58 +6,27 @@ #include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/logging.h" +#import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h" #import "flutter/shell/platform/darwin/ios/ios_external_texture_metal.h" #include "third_party/skia/include/gpu/GrContextOptions.h" namespace flutter { -static GrContextOptions CreateMetalGrContextOptions() { - GrContextOptions options = {}; - if (PersistentCache::cache_sksl()) { - options.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kSkSL; - } - PersistentCache::MarkStrategySet(); - options.fPersistentCache = PersistentCache::GetCacheForProcess(); - return options; -} - IOSContextMetal::IOSContextMetal() { - device_.reset([MTLCreateSystemDefaultDevice() retain]); - if (!device_) { - FML_DLOG(ERROR) << "Could not acquire Metal device."; - return; - } - - main_queue_.reset([device_ newCommandQueue]); - - if (!main_queue_) { - FML_DLOG(ERROR) << "Could not create Metal command queue."; - return; - } - - [main_queue_ setLabel:@"Flutter Main Queue"]; - - const auto& context_options = CreateMetalGrContextOptions(); - - // Skia expect arguments to `MakeMetal` transfer ownership of the reference in for release later - // when the GrDirectContext is collected. - main_context_ = - GrDirectContext::MakeMetal([device_ retain], [main_queue_ retain], context_options); - resource_context_ = - GrDirectContext::MakeMetal([device_ retain], [main_queue_ retain], context_options); + darwin_context_metal_ = fml::scoped_nsobject{ + [[[FlutterDarwinContextMetal alloc] initWithDefaultMTLDevice] retain]}; - if (!main_context_ || !resource_context_) { - FML_DLOG(ERROR) << "Could not create Skia Metal contexts."; + if (!darwin_context_metal_) { return; } - resource_context_->setResourceCacheLimits(0u, 0u); + main_command_queue_.reset([darwin_context_metal_.get().mtlCommandQueue retain]); CVMetalTextureCacheRef texture_cache_raw = NULL; auto cv_return = CVMetalTextureCacheCreate(kCFAllocatorDefault, // allocator - NULL, // cache attributes (NULL default) - device_.get(), // metal device - NULL, // texture attributes (NULL default) + NULL, // cache attributes (NULL default) + darwin_context_metal_.get().mtlDevice, // metal device + NULL, // texture attributes (NULL default) &texture_cache_raw // [out] cache ); if (cv_return != kCVReturnSuccess) { @@ -69,30 +38,21 @@ IOSContextMetal::IOSContextMetal() { IOSContextMetal::~IOSContextMetal() = default; -fml::scoped_nsprotocol> IOSContextMetal::GetDevice() const { - return device_; -} - -fml::scoped_nsprotocol> IOSContextMetal::GetMainCommandQueue() const { - return main_queue_; -} - -fml::scoped_nsprotocol> IOSContextMetal::GetResourceCommandQueue() const { - // TODO(52150): Create a dedicated resource queue once multiple queues are supported in Skia. - return main_queue_; +fml::scoped_nsobject IOSContextMetal::GetDarwinContext() const { + return darwin_context_metal_; } sk_sp IOSContextMetal::GetMainContext() const { - return main_context_; + return darwin_context_metal_.get().mainContext; } sk_sp IOSContextMetal::GetResourceContext() const { - return resource_context_; + return darwin_context_metal_.get().resourceContext; } // |IOSContext| sk_sp IOSContextMetal::CreateResourceContext() { - return resource_context_; + return darwin_context_metal_.get().resourceContext; } // |IOSContext| diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index 192d2b208f..e0cc09760b 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -9,9 +9,9 @@ #include "flutter/shell/platform/darwin/ios/rendering_api_selection.h" -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL #import "flutter/shell/platform/darwin/ios/ios_surface_metal.h" -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL namespace flutter { @@ -28,7 +28,7 @@ std::unique_ptr IOSSurface::Create(std::shared_ptr conte ); } -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL if (@available(iOS METAL_IOS_VERSION_BASELINE, *)) { if ([layer.get() isKindOfClass:[CAMetalLayer class]]) { return std::make_unique( @@ -38,7 +38,7 @@ std::unique_ptr IOSSurface::Create(std::shared_ptr conte ); } } -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL return std::make_unique(std::move(layer), // layer std::move(context) // context diff --git a/shell/platform/darwin/ios/ios_surface_metal.h b/shell/platform/darwin/ios/ios_surface_metal.h index bdf80700dd..2ebcfee383 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.h +++ b/shell/platform/darwin/ios/ios_surface_metal.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_METAL_H_ #include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" #include "third_party/skia/include/gpu/mtl/GrMtlTypes.h" @@ -13,7 +14,8 @@ namespace flutter { -class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetal final : public IOSSurface { +class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetal final : public IOSSurface, + public GPUSurfaceMetalDelegate { public: IOSSurfaceMetal(fml::scoped_nsobject layer, std::shared_ptr context); @@ -22,6 +24,8 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetal final : public IOSSurface private: fml::scoped_nsobject layer_; + id device_; + id command_queue_; bool is_valid_ = false; // |IOSSurface| @@ -33,6 +37,18 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetal final : public IOSSurface // |IOSSurface| std::unique_ptr CreateGPUSurface(GrDirectContext* gr_context) override; + // |GPUSurfaceMetalDelegate| + GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize& frame_info) const override; + + // |GPUSurfaceMetalDelegate| + bool PresentDrawable(GrMTLHandle drawable) const override; + + // |GPUSurfaceMetalDelegate| + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; + + // |GPUSurfaceMetalDelegate| + bool PresentTexture(intptr_t texture_id) const override; + FML_DISALLOW_COPY_AND_ASSIGN(IOSSurfaceMetal); }; diff --git a/shell/platform/darwin/ios/ios_surface_metal.mm b/shell/platform/darwin/ios/ios_surface_metal.mm index 2d527062bb..176f50313b 100644 --- a/shell/platform/darwin/ios/ios_surface_metal.mm +++ b/shell/platform/darwin/ios/ios_surface_metal.mm @@ -5,7 +5,8 @@ #import "flutter/shell/platform/darwin/ios/ios_surface_metal.h" #include "flutter/shell/gpu/gpu_surface_metal.h" -#import "flutter/shell/platform/darwin/ios/ios_context_metal.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" +#include "flutter/shell/platform/darwin/ios/ios_context_metal.h" namespace flutter { @@ -15,17 +16,14 @@ static IOSContextMetal* CastToMetalContext(const std::shared_ptr& co IOSSurfaceMetal::IOSSurfaceMetal(fml::scoped_nsobject layer, std::shared_ptr context) - : IOSSurface(std::move(context)), layer_(std::move(layer)) { - if (!layer_) { - return; - } - + : IOSSurface(std::move(context)), + GPUSurfaceMetalDelegate(MTLRenderTargetType::kCAMetalLayer), + layer_(std::move(layer)) { + is_valid_ = layer_; auto metal_context = CastToMetalContext(GetContext()); - - layer_.get().device = metal_context->GetDevice().get(); - layer_.get().presentsWithTransaction = YES; - - is_valid_ = true; + auto darwin_context = metal_context->GetDarwinContext().get(); + command_queue_ = darwin_context.mtlCommandQueue; + device_ = darwin_context.mtlDevice; } // |IOSSurface| @@ -44,11 +42,60 @@ void IOSSurfaceMetal::UpdateStorageSizeIfNecessary() { // |IOSSurface| std::unique_ptr IOSSurfaceMetal::CreateGPUSurface(GrDirectContext* /* unused */) { auto metal_context = CastToMetalContext(GetContext()); - - return std::make_unique(layer_, // layer - metal_context->GetMainContext(), // context - metal_context->GetMainCommandQueue() // command queue + return std::make_unique(this, // layer + metal_context->GetMainContext() // context ); } +// |GPUSurfaceMetalDelegate| +GPUCAMetalLayerHandle IOSSurfaceMetal::GetCAMetalLayer(const SkISize& frame_info) const { + CAMetalLayer* layer = layer_.get(); + layer.device = device_; + + layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + // Flutter needs to read from the color attachment in cases where there are effects such as + // backdrop filters. + layer.framebufferOnly = NO; + + const auto drawable_size = CGSizeMake(frame_info.width(), frame_info.height()); + if (!CGSizeEqualToSize(drawable_size, layer.drawableSize)) { + layer.drawableSize = drawable_size; + } + + // When there are platform views in the scene, the drawable needs to be presented in the same + // transaction as the one created for platform views. When the drawable are being presented from + // the raster thread, there is no such transaction. + layer.presentsWithTransaction = [[NSThread currentThread] isMainThread]; + + return layer; +} + +// |GPUSurfaceMetalDelegate| +bool IOSSurfaceMetal::PresentDrawable(GrMTLHandle drawable) const { + if (drawable == nullptr) { + FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface."; + return false; + } + + auto command_buffer = + fml::scoped_nsprotocol>([[command_queue_ commandBuffer] retain]); + [command_buffer.get() commit]; + [command_buffer.get() waitUntilScheduled]; + + [reinterpret_cast>(drawable) present]; + return true; +} + +// |GPUSurfaceMetalDelegate| +GPUMTLTextureInfo IOSSurfaceMetal::GetMTLTexture(const SkISize& frame_info) const { + FML_CHECK(false) << "render to texture not supported on ios"; + return {.texture_id = -1, .texture = nullptr}; +} + +// |GPUSurfaceMetalDelegate| +bool IOSSurfaceMetal::PresentTexture(intptr_t texture_id) const { + FML_CHECK(false) << "render to texture not supported on ios"; + return false; +} + } // namespace flutter diff --git a/shell/platform/darwin/ios/rendering_api_selection.mm b/shell/platform/darwin/ios/rendering_api_selection.mm index 2cf23bcccd..b2472df89e 100644 --- a/shell/platform/darwin/ios/rendering_api_selection.mm +++ b/shell/platform/darwin/ios/rendering_api_selection.mm @@ -6,17 +6,17 @@ #include #include -#include -#if FLUTTER_SHELL_ENABLE_METAL +#import +#if SHELL_ENABLE_METAL #include -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL #import #include "flutter/fml/logging.h" namespace flutter { -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL bool ShouldUseMetalRenderer() { bool ios_version_supports_metal = false; if (@available(iOS METAL_IOS_VERSION_BASELINE, *)) { @@ -25,7 +25,7 @@ bool ShouldUseMetalRenderer() { } return ios_version_supports_metal; } -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL IOSRenderingAPI GetRenderingAPIForProcess(bool force_software) { #if TARGET_OS_SIMULATOR @@ -39,12 +39,12 @@ IOSRenderingAPI GetRenderingAPIForProcess(bool force_software) { } #endif // TARGET_OS_SIMULATOR -#if FLUTTER_SHELL_ENABLE_METAL +#if SHELL_ENABLE_METAL static bool should_use_metal = ShouldUseMetalRenderer(); if (should_use_metal) { return IOSRenderingAPI::kMetal; } -#endif // FLUTTER_SHELL_ENABLE_METAL +#endif // SHELL_ENABLE_METAL // OpenGL will be emulated using software rendering by Apple on the simulator, so we use the // Skia software rendering since it performs a little better than the emulated OpenGL. -- GitLab