[macos] Reland default metal and also check if the system supports metal before defaulting to it (#24601)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 29f721d..1207450 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1132,6 +1132,8 @@
 FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm
 FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm
 FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h
+FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h
+FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm
 FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.h
 FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.mm
 FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h
diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn
index 9c52f8e..9422bd1 100644
--- a/shell/platform/darwin/macos/BUILD.gn
+++ b/shell/platform/darwin/macos/BUILD.gn
@@ -82,6 +82,8 @@
     "framework/Source/FlutterOpenGLRenderer.h",
     "framework/Source/FlutterOpenGLRenderer.mm",
     "framework/Source/FlutterRenderer.h",
+    "framework/Source/FlutterRenderingBackend.h",
+    "framework/Source/FlutterRenderingBackend.mm",
     "framework/Source/FlutterResizableBackingStoreProvider.h",
     "framework/Source/FlutterResizableBackingStoreProvider.mm",
     "framework/Source/FlutterResizeSynchronizer.h",
@@ -151,23 +153,17 @@
   testonly = true
 
   sources = [
+    "framework/Source/FlutterEmbedderExternalTextureUnittests.mm",
     "framework/Source/FlutterEngineTest.mm",
     "framework/Source/FlutterGLCompositorUnittests.mm",
+    "framework/Source/FlutterMetalRendererTest.mm",
+    "framework/Source/FlutterMetalSurfaceManagerTest.mm",
+    "framework/Source/FlutterOpenGLRendererTest.mm",
     "framework/Source/FlutterViewControllerTest.mm",
     "framework/Source/FlutterViewControllerTestUtils.h",
     "framework/Source/FlutterViewControllerTestUtils.mm",
   ]
 
-  if (shell_enable_metal) {
-    sources += [
-      "framework/Source/FlutterEmbedderExternalTextureUnittests.mm",
-      "framework/Source/FlutterMetalRendererTest.mm",
-      "framework/Source/FlutterMetalSurfaceManagerTest.mm",
-    ]
-  } else {
-    sources += [ "framework/Source/FlutterOpenGLRendererTest.mm" ]
-  }
-
   cflags_objcc = flutter_cflags_objcc_arc
 
   ldflags = [ "-ObjC" ]
@@ -176,6 +172,7 @@
     ":flutter_desktop_darwin_fixtures",
     ":flutter_framework_source",
     "//flutter/shell/platform/darwin/common:framework_shared",
+    "//flutter/shell/platform/darwin/graphics",
     "//flutter/shell/platform/embedder:embedder_as_internal_library",
     "//flutter/shell/platform/embedder:embedder_test_utils",
     "//flutter/testing",
@@ -184,10 +181,6 @@
     "//flutter/testing:testing_lib",
     "//third_party/ocmock:ocmock",
   ]
