Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a delegate class for gpu metal rendering #22611

Merged
merged 4 commits into from
Nov 25, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -930,6 +932,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
Expand Down
6 changes: 3 additions & 3 deletions shell/common/animator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<LayerTreePipeline>(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.
Expand All @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions shell/gpu/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 13 additions & 15 deletions shell/gpu/gpu_surface_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,30 @@
#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_
#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_

#include <Metal/Metal.h>

#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<CAMetalLayer> layer,
sk_sp<GrDirectContext> context,
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue);
GPUSurfaceMetal(GPUSurfaceMetalDelegate* delegate,
sk_sp<GrDirectContext> context);

// |Surface|
~GPUSurfaceMetal();

private:
fml::scoped_nsobject<CAMetalLayer> layer_;
sk_sp<GrDirectContext> context_;
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue_;
GrMTLHandle next_drawable_ = nullptr;

// |Surface|
bool IsValid() override;

private:
const GPUSurfaceMetalDelegate* delegate_;
const MTLRenderTargetType render_target_type_;
sk_sp<GrDirectContext> context_;

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

Expand All @@ -47,7 +41,11 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface {
// |Surface|
std::unique_ptr<GLContextResult> MakeRenderContextCurrent() override;

void ReleaseUnusedDrawableIfNecessary();
std::unique_ptr<SurfaceFrame> AcquireFrameFromCAMetalLayer(
const SkISize& frame_info);

std::unique_ptr<SurfaceFrame> AcquireFrameFromMTLTexture(
const SkISize& frame_info);

FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetal);
};
Expand Down
146 changes: 82 additions & 64 deletions shell/gpu/gpu_surface_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

#include "flutter/shell/gpu/gpu_surface_metal.h"

#include <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.h>
#include "include/core/SkSize.h"

#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"
Expand All @@ -15,25 +19,16 @@

namespace flutter {

GPUSurfaceMetal::GPUSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer,
sk_sp<GrDirectContext> context,
fml::scoped_nsprotocol<id<MTLCommandQueue>> 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<GrDirectContext> context)
: delegate_(delegate),
render_target_type_(delegate->GetRenderTargetType()),
context_(std::move(context)) {}

GPUSurfaceMetal::~GPUSurfaceMetal() {
ReleaseUnusedDrawableIfNecessary();
}
GPUSurfaceMetal::~GPUSurfaceMetal() = default;

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

// |Surface|
Expand All @@ -48,60 +43,95 @@
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<SurfaceFrame> 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();
GrMTLHandle drawable_raw;
sk_sp<SkSurface> surface =
SkSurface::MakeFromCAMetalLayer(context_.get(), // context
layer, // layer
kTopLeft_GrSurfaceOrigin, // origin
1, // sample count
kBGRA_8888_SkColorType, // color type
nullptr, // colorspace
nullptr, // surface properties
&drawable_raw // drawable (transfer out)
);
fml::CFRef<GrMTLHandle> drawable(drawable_raw);

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

auto submit_callback =
fml::MakeCopyable([drawable = std::move(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();

return delegate->PresentDrawable(drawable);
});

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

std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrameFromMTLTexture(
const SkISize& frame_info) {
GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info);
id<MTLTexture> mtl_texture = (id<MTLTexture>)(texture.texture);

if (!mtl_texture) {
FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
return nullptr;
}

// 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];
GrMtlTextureInfo info;
info.fTexture.reset([mtl_texture retain]);
GrBackendTexture backend_texture(frame_info.width(), frame_info.height(), GrMipmapped::kNo, info);

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)
);
sk_sp<SkSurface> 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;
}

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<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);

fml::scoped_nsprotocol<id<CAMetalDrawable>> drawable(
reinterpret_cast<id<CAMetalDrawable>>(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<SurfaceFrame>(std::move(surface), true, submit_callback);
Expand All @@ -125,16 +155,4 @@
return std::make_unique<GLContextDefaultResult>(true);
}

void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() {
// If the previous surface frame was not submitted before a new one is acquired, the old drawable
// needs to be released. An RAII wrapper may not be used because this needs to interoperate with
// Skia APIs.
if (next_drawable_ == nullptr) {
return;
}

CFRelease(next_drawable_);
next_drawable_ = nullptr;
}

} // namespace flutter
19 changes: 19 additions & 0 deletions shell/gpu/gpu_surface_metal_delegate.cc
Original file line number Diff line number Diff line change
@@ -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
Loading