Skip to content

Commit

Permalink
Wire up the Skia Metal backend on iOS. (flutter#8936)
Browse files Browse the repository at this point in the history
  • Loading branch information
chinmaygarde authored May 11, 2019
1 parent 5ad9864 commit 5526884
Show file tree
Hide file tree
Showing 15 changed files with 352 additions and 6 deletions.
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ FILE: ../../../flutter/shell/gpu/gpu_surface_gl.cc
FILE: ../../../flutter/shell/gpu/gpu_surface_gl.h
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_software.cc
FILE: ../../../flutter/shell/gpu/gpu_surface_software.h
FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan.cc
Expand Down Expand Up @@ -719,6 +721,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.mm
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_gl.h
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_gl.mm
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_metal.h
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_metal.mm
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.h
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.mm
FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.h
Expand Down
1 change: 1 addition & 0 deletions shell/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ shell_gpu_configuration("shell_unittests_gpu_configuration") {
enable_software = true
enable_vulkan = false
enable_gl = false
enable_metal = false
}

test_fixtures("shell_unittests_fixtures") {
Expand Down
1 change: 1 addition & 0 deletions shell/config.gni
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

declare_args() {
shell_enable_vulkan = false
shell_enable_metal = false
}
9 changes: 9 additions & 0 deletions shell/gpu/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,12 @@ source_set("gpu_surface_vulkan") {
"$flutter_root/vulkan",
]
}

source_set("gpu_surface_metal") {
sources = [
"$gpu_dir/gpu_surface_metal.h",
"$gpu_dir/gpu_surface_metal.mm",
]

deps = gpu_common_deps + [ "//third_party/skia" ]
}
6 changes: 6 additions & 0 deletions shell/gpu/gpu.gni
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ template("shell_gpu_configuration") {
"Caller must declare if the Vulkan backend must be enabled.")
assert(defined(invoker.enable_gl),
"Caller must declare if the Open GL backend must be enabled.")
assert(defined(invoker.enable_metal),
"Caller must declare if the Metal backend must be enabled.")

group(target_name) {
public_deps = []
Expand All @@ -26,5 +28,9 @@ template("shell_gpu_configuration") {
if (invoker.enable_vulkan) {
public_deps += [ "$flutter_root/shell/gpu:gpu_surface_vulkan" ]
}

if (invoker.enable_metal) {
public_deps += [ "$flutter_root/shell/gpu:gpu_surface_metal" ]
}
}
}
50 changes: 50 additions & 0 deletions shell/gpu/gpu_surface_metal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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_H_
#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_

#include <Metal/Metal.h>

#include "flutter/fml/macros.h"
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
#include "flutter/shell/common/surface.h"
#include "third_party/skia/include/gpu/GrContext.h"

@class CAMetalLayer;

namespace flutter {

class GPUSurfaceMetal : public Surface {
public:
GPUSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer);

~GPUSurfaceMetal() override;

private:
fml::scoped_nsobject<CAMetalLayer> layer_;
sk_sp<GrContext> context_;
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue_;

// |Surface|
bool IsValid() override;

// |Surface|
std::unique_ptr<SurfaceFrame> AcquireFrame(const SkISize& size) override;

// |Surface|
SkMatrix GetRootTransformation() const override;

// |Surface|
GrContext* GetContext() override;

// |Surface|
bool MakeRenderContextCurrent() override;

FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetal);
};

} // namespace flutter

#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_
145 changes: 145 additions & 0 deletions shell/gpu/gpu_surface_metal.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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.h"

#include <QuartzCore/CAMetalLayer.h>

#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"

