Flutter Engine
The Flutter Engine
gpu_surface_metal_skia.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#if !SLIMPELLER
6
7#include "flutter/shell/gpu/gpu_surface_metal_skia.h"
8
9#import <Metal/Metal.h>
10#import <QuartzCore/QuartzCore.h>
11
12#include <utility>
13
14#include "flutter/common/graphics/persistent_cache.h"
15#include "flutter/fml/make_copyable.h"
16#include "flutter/fml/platform/darwin/cf_utils.h"
17#include "flutter/fml/platform/darwin/scoped_nsobject.h"
18#include "flutter/fml/trace_event.h"
19#include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
35
36static_assert(!__has_feature(objc_arc), "ARC must be disabled.");
37
38namespace flutter {
39
40namespace {
41sk_sp<SkSurface> CreateSurfaceFromMetalTexture(GrDirectContext* context,
42 id<MTLTexture> texture,
43 GrSurfaceOrigin origin,
45 sk_sp<SkColorSpace> color_space,
46 const SkSurfaceProps* props,
48 SkSurface::ReleaseContext release_context) {
49 GrMtlTextureInfo info;
50 info.fTexture.reset([texture retain]);
51 GrBackendTexture backend_texture =
53 return SkSurfaces::WrapBackendTexture(context, backend_texture, origin, 1, color_type,
54 std::move(color_space), props, release_proc,
55 release_context);
56}
57} // namespace
58
61 bool render_to_surface)
62 : delegate_(delegate),
63 render_target_type_(delegate->GetRenderTargetType()),
64 context_(std::move(context)),
65 render_to_surface_(render_to_surface) {
66 // If this preference is explicitly set, we allow for disabling partial repaint.
67 NSNumber* disablePartialRepaint =
68 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"];
69 if (disablePartialRepaint != nil) {
70 disable_partial_repaint_ = disablePartialRepaint.boolValue;
71 }
72}
73
75
76// |Surface|
78 return context_ != nullptr;
79}
80
81void GPUSurfaceMetalSkia::PrecompileKnownSkSLsIfNecessary() {
82 auto* current_context = GetContext();
83 if (current_context == precompiled_sksl_context_) {
84 // Known SkSLs have already been prepared in this context.
85 return;
86 }
87 precompiled_sksl_context_ = current_context;
89}
90
91// |Surface|
92std::unique_ptr<SurfaceFrame> GPUSurfaceMetalSkia::AcquireFrame(const SkISize& frame_size) {
93 if (!IsValid()) {
94 FML_LOG(ERROR) << "Metal surface was invalid.";
95 return nullptr;
96 }
97
98 if (frame_size.isEmpty()) {
99 FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
100 return nullptr;
101 }
102
103 if (!render_to_surface_) {
104 return std::make_unique<SurfaceFrame>(
105 nullptr, SurfaceFrame::FramebufferInfo(),
106 [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, frame_size);
107 }
108
109 PrecompileKnownSkSLsIfNecessary();
110
111 switch (render_target_type_) {
113 return AcquireFrameFromCAMetalLayer(frame_size);
115 return AcquireFrameFromMTLTexture(frame_size);
116 default:
117 FML_CHECK(false) << "Unknown MTLRenderTargetType type.";
118 }
119
120 return nullptr;
121}
122
123std::unique_ptr<SurfaceFrame> GPUSurfaceMetalSkia::AcquireFrameFromCAMetalLayer(
124 const SkISize& frame_info) {
125 auto layer = delegate_->GetCAMetalLayer(frame_info);
126 if (!layer) {
127 FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder.";
128 return nullptr;
129 }
130
131 auto* mtl_layer = (CAMetalLayer*)layer;
132 // Get the drawable eagerly, we will need texture object to identify target framebuffer
134 reinterpret_cast<id<CAMetalDrawable>>([[mtl_layer nextDrawable] retain]));
135
136 if (!drawable.get()) {
137 FML_LOG(ERROR) << "Could not obtain drawable from the metal layer.";
138 return nullptr;
139 }
140
141 auto surface = CreateSurfaceFromMetalTexture(context_.get(), drawable.get().texture,
142 kTopLeft_GrSurfaceOrigin, // origin
143 kBGRA_8888_SkColorType, // color type
144 nullptr, // colorspace
145 nullptr, // surface properties
146 nullptr, // release proc
147 nullptr // release context
148 );
149
150 if (!surface) {
151 FML_LOG(ERROR) << "Could not create the SkSurface from the CAMetalLayer.";
152 return nullptr;
153 }
154
155 auto submit_callback = [this, drawable](const SurfaceFrame& surface_frame,
156 DlCanvas* canvas) -> bool {
157 TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit");
158 if (canvas == nullptr) {
159 FML_DLOG(ERROR) << "Canvas not available.";
160 return false;
161 }
162
163 {
164 TRACE_EVENT0("flutter", "SkCanvas::Flush");
165 canvas->Flush();
166 }
167
168 if (!disable_partial_repaint_) {
169 uintptr_t texture = reinterpret_cast<uintptr_t>(drawable.get().texture);
170 for (auto& entry : damage_) {
171 if (entry.first != texture) {
172 // Accumulate damage for other framebuffers
173 if (surface_frame.submit_info().frame_damage) {
174 entry.second.join(*surface_frame.submit_info().frame_damage);
175 }
176 }
177 }
178 // Reset accumulated damage for current framebuffer
179 damage_[texture] = SkIRect::MakeEmpty();
180 }
181
182 return delegate_->PresentDrawable(drawable);
183 };
184
185 SurfaceFrame::FramebufferInfo framebuffer_info;
186 framebuffer_info.supports_readback = true;
187
188 if (!disable_partial_repaint_) {
189 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
190 // front buffer)
191 uintptr_t texture = reinterpret_cast<uintptr_t>(drawable.get().texture);
192 auto i = damage_.find(texture);
193 if (i != damage_.end()) {
194 framebuffer_info.existing_damage = i->second;
195 }
196 framebuffer_info.supports_partial_repaint = true;
197 }
198
199 return std::make_unique<SurfaceFrame>(std::move(surface), framebuffer_info, submit_callback,
200 frame_info);
201}
202
203std::unique_ptr<SurfaceFrame> GPUSurfaceMetalSkia::AcquireFrameFromMTLTexture(
204 const SkISize& frame_info) {
205 GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info);
206 id<MTLTexture> mtl_texture = (id<MTLTexture>)(texture.texture);
207
208 if (!mtl_texture) {
209 FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
210 return nullptr;
211 }
212
213 sk_sp<SkSurface> surface = CreateSurfaceFromMetalTexture(
214 context_.get(), mtl_texture, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr,
215 nullptr, static_cast<SkSurfaces::TextureReleaseProc>(texture.destruction_callback),
216 texture.destruction_context);
217
218 if (!surface) {
219 FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
220 return nullptr;
221 }
222
223 auto submit_callback = [texture = texture, delegate = delegate_](
224 const SurfaceFrame& surface_frame, DlCanvas* canvas) -> bool {
225 TRACE_EVENT0("flutter", "GPUSurfaceMetal::PresentTexture");
226 if (canvas == nullptr) {
227 FML_DLOG(ERROR) << "Canvas not available.";
228 return false;
229 }
230
231 {
232 TRACE_EVENT0("flutter", "SkCanvas::Flush");
233 canvas->Flush();
234 }
235
236 return delegate->PresentTexture(texture);
237 };
238
239 SurfaceFrame::FramebufferInfo framebuffer_info;
240 framebuffer_info.supports_readback = true;
241
242 return std::make_unique<SurfaceFrame>(std::move(surface), framebuffer_info, submit_callback,
243 frame_info);
244}
245
246// |Surface|
247SkMatrix GPUSurfaceMetalSkia::GetRootTransformation() const {
248 // This backend does not currently support root surface transformations. Just
249 // return identity.
250 return {};
251}
252
253// |Surface|
254GrDirectContext* GPUSurfaceMetalSkia::GetContext() {
255 return context_.get();
256}
257
258// |Surface|
259std::unique_ptr<GLContextResult> GPUSurfaceMetalSkia::MakeRenderContextCurrent() {
260 // A context may either be necessary to render to the surface or to snapshot an offscreen
261 // surface. Either way, SkSL precompilation must be attempted.
262 PrecompileKnownSkSLsIfNecessary();
263
264 // This backend has no such concept.
265 return std::make_unique<GLContextDefaultResult>(true);
266}
267
268bool GPUSurfaceMetalSkia::AllowsDrawingWhenGpuDisabled() const {
269 return delegate_->AllowsDrawingWhenGpuDisabled();
270}
271
272} // namespace flutter
273
274#endif // !SLIMPELLER
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
GrSurfaceOrigin
Definition: GrTypes.h:147
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
SkColorType
Definition: SkColorType.h:19
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
#define __has_feature(x)
void * ReleaseContext
Definition: SkSurface.h:249
Interface implemented by all platform surfaces that can present a metal backing store to the "screen"...
virtual bool AllowsDrawingWhenGpuDisabled() const
Whether to allow drawing to the surface when the GPU is disabled.
virtual GPUMTLTextureInfo GetMTLTexture(const SkISize &frame_info) const =0
Returns the handle to the MTLTexture to render to. This is only called when the specified render targ...
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...
virtual bool PresentDrawable(GrMTLHandle drawable) const =0
Presents the drawable to the "screen". The drawable is obtained from the CAMetalLayer that given by G...
GPUSurfaceMetalSkia(GPUSurfaceMetalDelegate *delegate, sk_sp< GrDirectContext > context, bool render_to_surface=true)
static PersistentCache * GetCacheForProcess()
size_t PrecompileKnownSkSLs(GrDirectContext *context) const
Precompile SkSLs packaged with the application and gathered during previous runs in the given context...
T * get() const
Definition: SkRefCnt.h:303
MockDelegate delegate_
VkSurfaceKHR surface
Definition: main.cc:49
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
FlTexture * texture
SK_API GrBackendTexture MakeMtl(int width, int height, skgpu::Mipmapped, const GrMtlTextureInfo &mtlInfo, std::string_view label={})
SK_API sk_sp< SkSurface > WrapBackendTexture(GrRecordingContext *context, const GrBackendTexture &backendTexture, GrSurfaceOrigin origin, int sampleCnt, SkColorType colorType, sk_sp< SkColorSpace > colorSpace, const SkSurfaceProps *surfaceProps, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
void(*)(ReleaseContext) TextureReleaseProc
static thread_local std::unique_ptr< int > current_context
Definition: ref_ptr.h:256
flutter::DlCanvas DlCanvas
uint32_t color_type
static constexpr SkIRect MakeEmpty()
Definition: SkRect.h:45
Definition: SkSize.h:16
bool isEmpty() const
Definition: SkSize.h:31
#define ERROR(message)
Definition: elf_loader.cc:260
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131