Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ DEF_SWITCH(EnableSoftwareRendering,
"enable-software-rendering",
"Enable rendering using the Skia software backend. This is useful "
"when testing Flutter on emulators. By default, Flutter will "
"attempt to either use OpenGL or Vulkan.")
"attempt to either use OpenGL, Metal, or Vulkan.")
DEF_SWITCH(SkiaDeterministicRendering,
"skia-deterministic-rendering",
"Skips the call to SkGraphics::Init(), thus avoiding swapping out "
Expand Down
3 changes: 2 additions & 1 deletion shell/gpu/gpu_surface_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
#include "flutter/shell/gpu/gpu_surface_delegate.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/mtl/GrMtlTypes.h"

@class CAMetalLayer;

namespace flutter {

class GPUSurfaceMetal : public Surface {
class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface {
public:
GPUSurfaceMetal(GPUSurfaceDelegate* delegate,
fml::scoped_nsobject<CAMetalLayer> layer,
Expand Down
5 changes: 4 additions & 1 deletion shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ - (BOOL)createShell:(NSString*)entrypoint
self.initialRoute = initialRoute;

auto settings = [_dartProject.get() settings];
FlutterView.forceSoftwareRendering = settings.enable_software_rendering;

auto platformData = [_dartProject.get() defaultPlatformData];

if (libraryURI) {
Expand Down Expand Up @@ -513,7 +515,8 @@ - (BOOL)createShell:(NSString*)entrypoint
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
[](flutter::Shell& shell) {
return std::make_unique<flutter::PlatformViewIOS>(
shell, flutter::GetRenderingAPIForProcess(), shell.GetTaskRunners());
shell, flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering),
shell.GetTaskRunners());
};

flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
Expand Down
3 changes: 2 additions & 1 deletion shell/platform/darwin/ios/framework/Source/FlutterView.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
asBase64Encoded:(BOOL)base64Encode;

- (flutter::FlutterPlatformViewsController*)platformViewsController;

@end

@interface FlutterView : UIView
Expand All @@ -35,6 +34,8 @@
opaque:(BOOL)opaque NS_DESIGNATED_INITIALIZER;
- (std::unique_ptr<flutter::IOSSurface>)createSurface:(std::shared_ptr<flutter::IOSContext>)context;

// Set by FlutterEngine or FlutterViewController to override software rendering.
@property(class, nonatomic) BOOL forceSoftwareRendering;
@end

#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_VIEW_H_
13 changes: 12 additions & 1 deletion shell/platform/darwin/ios/framework/Source/FlutterView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,19 @@ - (void)layoutSubviews {
[super layoutSubviews];
}

static BOOL _forceSoftwareRendering;

+ (BOOL)forceSoftwareRendering {
return _forceSoftwareRendering;
}

+ (void)setForceSoftwareRendering:(BOOL)forceSoftwareRendering {
_forceSoftwareRendering = forceSoftwareRendering;
}

+ (Class)layerClass {
return flutter::GetCoreAnimationLayerClassForRenderingAPI();
return flutter::GetCoreAnimationLayerClassForRenderingAPI(
flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering));
}

- (std::unique_ptr<flutter::IOSSurface>)createSurface:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ - (instancetype)init {

- (void)sharedSetupWithProject:(nullable FlutterDartProject*)project
initialRoute:(nullable NSString*)initialRoute {
// Need the project to get settings for the view. Initializing it here means
// the Engine class won't initialize it later.
if (!project) {
project = [[[FlutterDartProject alloc] init] autorelease];
}
FlutterView.forceSoftwareRendering = project.settings.enable_software_rendering;
auto engine = fml::scoped_nsobject<FlutterEngine>{[[FlutterEngine alloc]
initWithName:@"io.flutter"
project:project
Expand Down
18 changes: 11 additions & 7 deletions shell/platform/darwin/ios/ios_surface.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#import "flutter/shell/platform/darwin/ios/ios_surface_gl.h"
#import "flutter/shell/platform/darwin/ios/ios_surface_software.h"

#include "flutter/shell/platform/darwin/ios/rendering_api_selection.h"

#if FLUTTER_SHELL_ENABLE_METAL
#import "flutter/shell/platform/darwin/ios/ios_surface_metal.h"
#endif // FLUTTER_SHELL_ENABLE_METAL
Expand All @@ -30,13 +32,15 @@
}

#if FLUTTER_SHELL_ENABLE_METAL
if ([layer.get() isKindOfClass:[CAMetalLayer class]]) {
return std::make_unique<IOSSurfaceMetal>(
fml::scoped_nsobject<CAMetalLayer>(
reinterpret_cast<CAMetalLayer*>([layer.get() retain])), // Metal layer
std::move(context), // context
platform_views_controller // platform views controller
);
if (@available(iOS METAL_IOS_VERSION_BASELINE, *)) {
if ([layer.get() isKindOfClass:[CAMetalLayer class]]) {
return std::make_unique<IOSSurfaceMetal>(
fml::scoped_nsobject<CAMetalLayer>(
reinterpret_cast<CAMetalLayer*>([layer.get() retain])), // Metal layer
std::move(context), // context
platform_views_controller // platform views controller
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: FML_CHECK(false) in an else clause saying this should already have been checked.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesnt make sense to me, it not being available is valid since it will use the software renderer in that case

}
#endif // FLUTTER_SHELL_ENABLE_METAL

Expand Down
4 changes: 3 additions & 1 deletion shell/platform/darwin/ios/ios_surface_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
#include "flutter/fml/macros.h"
#include "flutter/shell/gpu/gpu_surface_delegate.h"
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
#include "third_party/skia/include/gpu/mtl/GrMtlTypes.h"

@class CAMetalLayer;

namespace flutter {

class IOSSurfaceMetal final : public IOSSurface, public GPUSurfaceDelegate {
class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetal final : public IOSSurface,
public GPUSurfaceDelegate {
public:
IOSSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer,
std::shared_ptr<IOSContext> context,
Expand Down
18 changes: 15 additions & 3 deletions shell/platform/darwin/ios/rendering_api_selection.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,23 @@ enum class IOSRenderingAPI {
kMetal,
};

IOSRenderingAPI GetRenderingAPIForProcess();
// Pass force_software to force software rendering. This is only respected on
// simulators.
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software);

Class GetCoreAnimationLayerClassForRenderingAPI(
IOSRenderingAPI rendering_api = GetRenderingAPIForProcess());
Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api);

} // namespace flutter

// Flutter supports Metal on all devices with Apple A7 SoC or above that have
// been updated to or past iOS 10.0. The processor was selected as it is the
// first version at which Metal was supported. The iOS version floor was
// selected due to the availability of features used by Skia.
// Support for Metal on simulators was added by Apple in the SDK for iOS 13.
#if TARGET_OS_SIMULATOR
#define METAL_IOS_VERSION_BASELINE 13.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document how these baselines were selected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added by @Kavantix - Adding what I assume is correct as

// Metal support was added for simulators in iOS 13.
// Otherwise, the lowest supported version in Skia is iOS 10.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I see, I'm moving the old comment from the impl file to here, and adding another line about when sim support was added.

#else
#define METAL_IOS_VERSION_BASELINE 10.0
#endif // TARGET_OS_SIMULATOR

#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_RENDERING_API_SELECTION_H_
36 changes: 25 additions & 11 deletions shell/platform/darwin/ios/rendering_api_selection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,49 @@
#if FLUTTER_SHELL_ENABLE_METAL
#include <Metal/Metal.h>
#endif // FLUTTER_SHELL_ENABLE_METAL
#import <TargetConditionals.h>

#include "flutter/fml/logging.h"

namespace flutter {

#if FLUTTER_SHELL_ENABLE_METAL
bool ShouldUseMetalRenderer() {
// Flutter supports Metal on all devices with Apple A7 SoC or above that have been updated to or
// past iOS 10.0. The processor was selected as it is the first version at which Metal was
// supported. The iOS version floor was selected due to the availability of features used by Skia.
bool ios_version_supports_metal = false;
if (@available(iOS 10.0, *)) {
if (@available(iOS METAL_IOS_VERSION_BASELINE, *)) {
auto device = MTLCreateSystemDefaultDevice();
ios_version_supports_metal = [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3];
}
return ios_version_supports_metal;
}
#endif // FLUTTER_SHELL_ENABLE_METAL

IOSRenderingAPI GetRenderingAPIForProcess() {
#if TARGET_IPHONE_SIMULATOR
return IOSRenderingAPI::kSoftware;
#endif // TARGET_IPHONE_SIMULATOR
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software) {
#if TARGET_OS_SIMULATOR
if (force_software) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows software rendering on devices. This check should only be performed on simulators.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an #elif block if we're not in release to print a warning that you can't use this on physical devices.

return IOSRenderingAPI::kSoftware;
}
#else
if (force_software) {
FML_LOG(WARNING) << "The --enable-software-rendering is only supported on Simulator targets "
"and will be ignored.";
}
#endif // TARGET_OS_SIMULATOR

#if FLUTTER_SHELL_ENABLE_METAL
static bool should_use_metal = ShouldUseMetalRenderer();
if (should_use_metal) {
return IOSRenderingAPI::kMetal;
}
#endif // FLUTTER_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.
#if TARGET_OS_SIMULATOR
return IOSRenderingAPI::kSoftware;
#else
return IOSRenderingAPI::kOpenGLES;
#endif // TARGET_OS_SIMULATOR
}

Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api) {
Expand All @@ -49,10 +61,12 @@ Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api) {
return [CALayer class];
case IOSRenderingAPI::kOpenGLES:
return [CAEAGLLayer class];
#if !TARGET_IPHONE_SIMULATOR
case IOSRenderingAPI::kMetal:
return [CAMetalLayer class];
#endif // !TARGET_IPHONE_SIMULATOR
if (@available(iOS METAL_IOS_VERSION_BASELINE, *)) {
return [CAMetalLayer class];
}
FML_CHECK(false) << "Metal availability should already have been checked";
break;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
242F37A222E636DE001E83D4 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
242F37A322E636DE001E83D4 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4122E3B5F700073EBF /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
244EA6D0230DBE8900B2D26E /* golden_platform_view_D21AP.png in Resources */ = {isa = PBXBuildFile; fileRef = 244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */; };
246A6611252E693A00EAB0F3 /* RenderingSelectionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 246A6610252E693A00EAB0F3 /* RenderingSelectionTest.m */; };
246B4E4222E3B5F700073EBF /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4122E3B5F700073EBF /* App.framework */; };
246B4E4622E3B61000073EBF /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* Flutter.framework */; };
248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 248D76CB22E388370012F0C1 /* AppDelegate.m */; };
Expand Down Expand Up @@ -126,6 +127,7 @@
0D8470A3240F0B1F0030B565 /* StatusBarTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StatusBarTest.m; sourceTree = "<group>"; };
0DB781FC22EA2C0300E9B371 /* FlutterViewControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlutterViewControllerTest.m; sourceTree = "<group>"; };
244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = golden_platform_view_D21AP.png; sourceTree = "<group>"; };
246A6610252E693A00EAB0F3 /* RenderingSelectionTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RenderingSelectionTest.m; sourceTree = "<group>"; };
246B4E4122E3B5F700073EBF /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = App.framework; sourceTree = "<group>"; };
246B4E4522E3B61000073EBF /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Flutter.framework; sourceTree = "<group>"; };
248D76C722E388370012F0C1 /* Scenarios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Scenarios.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -291,6 +293,7 @@
0D8470A3240F0B1F0030B565 /* StatusBarTest.m */,
0A42BFB32447E179007E212E /* TextSemanticsFocusTest.m */,
0A42BFB52447E19F007E212E /* TextSemanticsFocusTest.h */,
246A6610252E693A00EAB0F3 /* RenderingSelectionTest.m */,
);
path = ScenariosUITests;
sourceTree = "<group>";
Expand Down Expand Up @@ -488,6 +491,7 @@
6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */,
248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */,
0D8470A4240F0B1F0030B565 /* StatusBarTest.m in Sources */,
246A6611252E693A00EAB0F3 /* RenderingSelectionTest.m in Sources */,
4F06F1B32473296E000AF246 /* LocalizationInitializationTest.m in Sources */,
0A42BFB42447E179007E212E /* TextSemanticsFocusTest.m in Sources */,
);
Expand Down
13 changes: 12 additions & 1 deletion testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ - (BOOL)application:(UIApplication*)application
@"--gesture-reject-eager" : @"platform_view_gesture_reject_eager",
@"--gesture-accept" : @"platform_view_gesture_accept",
@"--tap-status-bar" : @"tap_status_bar",
@"--text-semantics-focus" : @"text_semantics_focus"
@"--text-semantics-focus" : @"text_semantics_focus",
@"--animated-color-square" : @"animated_color_square",
};
__block NSString* flutterViewControllerTestName = nil;
[launchArgsMap
Expand Down Expand Up @@ -119,6 +120,16 @@ - (void)setupFlutterViewControllerTest:(NSString*)scenarioIdentifier {
gestureRecognizersBlockingPolicy:
FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded];
self.window.rootViewController = flutterViewController;

if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--assert-ca-layer-type"]) {
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--enable-software-rendering"]) {
NSAssert([flutterViewController.view.layer isKindOfClass:[CALayer class]],
@"Expected CALayer for software rendering.");
} else {
NSAssert([flutterViewController.view.layer isKindOfClass:[CAMetalLayer class]],
@"Expected CAMetalLayer for non-software rendering.");
}
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ - (void)setUp {
self.continueAfterFailure = NO;

self.application = [[XCUIApplication alloc] init];
self.application.launchArguments = @[ self.manager.launchArg ];
self.application.launchArguments = @[ self.manager.launchArg, @"--enable-software-rendering" ];
[self.application launch];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ - (void)setUp {

- (void)testRejectPolicyUtilTouchesEnded {
XCUIApplication* app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"--gesture-reject-after-touches-ended" ];
app.launchArguments =
@[ @"--gesture-reject-after-touches-ended", @"--enable-software-rendering" ];
[app launch];

NSPredicate* predicateToFindPlatformView =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2020 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/Flutter.h>
#import <XCTest/XCTest.h>

@interface RenderingSelectionTest : XCTestCase
@property(nonatomic, strong) XCUIApplication* application;
@end

@implementation RenderingSelectionTest

- (void)setUp {
[super setUp];
self.continueAfterFailure = NO;
self.application = [[XCUIApplication alloc] init];
}

- (void)testSoftwareRendering {
self.application.launchArguments =
@[ @"--animated-color-square", @"--assert-ca-layer-type", @"--enable-software-rendering" ];
[self.application launch];

// App asserts that the rendering API is CALayer
}

- (void)testMetalRendering {
self.application.launchArguments = @[ @"--animated-color-square", @"--assert-ca-layer-type" ];
[self.application launch];

// App asserts that the rendering API is CAMetalLayer
}
@end
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ - (void)setUp {
// +---+
- (void)testNoOverlay {
XCUIApplication* app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ];
app.launchArguments =
@[ @"--platform-view-no-overlay-intersection", @"--enable-software-rendering" ];
[app launch];

XCUIElement* platform_view = app.textViews[@"platform_view[0]"];
Expand Down
4 changes: 2 additions & 2 deletions tools/gn
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ def to_gn_args(args):
gn_args['use_goma'] = False
gn_args['goma_dir'] = None

# Enable Metal on non-simulator iOS builds.
if args.target_os == 'ios' and not args.simulator:
# Enable Metal on iOS builds.
if args.target_os == 'ios':
gn_args['skia_use_metal'] = True
gn_args['shell_enable_metal'] = True
# Bitcode enabled builds using the current version of the toolchain leak
Expand Down