-
-  if (shell_enable_metal) {
-    deps += [ "//flutter/shell/platform/darwin/graphics" ]
-  }
 }
 
 copy("copy_dylib") {
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureUnittests.mm
index 700bd6e..e5abef7 100644
--- a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureUnittests.mm
+++ b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureUnittests.mm
@@ -41,6 +41,9 @@
   textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
   id<MTLTexture> mtlTexture =
       [darwinContextMetal.device newTextureWithDescriptor:textureDescriptor];
+  std::vector<FlutterMetalTextureHandle> textures = {
+      (__bridge FlutterMetalTextureHandle)mtlTexture,
+  };
 
   // callback to resolve the texture.
   EmbedderExternalTextureMetal::ExternalTextureCallback callback = [&](int64_t texture_id, size_t w,
@@ -54,11 +57,6 @@
     texture->height = h;
     texture->width = w;
     texture->pixel_format = FlutterMetalExternalTexturePixelFormat::kRGBA;
-
-    std::vector<FlutterMetalTextureHandle> textures = {
-        (__bridge FlutterMetalTextureHandle)mtlTexture,
-    };
-
     texture->textures = textures.data();
 
     return std::unique_ptr<FlutterMetalExternalTexture>(texture);
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
index cc7b847..eab43a4 100644
--- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
+++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
@@ -13,6 +13,7 @@
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h"
+#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
 #import "flutter/shell/platform/embedder/embedder.h"
 
@@ -131,10 +132,6 @@
   // Pointer to the Dart AOT snapshot and instruction data.
   _FlutterEngineAOTData* _aotData;
 
-  // If set to true, engine will render using metal. This is controlled by SHELL_ENABLE_METAL
-  // for now, intent is to be made default in the future.
-  BOOL _enableMetalRendering;
-
   // _macOSGLCompositor is created when the engine is created and
   // it's destruction is handled by ARC when the engine is destroyed.
   std::unique_ptr<flutter::FlutterGLCompositor> _macOSGLCompositor;
@@ -157,14 +154,10 @@
   _messageHandlers = [[NSMutableDictionary alloc] init];
   _allowHeadlessExecution = allowHeadlessExecution;
 
-#ifdef SHELL_ENABLE_METAL
-  _enableMetalRendering = YES;
-#endif
-
   _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
   FlutterEngineGetProcAddresses(&_embedderAPI);
 
-  if (_enableMetalRendering) {
+  if ([FlutterRenderingBackend renderUsingMetal]) {
     _renderer = [[FlutterMetalRenderer alloc] initWithFlutterEngine:self];
   } else {
     _renderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:self];
@@ -307,7 +300,7 @@
 
 - (FlutterCompositor*)createFlutterCompositor {
   // When rendering with metal do not support platform views.
-  if (_enableMetalRendering) {
+  if ([FlutterRenderingBackend renderUsingMetal]) {
     return nil;
   }
 
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm
index d7da0d3..a1d9a5d 100644
--- a/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm
+++ b/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm
@@ -13,21 +13,36 @@
 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
 #include "flutter/testing/testing.h"
 
-namespace flutter::testing {
+@interface TestOpenGLEngine : FlutterEngine
 
-namespace {
-// Returns an engine configured for the test fixture resource configuration.
-FlutterEngine* CreateTestEngine() {
-  NSString* fixtures = @(testing::GetFixturesPath());
+@property(nonatomic, readwrite) id<FlutterRenderer> renderer;
+
+- (nullable instancetype)initWithGLRenderer;
+
+@end
+
+@implementation TestOpenGLEngine
+
+@synthesize renderer;
+
+- (nullable instancetype)initWithGLRenderer {
+  NSString* fixtures = @(flutter::testing::GetFixturesPath());
   FlutterDartProject* project = [[FlutterDartProject alloc]
       initWithAssetsPath:fixtures
              ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
-  return [[FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true];
+  self = [self initWithName:@"test" project:project allowHeadlessExecution:true];
+  if (self) {
+    renderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:self];
+  }
+  return self;
 }
-}  // namespace
+
+@end
+
+namespace flutter::testing {
 
 TEST(FlutterOpenGLRenderer, RegisterExternalTexture) {
-  FlutterEngine* engine = CreateTestEngine();
+  FlutterEngine* engine = [[TestOpenGLEngine alloc] initWithGLRenderer];
   EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
 
   id<FlutterTexture> flutterTexture = OCMProtocolMock(@protocol(FlutterTexture));
@@ -48,7 +63,7 @@
 }
 
 TEST(FlutterOpenGLRenderer, UnregisterExternalTexture) {
-  FlutterEngine* engine = CreateTestEngine();
+  FlutterEngine* engine = [[TestOpenGLEngine alloc] initWithGLRenderer];
   EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
 
   id<FlutterTexture> flutterTexture = OCMProtocolMock(@protocol(FlutterTexture));
@@ -70,7 +85,7 @@
 }
 
 TEST(FlutterOpenGLRenderer, MarkExternalTextureFrameAvailable) {
-  FlutterEngine* engine = CreateTestEngine();
+  FlutterEngine* engine = [[TestOpenGLEngine alloc] initWithGLRenderer];
   EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
 
   id<FlutterTexture> flutterTexture = OCMProtocolMock(@protocol(FlutterTexture));
@@ -92,7 +107,7 @@
 }
 
 TEST(FlutterOpenGLRenderer, PresetDelegatesToFlutterView) {
-  FlutterEngine* engine = CreateTestEngine();
+  FlutterEngine* engine = [[TestOpenGLEngine alloc] initWithGLRenderer];
   FlutterOpenGLRenderer* renderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:engine];
   id mockFlutterView = OCMClassMock([FlutterView class]);
   [[mockFlutterView expect] present];
@@ -102,7 +117,7 @@
 }
 
 TEST(FlutterOpenGLRenderer, FBOReturnedByFlutterView) {
-  FlutterEngine* engine = CreateTestEngine();
+  FlutterEngine* engine = [[TestOpenGLEngine alloc] initWithGLRenderer];
   FlutterOpenGLRenderer* renderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:engine];
   id mockFlutterView = OCMClassMock([FlutterView class]);
   FlutterFrameInfo frameInfo;
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h b/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h
new file mode 100644
index 0000000..28fe3b7
--- /dev/null
+++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h
@@ -0,0 +1,30 @@
+// 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 <Cocoa/Cocoa.h>
+
+/**
+ * Flutter on macOS currently supports both OpenGL and Metal rendering backends. This class provides
+ * utilities for determining the rendering backend and the corresponging layer properties.
+ */
+@interface FlutterRenderingBackend : NSObject
+
+/**
+ * Returns YES if the engine is supposed to use Metal as the rendering backend. On macOS versions
+ * >= 10.4 this is YES.
+ */
++ (BOOL)renderUsingMetal;
+
+/**
+ * CALayer class depending on the rendering backend.
+ */
++ (nonnull Class)layerClass;
+
+/**
+ * On both Metal and OpenGL rendering backends, `wantsLayer` is set on the `FlutterView`. This
+ * methos is used by `FlutterView` to provide the said `CALayer`.
+ */
++ (nonnull CALayer*)createBackingLayer;
+
+@end
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm b/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm
new file mode 100644
index 0000000..384455b
--- /dev/null
+++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm
@@ -0,0 +1,46 @@
+// 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/macos/framework/Source/FlutterRenderingBackend.h"
+
+#import <Metal/Metal.h>
+#import <QuartzCore/QuartzCore.h>
+
+@implementation FlutterRenderingBackend
+
++ (BOOL)renderUsingMetal {
+  if (@available(macOS 10.14, *)) {
+    BOOL systemSupportsMetal = MTLCreateSystemDefaultDevice() != nil;
+    return systemSupportsMetal;
+  } else {
+    return NO;
+  }
+}
+
++ (Class)layerClass {
+  BOOL enableMetal = [FlutterRenderingBackend renderUsingMetal];
+  if (enableMetal) {
+    return [CAMetalLayer class];
+  } else {
+    return [CAOpenGLLayer class];
+  }
+}
+
++ (CALayer*)createBackingLayer {
+  BOOL enableMetal = [FlutterRenderingBackend renderUsingMetal];
+  if (enableMetal) {
+    CAMetalLayer* metalLayer = [CAMetalLayer layer];
+    // This is set to true to synchronize the presentation of the layer and its contents with Core
+    // Animation. When presenting the texture see `[FlutterMetalResizableBackingStoreProvider
+    // resizeSynchronizerCommit:]` we start a CATransaction and wait for the command buffer to be
+    // scheduled. This ensures that the resizing process is smooth.
+    metalLayer.presentsWithTransaction = YES;
+    metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
+    return metalLayer;
+  } else {
+    return [CAOpenGLLayer layer];
+  }
+}
+
+@end
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm
index 6a36f9b..46faff7 100644
--- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm
+++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm
@@ -4,6 +4,7 @@
 
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
 
+#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h"
@@ -39,22 +40,13 @@
   return self;
 }
 
-#ifdef SHELL_ENABLE_METAL
 + (Class)layerClass {
-  return [CAMetalLayer class];
+  return [FlutterRenderingBackend layerClass];
 }
 
 - (CALayer*)makeBackingLayer {
-  CAMetalLayer* metalLayer = [CAMetalLayer layer];
-  // This is set to true to synchronize the presentation of the layer and its contents with Core
-  // Animation. When presenting the texture see `[FlutterMetalResizableBackingStoreProvider
-  // resizeSynchronizerCommit:]` we start a CATransaction and wait for the command buffer to be
-  // scheduled. This ensures that the resizing process is smooth.
-  metalLayer.presentsWithTransaction = YES;
-  metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
-  return metalLayer;
+  return [FlutterRenderingBackend createBackingLayer];
 }
-#endif
 
 - (instancetype)initWithMainContext:(NSOpenGLContext*)mainContext
                     reshapeListener:(id<FlutterViewReshapeListener>)reshapeListener {
diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm
index 0885929..8b4a62e 100644
--- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm
+++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm
@@ -12,6 +12,7 @@
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h"
+#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h"
 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
 #import "flutter/shell/platform/embedder/embedder.h"
@@ -278,11 +279,7 @@
 
 - (void)loadView {
   FlutterView* flutterView;
-  BOOL enableMetalRendering = NO;
-#ifdef SHELL_ENABLE_METAL
-  enableMetalRendering = YES;
-#endif
-  if (enableMetalRendering) {
+  if ([FlutterRenderingBackend renderUsingMetal]) {
     FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_engine.renderer);
     id<MTLDevice> device = metalRenderer.device;
     id<MTLCommandQueue> commandQueue = metalRenderer.commandQueue;
diff --git a/tools/gn b/tools/gn
index cd6ec76..feb8e21 100755
--- a/tools/gn
+++ b/tools/gn
@@ -54,6 +54,9 @@
     if args.enable_vulkan:
         target_dir.append('vulkan')
 
+    # This exists for backwards compatibility of tests that are being run
+    # on LUCI. This can be removed in coordination with a LUCI change:
+    # https://github.com/flutter/flutter/issues/76547
     if args.macos_enable_metal:
       target_dir.append('metal')
 
@@ -263,11 +266,7 @@
       # build a macOS metal only shell and a gl only shell.
       gn_args['allow_deprecated_api_calls'] = True
       gn_args['skia_use_metal'] = True
-      if args.macos_enable_metal:
-        gn_args['shell_enable_metal'] = True
-        # Skia has Metal support on macOS version >= 10.14.
-        MACOS_SKIA_METAL_SUPPORTED_MIN_VERSION = '10.14'
-        gn_args['mac_sdk_min'] = MACOS_SKIA_METAL_SUPPORTED_MIN_VERSION
+      gn_args['shell_enable_metal'] = True
 
     if args.enable_vulkan:
       # Enable vulkan in the Flutter shell.