Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 569fea4

Browse files
committed
Introduce a delegate class for gpu metal rendering
This supports rendering to CAMetalLayer and also MTLTexture.
1 parent be7f80e commit 569fea4

17 files changed

+462
-148
lines changed

shell/common/animator.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ Animator::Animator(Delegate& delegate,
2929
last_vsync_start_time_(),
3030
last_frame_target_time_(),
3131
dart_frame_deadline_(0),
32-
#if FLUTTER_SHELL_ENABLE_METAL
32+
#if SHELL_ENABLE_METAL
3333
layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)),
34-
#else // FLUTTER_SHELL_ENABLE_METAL
34+
#else // SHELL_ENABLE_METAL
3535
// TODO(dnfield): We should remove this logic and set the pipeline depth
3636
// back to 2 in this case. See
3737
// https://github.com/flutter/engine/pull/9132 for discussion.
@@ -40,7 +40,7 @@ Animator::Animator(Delegate& delegate,
4040
task_runners.GetRasterTaskRunner()
4141
? 1
4242
: 2)),
43-
#endif // FLUTTER_SHELL_ENABLE_METAL
43+
#endif // SHELL_ENABLE_METAL
4444
pending_frame_semaphore_(1),
4545
frame_number_(1),
4646
paused_(false),

shell/gpu/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ source_set("gpu_surface_metal") {
5151
sources = [
5252
"gpu_surface_metal.h",
5353
"gpu_surface_metal.mm",
54+
"gpu_surface_metal_delegate.cc",
55+
"gpu_surface_metal_delegate.h",
5456
]
5557

5658
deps = gpu_common_deps

shell/gpu/gpu_surface_metal.h

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,31 @@
55
#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_
66
#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_
77

8-
#include <Metal/Metal.h>
9-
108
#include "flutter/flow/surface.h"
119
#include "flutter/fml/macros.h"
12-
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
10+
#include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
1311
#include "third_party/skia/include/gpu/GrDirectContext.h"
1412
#include "third_party/skia/include/gpu/mtl/GrMtlTypes.h"
1513

16-
@class CAMetalLayer;
17-
1814
namespace flutter {
1915

2016
class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface {
2117
public:
22-
GPUSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer,
23-
sk_sp<GrDirectContext> context,
24-
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue);
18+
GPUSurfaceMetal(GPUSurfaceMetalDelegate* delegate,
19+
sk_sp<GrDirectContext> context);
2520

2621
// |Surface|
2722
~GPUSurfaceMetal();
2823

24+
// |Surface|
25+
bool IsValid() override;
26+
2927
private:
30-
fml::scoped_nsobject<CAMetalLayer> layer_;
28+
const GPUSurfaceMetalDelegate* delegate_;
29+
const MTLRenderTargetType render_target_type_;
3130
sk_sp<GrDirectContext> context_;
32-
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue_;
3331
GrMTLHandle next_drawable_ = nullptr;
3432

35-
// |Surface|
36-
bool IsValid() override;
37-
3833
// |Surface|
3934
std::unique_ptr<SurfaceFrame> AcquireFrame(const SkISize& size) override;
4035

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

45+
std::unique_ptr<SurfaceFrame> AcquireFrameFromMTLLayer(
46+
const MTLFrameInfo& frame_info);
47+
48+
std::unique_ptr<SurfaceFrame> AcquireFrameFromMTLTexture(
49+
const MTLFrameInfo& frame_info);
50+
5051
void ReleaseUnusedDrawableIfNecessary();
5152

5253
FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetal);

shell/gpu/gpu_surface_metal.mm

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

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

7-
#include <QuartzCore/CAMetalLayer.h>
7+
#import <Metal/Metal.h>
88

99
#include "flutter/fml/trace_event.h"
10+
#include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
1011
#include "third_party/skia/include/core/SkSurface.h"
1112
#include "third_party/skia/include/gpu/GrBackendSurface.h"
1213
#include "third_party/skia/include/ports/SkCFObject.h"
@@ -15,25 +16,18 @@
1516

1617
namespace flutter {
1718

18-
GPUSurfaceMetal::GPUSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer,
19-
sk_sp<GrDirectContext> context,
20-
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue)
21-
: layer_(std::move(layer)),
22-
context_(std::move(context)),
23-
command_queue_(std::move(command_queue)) {
24-
layer_.get().pixelFormat = MTLPixelFormatBGRA8Unorm;
25-
// Flutter needs to read from the color attachment in cases where there are effects such as
26-
// backdrop filters.
27-
layer_.get().framebufferOnly = NO;
28-
}
19+
GPUSurfaceMetal::GPUSurfaceMetal(GPUSurfaceMetalDelegate* delegate, sk_sp<GrDirectContext> context)
20+
: delegate_(delegate),
21+
render_target_type_(delegate->GetRenderTargetType()),
22+
context_(std::move(context)) {}
2923

3024
GPUSurfaceMetal::~GPUSurfaceMetal() {
3125
ReleaseUnusedDrawableIfNecessary();
3226
}
3327

3428
// |Surface|
3529
bool GPUSurfaceMetal::IsValid() {
36-
return layer_ && context_ && command_queue_;
30+
return context_ != nullptr;
3731
}
3832

3933
// |Surface|
@@ -48,21 +42,34 @@
4842
return nullptr;
4943
}
5044

