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 #include <QuartzCore/CAMetalLayer.h>
8 
9 #include "flutter/fml/trace_event.h"
10 #include "third_party/skia/include/core/SkSurface.h"
11 #include "third_party/skia/include/gpu/GrBackendSurface.h"
12 #include "third_party/skia/include/ports/SkCFObject.h"
13 
14 static_assert(!__has_feature(objc_arc), "ARC must be disabled.");
15 
16 namespace flutter {
17 
20  sk_sp<GrDirectContext> context,
21  fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue)
22  : delegate_(delegate),
23  layer_(std::move(layer)),
24  context_(std::move(context)),
25  command_queue_(std::move(command_queue)) {
26  layer_.get().pixelFormat = MTLPixelFormatBGRA8Unorm;
27  // Flutter needs to read from the color attachment in cases where there are effects such as
28  // backdrop filters.
29  layer_.get().framebufferOnly = NO;
30 }
31 
33  ReleaseUnusedDrawableIfNecessary();
34 }
35 
36 // |Surface|
37 bool GPUSurfaceMetal::IsValid() {
38  return layer_ && context_ && command_queue_;
39 }
40 
41 // |Surface|
42 std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& frame_size) {
43  if (!IsValid()) {
44  FML_LOG(ERROR) << "Metal surface was invalid.";
45  return nullptr;
46  }
47 
48  if (frame_size.isEmpty()) {
49  FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
50  return nullptr;
51  }
52 
53  const auto drawable_size = CGSizeMake(frame_size.width(), frame_size.height());
54 
55  if (!CGSizeEqualToSize(drawable_size, layer_.get().drawableSize)) {
56  layer_.get().drawableSize = drawable_size;
57  }
58 
59  ReleaseUnusedDrawableIfNecessary();
60 
61  // When there are platform views in the scene, the drawable needs to be presented in the same
62  // transaction as the one created for platform views. When the drawable are being presented from
63  // the raster thread, there is no such transaction.
64  layer_.get().presentsWithTransaction = [[NSThread currentThread] isMainThread];
65 
66  auto surface = SkSurface::MakeFromCAMetalLayer(context_.get(), // context
67  layer_.get(), // layer
68  kTopLeft_GrSurfaceOrigin, // origin
69  1, // sample count
70  kBGRA_8888_SkColorType, // color type
71  nullptr, // colorspace
72  nullptr, // surface properties
73  &next_drawable_ // drawable (transfer out)
74  );
75 
76  if (!surface) {
77  FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
78  return nullptr;
79  }
80 
81  auto submit_callback = [this](const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
82  TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit");
83  if (canvas == nullptr) {
84  FML_DLOG(ERROR) << "Canvas not available.";
85  return false;
86  }
87 
88  canvas->flush();
89 
90  if (next_drawable_ == nullptr) {
91  FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface.";
92  return false;
93  }
94 
95  auto command_buffer =
96  fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);
97 
99  reinterpret_cast<id<CAMetalDrawable>>(next_drawable_));
100  next_drawable_ = nullptr;
101 
102  [command_buffer.get() commit];
103  [command_buffer.get() waitUntilScheduled];
104  [drawable.get() present];
105 
106  return true;
107  };
108 
109  return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
110 }
111 
112 // |Surface|
113 SkMatrix GPUSurfaceMetal::GetRootTransformation() const {
114  // This backend does not currently support root surface transformations. Just
115  // return identity.
116  return {};
117 }
118 
119 // |Surface|
120 GrDirectContext* GPUSurfaceMetal::GetContext() {
121  return context_.get();
122 }
123 
124 // |Surface|
125 flutter::ExternalViewEmbedder* GPUSurfaceMetal::GetExternalViewEmbedder() {
126  return delegate_->GetExternalViewEmbedder();
127 }
128 
129 // |Surface|
130 std::unique_ptr<GLContextResult> GPUSurfaceMetal::MakeRenderContextCurrent() {
131  // This backend has no such concept.
132  return std::make_unique<GLContextDefaultResult>(true);
133 }
134 
135 void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() {
136  // If the previous surface frame was not submitted before a new one is acquired, the old drawable
137  // needs to be released. An RAII wrapper may not be used because this needs to interoperate with
138  // Skia APIs.
139  if (next_drawable_ == nullptr) {
140  return;
141  }
142 
143  CFRelease(next_drawable_);
144  next_drawable_ = nullptr;
145 }
146 
147 } // namespace flutter
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
Definition: ref_ptr.h:252
#define FML_LOG(severity)
Definition: logging.h:65
MockDelegate delegate_
GPUSurfaceMetal(GPUSurfaceDelegate *delegate, fml::scoped_nsobject< CAMetalLayer > layer, sk_sp< GrDirectContext > context, fml::scoped_nsprotocol< id< MTLCommandQueue >> command_queue)
virtual ExternalViewEmbedder * GetExternalViewEmbedder()=0
Gets the view embedder that controls how the Flutter layer hierarchy split into multiple chunks shoul...
#define FML_DLOG(severity)
Definition: logging.h:85