namespace flutter {

GPUSurfaceMetal::GPUSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer)
: layer_(std::move(layer)) {
if (!layer_) {
FML_LOG(ERROR) << "Could not create metal surface because of invalid layer.";
return;
}

layer.get().pixelFormat = MTLPixelFormatBGRA8Unorm;

auto metal_device = fml::scoped_nsprotocol<id<MTLDevice>>([layer_.get().device retain]);
auto metal_queue = fml::scoped_nsprotocol<id<MTLCommandQueue>>([metal_device newCommandQueue]);

if (!metal_device || !metal_queue) {
FML_LOG(ERROR) << "Could not create metal device or queue.";
return;
}

command_queue_ = metal_queue;

// The context creation routine accepts arguments using transfer semantics.
auto context = GrContext::MakeMetal(metal_device.release(), metal_queue.release());
if (!context) {
FML_LOG(ERROR) << "Could not create Skia metal context.";
return;
}

context_ = context;
}

GPUSurfaceMetal::~GPUSurfaceMetal() = default;

// |Surface|
bool GPUSurfaceMetal::IsValid() {
return layer_ && context_ && command_queue_;
}

// |Surface|
std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size) {
if (!IsValid()) {
FML_LOG(ERROR) << "Metal surface was invalid.";
return nullptr;
}

if (size.isEmpty()) {
FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
return nullptr;
}

const auto bounds = layer_.get().bounds.size;
if (bounds.width <= 0.0 || bounds.height <= 0.0) {
FML_LOG(ERROR) << "Metal layer bounds were invalid.";
return nullptr;
}

const auto scale = layer_.get().contentsScale;

auto next_drawable = fml::scoped_nsprotocol<id<CAMetalDrawable>>([[layer_ nextDrawable] retain]);
if (!next_drawable) {
FML_LOG(ERROR) << "Could not acquire next metal drawable.";
return nullptr;
}

auto metal_texture = fml::scoped_nsprotocol<id<MTLTexture>>([next_drawable.get().texture retain]);
if (!metal_texture) {
FML_LOG(ERROR) << "Could not acquire metal texture from drawable.";
return nullptr;
}

GrMtlTextureInfo metal_texture_info;
metal_texture_info.fTexture = metal_texture.get();

GrBackendRenderTarget metal_render_target(bounds.width * scale, // width
bounds.height * scale, // height
1, // sample count
metal_texture_info // metal texture info
);

auto command_buffer =
fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);

SkSurface::RenderTargetReleaseProc release_proc = [](SkSurface::ReleaseContext context) {
[reinterpret_cast<id>(context) release];
};

auto surface =
SkSurface::MakeFromBackendRenderTarget(context_.get(), // context
metal_render_target, // backend render target
kTopLeft_GrSurfaceOrigin, // origin
kBGRA_8888_SkColorType, // color type
nullptr, // colorspace
nullptr, // surface properties
release_proc, // release proc
metal_texture.release() // release context (texture)
);

if (!surface) {
FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
return nullptr;
}

auto submit_callback = [drawable = next_drawable, command_buffer](
const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
canvas->flush();
[command_buffer.get() presentDrawable:drawable.get()];
[command_buffer.get() commit];
return true;
};

return std::make_unique<SurfaceFrame>(std::move(surface), submit_callback);
}

// |Surface|
SkMatrix GPUSurfaceMetal::GetRootTransformation() const {
// This backend does not currently support root surface transformations. Just
// return identity.
SkMatrix matrix;
matrix.reset();
return matrix;
}

// |Surface|
GrContext* GPUSurfaceMetal::GetContext() {
return context_.get();
}

// |Surface|
bool GPUSurfaceMetal::MakeRenderContextCurrent() {
// This backend has no such concept.
return true;
}

} // namespace flutter
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ shell_gpu_configuration("android_gpu_configuration") {
enable_software = true
enable_vulkan = shell_enable_vulkan
enable_gl = true
enable_metal = false
}

