Flutter Engine
gpu_surface_metal.mm
Go to the documentation of this file.
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.h"
6 
7 #import <Metal/Metal.h>
8 
9 #include "flutter/common/graphics/persistent_cache.h"
10 #include "flutter/fml/make_copyable.h"
11 #include "flutter/fml/platform/darwin/cf_utils.h"
12 #include "flutter/fml/trace_event.h"
13 #include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
14 #include "third_party/skia/include/core/SkSurface.h"
15 #include "third_party/skia/include/gpu/GrBackendSurface.h"
16 #include "third_party/skia/include/ports/SkCFObject.h"
17 
18 static_assert(!__has_feature(objc_arc), "ARC must be disabled.");
19 
20 namespace flutter {
21 
23  sk_sp<GrDirectContext> context,
24  bool render_to_surface)
25  : delegate_(delegate),
26  render_target_type_(delegate->GetRenderTargetType()),
27  context_(std::move(context)),
28  render_to_surface_(render_to_surface) {}
29 
31  ReleaseUnusedDrawableIfNecessary();
32 }
33 
34 // |Surface|
36  return context_ != nullptr;
37 }
38 
39 void GPUSurfaceMetal::PrecompileKnownSkSLsIfNecessary() {
40  auto* current_context = GetContext();
41  if (current_context == precompiled_sksl_context_) {
42  // Known SkSLs have already been prepared in this context.
43  return;
44  }
45  precompiled_sksl_context_ = current_context;
47 }
48 
49 // |Surface|
50 std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& frame_size) {
51  if (!IsValid()) {
52  FML_LOG(ERROR) << "Metal surface was invalid.";
53  return nullptr;
54  }
55 
56  if (frame_size.isEmpty()) {
57  FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
58  return nullptr;
59  }
60 
61  if (!render_to_surface_) {
62  return std::make_unique<SurfaceFrame>(
63  nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { return true; });
64  }
65 
66  PrecompileKnownSkSLsIfNecessary();
67 
68  switch (render_target_type_) {
70  return AcquireFrameFromCAMetalLayer(frame_size);
72  return AcquireFrameFromMTLTexture(frame_size);
73  default:
74  FML_CHECK(false) << "Unknown MTLRenderTargetType type.";
75  }
76 
77  return nullptr;
78 }
79 
80 std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrameFromCAMetalLayer(
81  const SkISize& frame_info) {
82  auto layer = delegate_->GetCAMetalLayer(frame_info);
83  if (!layer) {
84  FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder.";
85  return nullptr;
86  }
87 
88  ReleaseUnusedDrawableIfNecessary();
89  sk_sp<SkSurface> surface =
90  SkSurface::MakeFromCAMetalLayer(context_.get(), // context
91  layer, // layer
92  kTopLeft_GrSurfaceOrigin, // origin
93  1, // sample count
94  kBGRA_8888_SkColorType, // color type
95  nullptr, // colorspace
96  nullptr, // surface properties
97  &next_drawable_ // drawable (transfer out)
98  );
99 
100  if (!surface) {
101  FML_LOG(ERROR) << "Could not create the SkSurface from the CAMetalLayer.";
102  return nullptr;
103  }
104 
105  auto submit_callback = [this](const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
106  TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit");
107  if (canvas == nullptr) {
108  FML_DLOG(ERROR) << "Canvas not available.";
109  return false;
110  }
111 
112  canvas->flush();
113 
114  GrMTLHandle drawable = next_drawable_;
115  if (!drawable) {
116  FML_DLOG(ERROR) << "Unable to obtain a metal drawable.";
117  return false;
118  }
119 
120  return delegate_->PresentDrawable(drawable);
121  };
122 
123  return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
124 }
125 
126 std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrameFromMTLTexture(
127  const SkISize& frame_info) {
128  GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info);
129  id<MTLTexture> mtl_texture = (id<MTLTexture>)(texture.texture);
130 
131  if (!mtl_texture) {
132  FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
133  return nullptr;
134  }
135 
136  GrMtlTextureInfo info;
137  info.fTexture.reset([mtl_texture retain]);
138  GrBackendTexture backend_texture(frame_info.width(), frame_info.height(), GrMipmapped::kNo, info);
139 
140  sk_sp<SkSurface> surface =
141  SkSurface::MakeFromBackendTexture(context_.get(), backend_texture, kTopLeft_GrSurfaceOrigin,
142  1, kBGRA_8888_SkColorType, nullptr, nullptr);
143 
144  if (!surface) {
145  FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
146  return nullptr;
147  }
148 
149  auto submit_callback = [texture = texture, delegate = delegate_](
150  const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
151  TRACE_EVENT0("flutter", "GPUSurfaceMetal::PresentTexture");
152  if (canvas == nullptr) {
153  FML_DLOG(ERROR) << "Canvas not available.";
154  return false;
155  }
156 
157  canvas->flush();
158 
159  return delegate->PresentTexture(texture);
160  };
161 
162  return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
163 }
164 
165 // |Surface|
166 SkMatrix GPUSurfaceMetal::GetRootTransformation() const {
167  // This backend does not currently support root surface transformations. Just
168  // return identity.
169  return {};
170 }
171 
172 // |Surface|
173 GrDirectContext* GPUSurfaceMetal::GetContext() {
174  return context_.get();
175 }
176 
177 // |Surface|
178 std::unique_ptr<GLContextResult> GPUSurfaceMetal::MakeRenderContextCurrent() {
179  // A context may either be necessary to render to the surface or to snapshot an offscreen
180  // surface. Either way, SkSL precompilation must be attempted.
181  PrecompileKnownSkSLsIfNecessary();
182 
183  // This backend has no such concept.
184  return std::make_unique<GLContextDefaultResult>(true);
185 }
186 
187 bool GPUSurfaceMetal::AllowsDrawingWhenGpuDisabled() const {
188  return delegate_->AllowsDrawingWhenGpuDisabled();
189 }
190 
191 void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() {
192  // If the previous surface frame was not submitted before a new one is acquired, the old drawable
193  // needs to be released. An RAII wrapper may not be used because this needs to interoperate with
194  // Skia APIs.
195  if (next_drawable_ == nullptr) {
196  return;
197  }
198 
199  CFRelease(next_drawable_);
200  next_drawable_ = nullptr;
201 }
202 
203 } // namespace flutter
virtual GPUCAMetalLayerHandle GetCAMetalLayer(const SkISize &frame_info) const =0
Returns the handle to the CAMetalLayer to render to. This is only called when the specified render ta...
G_BEGIN_DECLS FlTexture * texture
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
MockDelegate delegate_
size_t PrecompileKnownSkSLs(GrDirectContext *context) const
Precompile SkSLs packaged with the application and gathered during previous runs in the given context...
FML_THREAD_LOCAL fml::ThreadLocalUniquePtr< int > current_context
virtual bool AllowsDrawingWhenGpuDisabled() const
Whether to allow drawing to the surface when the GPU is disabled.
Interface implemented by all platform surfaces that can present a metal backing store to the "screen"...
Definition: ref_ptr.h:252
#define FML_LOG(severity)
Definition: logging.h:65
GPUSurfaceMetal(GPUSurfaceMetalDelegate *delegate, sk_sp< GrDirectContext > context, bool render_to_surface=true)
virtual GPUMTLTextureInfo GetMTLTexture(const SkISize &frame_info) const =0
Returns the handle to the MTLTexture to render to. This is only called when the specefied render targ...
virtual bool PresentDrawable(GrMTLHandle drawable) const =0
Presents the drawable to the "screen". The drawable is obtained from the CAMetalLayer that given by G...
static PersistentCache * GetCacheForProcess()
#define FML_CHECK(condition)
Definition: logging.h:68
#define FML_DLOG(severity)
Definition: logging.h:85