• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "third_party/skia/include/core/SkSurface.h"
10#include "third_party/skia/include/gpu/GrBackendSurface.h"
11#include "third_party/skia/include/ports/SkCFObject.h"
12
13namespace flutter {
14
15GPUSurfaceMetal::GPUSurfaceMetal(fml::scoped_nsobject<CAMetalLayer> layer)
16    : layer_(std::move(layer)) {
17  if (!layer_) {
18    FML_LOG(ERROR) << "Could not create metal surface because of invalid layer.";
19    return;
20  }
21
22  layer.get().pixelFormat = MTLPixelFormatBGRA8Unorm;
23
24  auto metal_device = fml::scoped_nsprotocol<id<MTLDevice>>([layer_.get().device retain]);
25  auto metal_queue = fml::scoped_nsprotocol<id<MTLCommandQueue>>([metal_device newCommandQueue]);
26
27  if (!metal_device || !metal_queue) {
28    FML_LOG(ERROR) << "Could not create metal device or queue.";
29    return;
30  }
31
32  command_queue_ = metal_queue;
33
34  // The context creation routine accepts arguments using transfer semantics.
35  auto context = GrContext::MakeMetal(metal_device.release(), metal_queue.release());
36  if (!context) {
37    FML_LOG(ERROR) << "Could not create Skia metal context.";
38    return;
39  }
40
41  context_ = context;
42}
43
44GPUSurfaceMetal::~GPUSurfaceMetal() = default;
45
46// |Surface|
47bool GPUSurfaceMetal::IsValid() {
48  return layer_ && context_ && command_queue_;
49}
50
51// |Surface|
52std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size) {
53  if (!IsValid()) {
54    FML_LOG(ERROR) << "Metal surface was invalid.";
55    return nullptr;
56  }
57
58  if (size.isEmpty()) {
59    FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
60    return nullptr;
61  }
62
63  const auto bounds = layer_.get().bounds.size;
64  if (bounds.width <= 0.0 || bounds.height <= 0.0) {
65    FML_LOG(ERROR) << "Metal layer bounds were invalid.";
66    return nullptr;
67  }
68
69  const auto scale = layer_.get().contentsScale;
70
71  auto next_drawable = fml::scoped_nsprotocol<id<CAMetalDrawable>>([[layer_ nextDrawable] retain]);
72  if (!next_drawable) {
73    FML_LOG(ERROR) << "Could not acquire next metal drawable.";
74    return nullptr;
75  }
76
77  auto metal_texture = fml::scoped_nsprotocol<id<MTLTexture>>([next_drawable.get().texture retain]);
78  if (!metal_texture) {
79    FML_LOG(ERROR) << "Could not acquire metal texture from drawable.";
80    return nullptr;
81  }
82
83  GrMtlTextureInfo metal_texture_info;
84  metal_texture_info.fTexture.reset(SkCFSafeRetain(metal_texture.get()));
85
86  GrBackendRenderTarget metal_render_target(bounds.width * scale,   // width
87                                            bounds.height * scale,  // height
88                                            1,                      // sample count
89                                            metal_texture_info      // metal texture info
90  );
91
92  auto command_buffer =
93      fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);
94
95  SkSurface::RenderTargetReleaseProc release_proc = [](SkSurface::ReleaseContext context) {
96    [reinterpret_cast<id>(context) release];
97  };
98
99  auto surface =
100      SkSurface::MakeFromBackendRenderTarget(context_.get(),            // context
101                                             metal_render_target,       // backend render target
102                                             kTopLeft_GrSurfaceOrigin,  // origin
103                                             kBGRA_8888_SkColorType,    // color type
104                                             nullptr,                   // colorspace
105                                             nullptr,                   // surface properties
106                                             release_proc,              // release proc
107                                             metal_texture.release()    // release context (texture)
108      );
109
110  if (!surface) {
111    FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
112    return nullptr;
113  }
114
115  auto submit_callback = [drawable = next_drawable, command_buffer](
116                             const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
117    canvas->flush();
118    [command_buffer.get() presentDrawable:drawable.get()];
119    [command_buffer.get() commit];
120    return true;
121  };
122
123  return std::make_unique<SurfaceFrame>(std::move(surface), submit_callback);
124}
125
126// |Surface|
127SkMatrix GPUSurfaceMetal::GetRootTransformation() const {
128  // This backend does not currently support root surface transformations. Just
129  // return identity.
130  SkMatrix matrix;
131  matrix.reset();
132  return matrix;
133}
134
135// |Surface|
136GrContext* GPUSurfaceMetal::GetContext() {
137  return context_.get();
138}
139
140// |Surface|
141bool GPUSurfaceMetal::MakeRenderContextCurrent() {
142  // This backend has no such concept.
143  return true;
144}
145
146}  // namespace flutter
147