shared_library("flutter_shell_native") {
Expand Down
15 changes: 14 additions & 1 deletion shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ shell_gpu_configuration("ios_gpu_configuration") {
enable_software = true
enable_gl = true
enable_vulkan = false
enable_metal = shell_enable_metal
}

# The headers that will be copied to the Flutter.framework and be accessed
Expand Down Expand Up @@ -100,6 +101,13 @@ shared_library("create_flutter_framework_dylib") {
"platform_view_ios.mm",
]

if (shell_enable_metal) {
sources += [
"ios_surface_metal.h",
"ios_surface_metal.mm",
]
}

sources += _flutter_framework_headers

deps = [
Expand All @@ -117,7 +125,11 @@ shared_library("create_flutter_framework_dylib") {

public_configs = [ "$flutter_root:config" ]

defines = [ "FLUTTER_FRAMEWORK" ]
defines = [ "FLUTTER_FRAMEWORK=1" ]

if (shell_enable_metal) {
defines += [ "FLUTTER_SHELL_ENABLE_METAL=1" ]
}

libs = [
"CoreMedia.framework",
Expand Down Expand Up @@ -241,6 +253,7 @@ copy("copy_license") {
shared_library("copy_and_verify_framework_headers") {
visibility = [ ":*" ]
include_dirs = [ "$_flutter_framework_headers_copy_dir" ]

sources = [
"framework/Source/FlutterUmbrellaImport.m",
]
Expand Down
29 changes: 27 additions & 2 deletions shell/platform/darwin/ios/framework/Source/FlutterView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include "flutter/shell/platform/darwin/ios/ios_surface_software.h"
#include "third_party/skia/include/utils/mac/SkCGUtils.h"

#if FLUTTER_SHELL_ENABLE_METAL
#include "flutter/shell/platform/darwin/ios/ios_surface_metal.h"
#endif // FLUTTER_SHELL_ENABLE_METAL

@implementation FlutterView

id<FlutterViewEngineDelegate> _delegate;
Expand Down Expand Up @@ -60,14 +64,26 @@ - (void)layoutSubviews {
layer.rasterizationScale = screenScale;
}

#if FLUTTER_SHELL_ENABLE_METAL
if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
CGFloat screenScale = [UIScreen mainScreen].scale;
self.layer.contentsScale = screenScale;
self.layer.rasterizationScale = screenScale;
}

#endif // FLUTTER_SHELL_ENABLE_METAL
[super layoutSubviews];
}

+ (Class)layerClass {
#if TARGET_IPHONE_SIMULATOR
return [CALayer class];
#else // TARGET_IPHONE_SIMULATOR
#else // TARGET_IPHONE_SIMULATOR
#if FLUTTER_SHELL_ENABLE_METAL
return [CAMetalLayer class];
#else // FLUTTER_SHELL_ENABLE_METAL
return [CAEAGLLayer class];
#endif // FLUTTER_SHELL_ENABLE_METAL
#endif // TARGET_IPHONE_SIMULATOR
}

Expand All @@ -87,7 +103,16 @@ + (Class)layerClass {
}
return std::make_unique<flutter::IOSSurfaceGL>(context, std::move(eagl_layer),
[_delegate platformViewsController]);
} else {
}
#if FLUTTER_SHELL_ENABLE_METAL
else if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
return std::make_unique<flutter::IOSSurfaceMetal>(
fml::scoped_nsobject<CAMetalLayer>(reinterpret_cast<CAMetalLayer*>([self.layer retain])),
[_delegate platformViewsController]);
}
#endif // FLUTTER_SHELL_ENABLE_METAL

else {
fml::scoped_nsobject<CALayer> layer(reinterpret_cast<CALayer*>([self.layer retain]));
return std::make_unique<flutter::IOSSurfaceSoftware>(std::move(layer),
[_delegate platformViewsController]);
Expand Down
6 changes: 3 additions & 3 deletions shell/platform/darwin/ios/ios_surface_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

namespace flutter {

class IOSSurfaceGL : public IOSSurface,
public GPUSurfaceGLDelegate,
public flutter::ExternalViewEmbedder {
class IOSSurfaceGL final : public IOSSurface,
public GPUSurfaceGLDelegate,
public flutter::ExternalViewEmbedder {
public:
IOSSurfaceGL(std::shared_ptr<IOSGLContext> context,
fml::scoped_nsobject<CAEAGLLayer> layer,
Expand Down
Loading

0 comments on commit 5526884

Please sign in to comment.