Skip to content

Commit

Permalink
Reland "Introduce a delegate class for gpu metal rendering (flutter#2…
Browse files Browse the repository at this point in the history
…2611)"

This reverts commit 0d71d27.
  • Loading branch information
iskakaushik committed Nov 30, 2020
1 parent 587c023 commit fbdcb8d
Show file tree
Hide file tree
Showing 18 changed files with 498 additions and 158 deletions.
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 @@ -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
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
29 changes: 15 additions & 14 deletions shell/gpu/gpu_surface_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,31 @@
#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_;
GrMTLHandle next_drawable_ = nullptr;
sk_sp<GrDirectContext> context_;

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

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

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

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

void ReleaseUnusedDrawableIfNecessary();

FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetal);
Expand Down
120 changes: 77 additions & 43 deletions shell/gpu/gpu_surface_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

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

#include <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.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 +18,18 @@

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();
}

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

// |Surface|
Expand All @@ -48,31 +44,40 @@
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();

// 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)
);
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
&next_drawable_ // drawable (transfer out)
);

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

Expand All @@ -85,23 +90,52 @@

canvas->flush();

if (next_drawable_ == nullptr) {
FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface.";
GrMTLHandle drawable = next_drawable_;
if (!drawable) {
FML_DLOG(ERROR) << "Unable to obtain a metal drawable.";
return false;
}

auto command_buffer =
fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);
return delegate_->PresentDrawable(drawable);
};

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

fml::scoped_nsprotocol<id<CAMetalDrawable>> drawable(
reinterpret_cast<id<CAMetalDrawable>>(next_drawable_));
next_drawable_ = nullptr;
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;
}

[command_buffer.get() commit];
[command_buffer.get() waitUntilScheduled];
[drawable.get() present];
GrMtlTextureInfo info;
info.fTexture.reset([mtl_texture retain]);
GrBackendTexture backend_texture(frame_info.width(), frame_info.height(), GrMipmapped::kNo, info);

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 = [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();

return true;
return delegate->PresentTexture(texture_id);
};

return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
Expand Down
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
100 changes: 100 additions & 0 deletions shell/gpu/gpu_surface_metal_delegate.h
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

#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<MTLDevice>
typedef void* GPUMTLDeviceHandle;

// expected to be id<MTLCommandQueues>
typedef void* GPUMTLCommandQueueHandle;

// expected to be CAMetalLayer*
typedef void* GPUCAMetalLayerHandle;

// expected to be id<MTLTexture>
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_
Loading

0 comments on commit fbdcb8d

Please sign in to comment.