51-
const auto drawable_size = CGSizeMake(frame_size.width(), frame_size.height());
45+
ReleaseUnusedDrawableIfNecessary();
5246

53-
if (!CGSizeEqualToSize(drawable_size, layer_.get().drawableSize)) {
54-
layer_.get().drawableSize = drawable_size;
47+
MTLFrameInfo frame_info;
48+
frame_info.height = frame_size.height();
49+
frame_info.width = frame_size.width();
50+
51+
switch (render_target_type_) {
52+
case MTLRenderTargetType::kCAMetalLayer:
53+
return AcquireFrameFromMTLLayer(frame_info);
54+
case MTLRenderTargetType::kMTLTexture:
55+
return AcquireFrameFromMTLTexture(frame_info);
56+
default:
57+
FML_CHECK(false) << "unknown render target type!";
5558
}
5659

57-
ReleaseUnusedDrawableIfNecessary();
60+
return nullptr;
61+
}
5862

59-
// When there are platform views in the scene, the drawable needs to be presented in the same
60-
// transaction as the one created for platform views. When the drawable are being presented from
61-
// the raster thread, there is no such transaction.
62-
layer_.get().presentsWithTransaction = [[NSThread currentThread] isMainThread];
63+
std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrameFromMTLLayer(
64+
const MTLFrameInfo& frame_info) {
65+
auto layer = delegate_->GetCAMetalLayer(frame_info);
66+
if (!layer) {
67+
FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder.";
68+
return nullptr;
69+
}
6370

6471
auto surface = SkSurface::MakeFromCAMetalLayer(context_.get(), // context
65-
layer_.get(), // layer
72+
layer, // layer
6673
kTopLeft_GrSurfaceOrigin, // origin
6774
1, // sample count
6875
kBGRA_8888_SkColorType, // color type
@@ -72,7 +79,7 @@
7279
);
7380

7481
if (!surface) {
75-
FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
82+
FML_LOG(ERROR) << "Could not create the SkSurface from the CAMetalLayer.";
7683
return nullptr;
7784
}
7885

@@ -85,23 +92,48 @@
8592

8693
canvas->flush();
8794

88-
if (next_drawable_ == nullptr) {
89-
FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface.";
90-
return false;
91-
}
95+
bool present_result = delegate_->PresentDrawable(next_drawable_);
96+
next_drawable_ = nullptr;
97+
return present_result;
98+
};
9299

93-
auto command_buffer =
94-
fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);
100+
return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
101+
}
95102

96-
fml::scoped_nsprotocol<id<CAMetalDrawable>> drawable(
97-
reinterpret_cast<id<CAMetalDrawable>>(next_drawable_));
98-
next_drawable_ = nullptr;
103+
std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrameFromMTLTexture(
104+
const MTLFrameInfo& frame_info) {
105+
GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info);
106+
id<MTLTexture> mtl_texture = (id<MTLTexture>)(texture.texture);
107+
108+
if (!mtl_texture) {
109+
FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
110+
return nullptr;
111+
}
112+
113+
GrMtlTextureInfo info;
114+
info.fTexture.reset([mtl_texture retain]);
115+
GrBackendTexture backend_texture(frame_info.width, frame_info.height, GrMipmapped::kNo, info);
116+
117+
auto surface =
118+
SkSurface::MakeFromBackendTexture(context_.get(), backend_texture, kTopLeft_GrSurfaceOrigin,
119+
1, kBGRA_8888_SkColorType, nullptr, nullptr);
99120

100-
[command_buffer.get() commit];
101-
[command_buffer.get() waitUntilScheduled];
102-
[drawable.get() present];
121+
if (!surface) {
122+
FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
123+
return nullptr;
124+
}
125+
126+
auto submit_callback = [texture_id = texture.texture_id, this](const SurfaceFrame& surface_frame,
127+
SkCanvas* canvas) -> bool {
128+
TRACE_EVENT0("flutter", "GPUSurfaceMetal::PresentTexture");
129+
if (canvas == nullptr) {
130+
FML_DLOG(ERROR) << "Canvas not available.";
131+
return false;
132+
}
133+
134+
canvas->flush();
103135

104-
return true;
136+
return delegate_->PresentTexture(texture_id);
105137
};
106138

