Flutter Engine
The Flutter Engine
gpu_surface_metal_impeller.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_impeller.h"
6
7#import <Metal/Metal.h>
8#import <QuartzCore/QuartzCore.h>
9
10#include "flutter/common/settings.h"
11#include "flutter/fml/make_copyable.h"
12#include "flutter/fml/mapping.h"
13#include "flutter/fml/trace_event.h"
17
18static_assert(!__has_feature(objc_arc), "ARC must be disabled.");
19
20#define ENABLE_EXPERIMENTAL_CANVAS false
21
22namespace flutter {
23
24static std::shared_ptr<impeller::Renderer> CreateImpellerRenderer(
25 std::shared_ptr<impeller::Context> context) {
26 auto renderer = std::make_shared<impeller::Renderer>(std::move(context));
27 if (!renderer->IsValid()) {
28 FML_LOG(ERROR) << "Could not create valid Impeller Renderer.";
29 return nullptr;
30 }
31 return renderer;
32}
33
35 const std::shared_ptr<impeller::Context>& context,
36 bool render_to_surface)
37 : delegate_(delegate),
38 render_target_type_(delegate->GetRenderTargetType()),
39 impeller_renderer_(CreateImpellerRenderer(context)),
40 aiks_context_(
41 std::make_shared<impeller::AiksContext>(impeller_renderer_ ? context : nullptr,
42 impeller::TypographerContextSkia::Make())),
43 render_to_surface_(render_to_surface) {
44 // If this preference is explicitly set, we allow for disabling partial repaint.
45 NSNumber* disablePartialRepaint =
46 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"];
47 if (disablePartialRepaint != nil) {
48 disable_partial_repaint_ = disablePartialRepaint.boolValue;
49 }
50}
51
53
54// |Surface|
56 return !!aiks_context_ && aiks_context_->IsValid();
57}
58
59// |Surface|
60std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrame(const SkISize& frame_size) {
61 TRACE_EVENT0("impeller", "GPUSurfaceMetalImpeller::AcquireFrame");
62
63 if (!IsValid()) {
64 FML_LOG(ERROR) << "Metal surface was invalid.";
65 return nullptr;
66 }
67
68 if (frame_size.isEmpty()) {
69 FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
70 return nullptr;
71 }
72
73 if (!render_to_surface_) {
74 return std::make_unique<SurfaceFrame>(
75 nullptr, SurfaceFrame::FramebufferInfo(),
76 [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, frame_size);
77 }
78
79 switch (render_target_type_) {
81 return AcquireFrameFromCAMetalLayer(frame_size);
83 return AcquireFrameFromMTLTexture(frame_size);
84 default:
85 FML_CHECK(false) << "Unknown MTLRenderTargetType type.";
86 }
87
88 return nullptr;
89}
90
91std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLayer(
92 const SkISize& frame_size) {
93 auto layer = delegate_->GetCAMetalLayer(frame_size);
94
95 if (!layer) {
96 FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder.";
97 return nullptr;
98 }
99
100 auto* mtl_layer = (CAMetalLayer*)layer;
101
103 impeller_renderer_->GetContext(), mtl_layer);
104 if (!drawable) {
105 return nullptr;
106 }
108 last_texture_.reset([drawable.texture retain]);
109 }
110
111 id<MTLTexture> last_texture = static_cast<id<MTLTexture>>(last_texture_);
112 SurfaceFrame::SubmitCallback submit_callback =
113 fml::MakeCopyable([damage = damage_,
114 disable_partial_repaint = disable_partial_repaint_, //
115 renderer = impeller_renderer_, //
116 aiks_context = aiks_context_, //
117 drawable, //
118 last_texture //
119 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
120 if (!aiks_context) {
121 return false;
122 }
123
124 auto display_list = surface_frame.BuildDisplayList();
125 if (!display_list) {
126 FML_LOG(ERROR) << "Could not build display list for surface frame.";
127 return false;
128 }
129
130 if (!disable_partial_repaint && damage) {
131 uintptr_t texture = reinterpret_cast<uintptr_t>(last_texture);
132
133 for (auto& entry : *damage) {
134 if (entry.first != texture) {
135 // Accumulate damage for other framebuffers
136 if (surface_frame.submit_info().frame_damage) {
137 entry.second.join(*surface_frame.submit_info().frame_damage);
138 }
139 }
140 }
141 // Reset accumulated damage for current framebuffer
142 (*damage)[texture] = SkIRect::MakeEmpty();
143 }
144
145 std::optional<impeller::IRect> clip_rect;
146 if (surface_frame.submit_info().buffer_damage.has_value()) {
147 auto buffer_damage = surface_frame.submit_info().buffer_damage;
148 clip_rect = impeller::IRect::MakeXYWH(buffer_damage->x(), buffer_damage->y(),
149 buffer_damage->width(), buffer_damage->height());
150 }
151
153 drawable, clip_rect);
154
155 // The surface may be null if we failed to allocate the onscreen render target
156 // due to running out of memory.
157 if (!surface) {
158 return false;
159 }
160
161 if (clip_rect && clip_rect->IsEmpty()) {
162 return surface->Present();
163 }
164
165 impeller::IRect cull_rect = surface->coverage();
166 SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
167
168#if ENABLE_EXPERIMENTAL_CANVAS
169 impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(),
171 display_list->Dispatch(collector, sk_cull_rect);
172 return renderer->Render(
173 std::move(surface),
174 fml::MakeCopyable([aiks_context, &display_list, &cull_rect,
175 &sk_cull_rect](impeller::RenderTarget& render_target) -> bool {
176 impeller::ExperimentalDlDispatcher impeller_dispatcher(
177 aiks_context->GetContentContext(), render_target,
178 display_list->root_has_backdrop_filter(), display_list->max_root_blend_mode(),
179 cull_rect);
180 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
181 impeller_dispatcher.FinishRecording();
182 aiks_context->GetContentContext().GetTransientsBuffer().Reset();
183 aiks_context->GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
184 return true;
185 }));
186#else
187 impeller::DlDispatcher impeller_dispatcher(cull_rect);
188 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
189 auto picture = impeller_dispatcher.EndRecordingAsPicture();
190
191 return renderer->Render(
192 std::move(surface),
193 fml::MakeCopyable([aiks_context, picture = std::move(picture)](
194 impeller::RenderTarget& render_target) -> bool {
195 return aiks_context->Render(picture, render_target, /*reset_host_buffer=*/true);
196 }));
197#endif
198 });
199
200 SurfaceFrame::FramebufferInfo framebuffer_info;
201 framebuffer_info.supports_readback = true;
202
203 if (!disable_partial_repaint_) {
204 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
205 // front buffer)
206 uintptr_t texture = reinterpret_cast<uintptr_t>(drawable.texture);
207 auto i = damage_->find(texture);
208 if (i != damage_->end()) {
209 framebuffer_info.existing_damage = i->second;
210 }
211 framebuffer_info.supports_partial_repaint = true;
212 }
213
214 return std::make_unique<SurfaceFrame>(nullptr, // surface
215 framebuffer_info, // framebuffer info
216 submit_callback, // submit callback
217 frame_size, // frame size
218 nullptr, // context result
219 true // display list fallback
220 );
221}
222
223std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTexture(
224 const SkISize& frame_size) {
225 GPUMTLTextureInfo texture_info = delegate_->GetMTLTexture(frame_size);
226 id<MTLTexture> mtl_texture = (id<MTLTexture>)(texture_info.texture);
227
228 if (!mtl_texture) {
229 FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
230 return nullptr;
231 }
232
234 last_texture_.reset([mtl_texture retain]);
235 }
236
237 SurfaceFrame::SubmitCallback submit_callback =
238 fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
239 damage = damage_,
240 renderer = impeller_renderer_, //
241 aiks_context = aiks_context_, //
242 texture_info, //
243 mtl_texture, //
244 delegate = delegate_ //
245 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
246 if (!aiks_context) {
247 return false;
248 }
249
250 auto display_list = surface_frame.BuildDisplayList();
251 if (!display_list) {
252 FML_LOG(ERROR) << "Could not build display list for surface frame.";
253 return false;
254 }
255
256 if (!disable_partial_repaint && damage) {
257 uintptr_t texture_ptr = reinterpret_cast<uintptr_t>(mtl_texture);
258
259 for (auto& entry : *damage) {
260 if (entry.first != texture_ptr) {
261 // Accumulate damage for other framebuffers
262 if (surface_frame.submit_info().frame_damage) {
263 entry.second.join(*surface_frame.submit_info().frame_damage);
264 }
265 }
266 }
267 // Reset accumulated damage for current framebuffer
268 (*damage)[texture_ptr] = SkIRect::MakeEmpty();
269 }
270
271 std::optional<impeller::IRect> clip_rect;
272 if (surface_frame.submit_info().buffer_damage.has_value()) {
273 auto buffer_damage = surface_frame.submit_info().buffer_damage;
274 clip_rect = impeller::IRect::MakeXYWH(buffer_damage->x(), buffer_damage->y(),
275 buffer_damage->width(), buffer_damage->height());
276 }
277
278 auto surface =
279 impeller::SurfaceMTL::MakeFromTexture(renderer->GetContext(), mtl_texture, clip_rect);
280
281 if (clip_rect && clip_rect->IsEmpty()) {
282 return surface->Present();
283 }
284
285 impeller::IRect cull_rect = surface->coverage();
286 SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
287#if ENABLE_EXPERIMENTAL_CANVAS
288 impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(),
290 display_list->Dispatch(collector, sk_cull_rect);
291 bool render_result = renderer->Render(
292 std::move(surface),
293 fml::MakeCopyable([aiks_context, &display_list, &cull_rect,
294 &sk_cull_rect](impeller::RenderTarget& render_target) -> bool {
295 impeller::ExperimentalDlDispatcher impeller_dispatcher(
296 aiks_context->GetContentContext(), render_target, cull_rect);
297 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
298 impeller_dispatcher.FinishRecording();
299 aiks_context->GetContentContext().GetTransientsBuffer().Reset();
300 aiks_context->GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
301 return true;
302 }));
303#else
304 impeller::DlDispatcher impeller_dispatcher(cull_rect);
305 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
306 auto picture = impeller_dispatcher.EndRecordingAsPicture();
307
308 bool render_result = renderer->Render(
309 std::move(surface),
310 fml::MakeCopyable([aiks_context, picture = std::move(picture)](
311 impeller::RenderTarget& render_target) -> bool {
312 return aiks_context->Render(picture, render_target, /*reset_host_buffer=*/true);
313 }));
314#endif
315 if (!render_result) {
316 FML_LOG(ERROR) << "Failed to render Impeller frame";
317 return false;
318 }
319
320 return delegate->PresentTexture(texture_info);
321 });
322
323 SurfaceFrame::FramebufferInfo framebuffer_info;
324 framebuffer_info.supports_readback = true;
325
326 if (!disable_partial_repaint_) {
327 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
328 // front buffer)
329 uintptr_t texture = reinterpret_cast<uintptr_t>(mtl_texture);
330 auto i = damage_->find(texture);
331 if (i != damage_->end()) {
332 framebuffer_info.existing_damage = i->second;
333 }
334 framebuffer_info.supports_partial_repaint = true;
335 }
336
337 return std::make_unique<SurfaceFrame>(nullptr, // surface
338 framebuffer_info, // framebuffer info
339 submit_callback, // submit callback
340 frame_size, // frame size
341 nullptr, // context result
342 true // display list fallback
343 );
344}
345
346// |Surface|
347SkMatrix GPUSurfaceMetalImpeller::GetRootTransformation() const {
348 // This backend does not currently support root surface transformations. Just
349 // return identity.
350 return {};
351}
352
353// |Surface|
354GrDirectContext* GPUSurfaceMetalImpeller::GetContext() {
355 return nullptr;
356}
357
358// |Surface|
359std::unique_ptr<GLContextResult> GPUSurfaceMetalImpeller::MakeRenderContextCurrent() {
360 // This backend has no such concept.
361 return std::make_unique<GLContextDefaultResult>(true);
362}
363
364bool GPUSurfaceMetalImpeller::AllowsDrawingWhenGpuDisabled() const {
365 return delegate_->AllowsDrawingWhenGpuDisabled();
366}
367
368// |Surface|
369bool GPUSurfaceMetalImpeller::EnableRasterCache() const {
370 return false;
371}
372
373// |Surface|
374std::shared_ptr<impeller::AiksContext> GPUSurfaceMetalImpeller::GetAiksContext() const {
375 return aiks_context_;
376}
377
379 if (!(last_texture_ && [last_texture_ conformsToProtocol:@protocol(MTLTexture)])) {
380 return {};
381 }
382 id<MTLTexture> texture = last_texture_.get();
383 int bytesPerPixel = 0;
384 std::string pixel_format;
385 switch (texture.pixelFormat) {
386 case MTLPixelFormatBGR10_XR:
387 bytesPerPixel = 4;
388 pixel_format = "MTLPixelFormatBGR10_XR";
389 break;
390 case MTLPixelFormatBGRA10_XR:
391 bytesPerPixel = 8;
392 pixel_format = "MTLPixelFormatBGRA10_XR";
393 break;
394 case MTLPixelFormatBGRA8Unorm:
395 bytesPerPixel = 4;
396 pixel_format = "MTLPixelFormatBGRA8Unorm";
397 break;
398 case MTLPixelFormatRGBA16Float:
399 bytesPerPixel = 8;
400 pixel_format = "MTLPixelFormatRGBA16Float";
401 break;
402 default:
403 return {};
404 }
405
406 // Zero initialized so that errors are easier to find at the cost of
407 // performance.
409 SkData::MakeZeroInitialized(texture.width * texture.height * bytesPerPixel);
410 [texture getBytes:result->writable_data()
411 bytesPerRow:texture.width * bytesPerPixel
412 fromRegion:MTLRegionMake2D(0, 0, texture.width, texture.height)
413 mipmapLevel:0];
414 return {
415 .pixel_format = pixel_format,
416 .data = result,
417 };
418}
419
420} // namespace flutter
#define __has_feature(x)
static sk_sp< SkData > MakeZeroInitialized(size_t length)
Definition: SkData.cpp:120
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...
GPUSurfaceMetalImpeller(GPUSurfaceMetalDelegate *delegate, const std::shared_ptr< impeller::Context > &context, bool render_to_surface=true)
virtual Surface::SurfaceData GetSurfaceData() const override
std::function< bool(SurfaceFrame &surface_frame, DlCanvas *canvas)> SubmitCallback
Definition: surface_frame.h:27
T get() const __attribute((ns_returns_not_retained))
void reset(NST object=Traits::InvalidValue(), scoped_policy::OwnershipPolicy policy=scoped_policy::OwnershipPolicy::kAssume)
static std::unique_ptr< SurfaceMTL > MakeFromTexture(const std::shared_ptr< Context > &context, id< MTLTexture > texture, std::optional< IRect > clip_rect, id< CAMetalDrawable > drawable=nil)
Definition: surface_mtl.mm:131
static id< CAMetalDrawable > GetMetalDrawableAndValidate(const std::shared_ptr< Context > &context, CAMetalLayer *layer)
Wraps the current drawable of the given Metal layer to create a surface Impeller can render to....
Definition: surface_mtl.mm:25
static std::unique_ptr< SurfaceMTL > MakeFromMetalLayerDrawable(const std::shared_ptr< Context > &context, id< CAMetalDrawable > drawable, std::optional< IRect > clip_rect=std::nullopt)
Definition: surface_mtl.mm:123
Performs a first pass over the display list to collect all text frames.
MockDelegate delegate_
VkSurfaceKHR surface
Definition: main.cc:49
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
FlTexture * texture
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
static std::shared_ptr< impeller::Renderer > CreateImpellerRenderer(std::shared_ptr< impeller::Context > context)
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
Definition: ref_ptr.h:256
flutter::DlCanvas DlCanvas
Definition: SkRect.h:32
static constexpr SkIRect MakeEmpty()
Definition: SkRect.h:45
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
Definition: SkSize.h:16
bool isEmpty() const
Definition: SkSize.h:31
static constexpr bool kSurfaceDataAccessible
Definition: settings.h:113
A screenshot of the surface's raw data.
Definition: surface.h:27
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:337
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:331
#define ERROR(message)
Definition: elf_loader.cc:260
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131