107139
return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
6+
7+
namespace flutter {
8+
9+
GPUSurfaceMetalDelegate::GPUSurfaceMetalDelegate(
10+
MTLRenderTargetType render_target_type)
11+
: render_target_type_(render_target_type) {}
12+
13+
GPUSurfaceMetalDelegate::~GPUSurfaceMetalDelegate() = default;
14+
15+
MTLRenderTargetType GPUSurfaceMetalDelegate::GetRenderTargetType() {
16+
return render_target_type_;
17+
}
18+
19+
} // namespace flutter
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_DELEGATE_H_
6+
#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_DELEGATE_H_
7+
8+
#include <stdint.h>
9+
10+
#include "flutter/fml/macros.h"
11+
#include "third_party/skia/include/core/SkSurface.h"
12+
#include "third_party/skia/include/gpu/mtl/GrMtlTypes.h"
13+
14+
namespace flutter {
15+
16+
struct MTLFrameInfo {
17+
uint32_t width;
18+
uint32_t height;
19+
};
20+
21+
// expected to be id<MTLDevice>
22+
typedef void* GPUMTLDeviceHandle;
23+
24+
// expected to be id<MTLCommandQueues>
25+
typedef void* GPUMTLCommandQueueHandle;
26+
27+
// expected to be CAMetalLayer*
28+
typedef void* GPUMTLLayerHandle;
29+
30+
// expected to be id<MTLTexture>
31+
typedef void* GPUMTLTextureHandle;
32+
33+
struct GPUMTLTextureInfo {
34+
intptr_t texture_id;
35+
GPUMTLTextureHandle texture;
36+
};
37+
38+
enum class MTLRenderTargetType { kMTLTexture, kCAMetalLayer };
39+
40+
//------------------------------------------------------------------------------
41+
/// @brief Interface implemented by all platform surfaces that can present
42+
/// a metal backing store to the "screen". The GPU surface
43+
/// abstraction (which abstracts the client rendering API) uses this
44+
/// delegation pattern to tell the platform surface (which abstracts
45+
/// how backing stores fulfilled by the selected client rendering
46+
/// API end up on the "screen" on a particular platform) when the
47+
/// rasterizer needs to allocate and present the software backing
48+
/// store.
49+
///
50+
/// @see |IOSurfaceMetal| and |EmbedderSurfaceMetal|.
51+
///
52+
class GPUSurfaceMetalDelegate {
53+
public:
54+
//------------------------------------------------------------------------------
55+
/// @brief Construct a new GPUSurfaceMetalDelegate object with the specified
56+
/// render_target type.
57+
///
58+
/// @see |MTLRenderTargetType|
59+
///
60+
explicit GPUSurfaceMetalDelegate(MTLRenderTargetType render_target);
61+
62+
virtual ~GPUSurfaceMetalDelegate();
63+
64+
//------------------------------------------------------------------------------
65+
/// @brief Returns the handle to the CAMetalLayer to render to. This is only
66+
/// called when the specifed render target type is `kCAMetalLayer`.
67+
///
68+
virtual GPUMTLLayerHandle GetCAMetalLayer(MTLFrameInfo frame_info) const = 0;
69+
70+
//------------------------------------------------------------------------------
71+
/// @brief Presents the drawable to the "screen". The drawable is obtained
72+
/// from the CAMetalLayer that given by `GetCAMetalLayer` call. This is only
73+
/// called when the specified render target type in `kCAMetalLayer`.
74+
///
75+
/// @see |GPUSurfaceMetalDelegate::GetCAMetalLayer|
76+
///
77+
virtual bool PresentDrawable(GrMTLHandle drawable) const = 0;
78+
79+
//------------------------------------------------------------------------------
80+
/// @brief Returns the handle to the MTLTexture to render to. This is only
81+
/// called when the specefied render target type is `kMTLTexture`.
82+
///
83+
virtual GPUMTLTextureInfo GetMTLTexture(MTLFrameInfo frame_info) const = 0;
84+
85+
//------------------------------------------------------------------------------
86+
/// @brief Presents the texture with `texture_id` to the "screen".
87+
/// `texture_id` corresponds to a texture that has been obtained by an earlier
88+
/// call to `GetMTLTexture`. This is only called when the specefied render
89+
/// target type is `kMTLTexture`.
90+
///
91+
/// @see |GPUSurfaceMetalDelegate::GetMTLTexture|
92+
///
93+
virtual bool PresentTexture(intptr_t texture_id) const = 0;
94+
95+
MTLRenderTargetType GetRenderTargetType();
96+
97+
private:
98+
const MTLRenderTargetType render_target_type_;
99+
};
100+
101+
} // namespace flutter
102+
103+
#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_DELEGATE_H_
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
assert(is_ios || is_mac)
6+
7+
import("//flutter/common/config.gni")
8+
9+
source_set("graphics") {
10+
cflags_objc = flutter_cflags_objc
11+
cflags_objcc = flutter_cflags_objcc
12+
13+
sources = [
14+
"FlutterDarwinContextMetal.h",
15+
"FlutterDarwinContextMetal.mm",
16+
]
17+
18+
deps = [
19+
"//flutter/common/graphics",
20+
"//flutter/fml",
21+
]
22+
23+
public_deps = [ "//third_party/skia" ]
24+
25+
public_configs = [ "//flutter:config" ]
26+
}

0 commit comments

Comments
 (0)