Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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>(
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#if ENABLE_EXPERIMENTAL_CANVAS
168 impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(),
170 display_list->Dispatch(collector, sk_cull_rect);
171 return renderer->Render(
172 std::move(surface),
173 fml::MakeCopyable([aiks_context, &display_list, &cull_rect,
174 &sk_cull_rect](impeller::RenderTarget& render_target) -> bool {
175 impeller::ExperimentalDlDispatcher impeller_dispatcher(
176 aiks_context->GetContentContext(), render_target, cull_rect);
177 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
178 impeller_dispatcher.FinishRecording();
179 aiks_context->GetContentContext().GetTransientsBuffer().Reset();
180 aiks_context->GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
181 return true;
182 }));
183#else
184 impeller::DlDispatcher impeller_dispatcher(cull_rect);
185 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
186 auto picture = impeller_dispatcher.EndRecordingAsPicture();
187
188 return renderer->Render(
189 std::move(surface),
190 fml::MakeCopyable([aiks_context, picture = std::move(picture)](
191 impeller::RenderTarget& render_target) -> bool {
192 return aiks_context->Render(picture, render_target, /*reset_host_buffer=*/true);
193 }));
194#endif
195 });
196
197 SurfaceFrame::FramebufferInfo framebuffer_info;
198 framebuffer_info.supports_readback = true;
199
200 if (!disable_partial_repaint_) {
201 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
202 // front buffer)
203 uintptr_t texture = reinterpret_cast<uintptr_t>(drawable.texture);
204 auto i = damage_->find(texture);
205 if (i != damage_->end()) {
206 framebuffer_info.existing_damage = i->second;
207 }
208 framebuffer_info.supports_partial_repaint = true;
209 }
210
211 return std::make_unique<SurfaceFrame>(nullptr, // surface
212 framebuffer_info, // framebuffer info
213 submit_callback, // submit callback
214 frame_size, // frame size
215 nullptr, // context result
216 true // display list fallback
217 );
218}
219
220std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTexture(
221 const SkISize& frame_size) {
222 GPUMTLTextureInfo texture_info = delegate_->GetMTLTexture(frame_size);
223 id<MTLTexture> mtl_texture = (id<MTLTexture>)(texture_info.texture);
224
225 if (!mtl_texture) {
226 FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
227 return nullptr;
228 }
229
231 last_texture_.reset([mtl_texture retain]);
232 }
233
234 SurfaceFrame::SubmitCallback submit_callback =
235 fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
236 damage = damage_,
237 renderer = impeller_renderer_, //
238 aiks_context = aiks_context_, //
239 texture_info, //
240 mtl_texture, //
241 delegate = delegate_ //
242 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
243 if (!aiks_context) {
244 return false;
245 }
246
247 auto display_list = surface_frame.BuildDisplayList();
248 if (!display_list) {
249 FML_LOG(ERROR) << "Could not build display list for surface frame.";
250 return false;
251 }
252
253 if (!disable_partial_repaint && damage) {
254 uintptr_t texture_ptr = reinterpret_cast<uintptr_t>(mtl_texture);
255
256 for (auto& entry : *damage) {
257 if (entry.first != texture_ptr) {
258 // Accumulate damage for other framebuffers
259 if (surface_frame.submit_info().frame_damage) {
260 entry.second.join(*surface_frame.submit_info().frame_damage);
261 }
262 }
263 }
264 // Reset accumulated damage for current framebuffer
265 (*damage)[texture_ptr] = SkIRect::MakeEmpty();
266 }
267
268 std::optional<impeller::IRect> clip_rect;
269 if (surface_frame.submit_info().buffer_damage.has_value()) {
270 auto buffer_damage = surface_frame.submit_info().buffer_damage;
271 clip_rect = impeller::IRect::MakeXYWH(buffer_damage->x(), buffer_damage->y(),
272 buffer_damage->width(), buffer_damage->height());
273 }
274
275 auto surface =
276 impeller::SurfaceMTL::MakeFromTexture(renderer->GetContext(), mtl_texture, clip_rect);
277
278 if (clip_rect && clip_rect->IsEmpty()) {
279 return surface->Present();
280 }
281
282 impeller::IRect cull_rect = surface->coverage();
283 SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
284#if ENABLE_EXPERIMENTAL_CANVAS
285 impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(),
287 display_list->Dispatch(collector, sk_cull_rect);
288 bool render_result = renderer->Render(
289 std::move(surface),
290 fml::MakeCopyable([aiks_context, &display_list, &cull_rect,
291 &sk_cull_rect](impeller::RenderTarget& render_target) -> bool {
292 impeller::ExperimentalDlDispatcher impeller_dispatcher(
293 aiks_context->GetContentContext(), render_target, cull_rect);
294 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
295 impeller_dispatcher.FinishRecording();
296 aiks_context->GetContentContext().GetTransientsBuffer().Reset();
297 aiks_context->GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
298 return true;
299 }));
300#else
301 impeller::DlDispatcher impeller_dispatcher(cull_rect);
302 display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
303 auto picture = impeller_dispatcher.EndRecordingAsPicture();
304
305 bool render_result = renderer->Render(
306 std::move(surface),
307 fml::MakeCopyable([aiks_context, picture = std::move(picture)](
308 impeller::RenderTarget& render_target) -> bool {
309 return aiks_context->Render(picture, render_target, /*reset_host_buffer=*/true);
310 }));
311#endif
312 if (!render_result) {
313 FML_LOG(ERROR) << "Failed to render Impeller frame";
314 return false;
315 }
316
317 return delegate->PresentTexture(texture_info);
318 });
319
320 SurfaceFrame::FramebufferInfo framebuffer_info;
321 framebuffer_info.supports_readback = true;
322
323 if (!disable_partial_repaint_) {
324 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
325 // front buffer)
326 uintptr_t texture = reinterpret_cast<uintptr_t>(mtl_texture);
327 auto i = damage_->find(texture);
328 if (i != damage_->end()) {
329 framebuffer_info.existing_damage = i->second;
330 }
331 framebuffer_info.supports_partial_repaint = true;
332 }
333
334 return std::make_unique<SurfaceFrame>(nullptr, // surface
335 framebuffer_info, // framebuffer info
336 submit_callback, // submit callback
337 frame_size, // frame size
338 nullptr, // context result
339 true // display list fallback
340 );
341}
342
343// |Surface|
345 // This backend does not currently support root surface transformations. Just
346 // return identity.
347 return {};
348}
349
350// |Surface|
354
355// |Surface|
356std::unique_ptr<GLContextResult> GPUSurfaceMetalImpeller::MakeRenderContextCurrent() {
357 // This backend has no such concept.
358 return std::make_unique<GLContextDefaultResult>(true);
359}
360
364
365// |Surface|
367 return false;
368}
369
370// |Surface|
371std::shared_ptr<impeller::AiksContext> GPUSurfaceMetalImpeller::GetAiksContext() const {
372 return aiks_context_;
373}
374
376 if (!(last_texture_ && [last_texture_ conformsToProtocol:@protocol(MTLTexture)])) {
377 return {};
378 }
379 id<MTLTexture> texture = last_texture_.get();
380 int bytesPerPixel = 0;
381 std::string pixel_format;
382 switch (texture.pixelFormat) {
383 case MTLPixelFormatBGR10_XR:
384 bytesPerPixel = 4;
385 pixel_format = "MTLPixelFormatBGR10_XR";
386 break;
387 case MTLPixelFormatBGRA10_XR:
388 bytesPerPixel = 8;
389 pixel_format = "MTLPixelFormatBGRA10_XR";
390 break;
391 case MTLPixelFormatBGRA8Unorm:
392 bytesPerPixel = 4;
393 pixel_format = "MTLPixelFormatBGRA8Unorm";
394 break;
395 case MTLPixelFormatRGBA16Float:
396 bytesPerPixel = 8;
397 pixel_format = "MTLPixelFormatRGBA16Float";
398 break;
399 default:
400 return {};
401 }
402
403 // Zero initialized so that errors are easier to find at the cost of
404 // performance.
406 SkData::MakeZeroInitialized(texture.width * texture.height * bytesPerPixel);
407 [texture getBytes:result->writable_data()
408 bytesPerRow:texture.width * bytesPerPixel
409 fromRegion:MTLRegionMake2D(0, 0, texture.width, texture.height)
410 mipmapLevel:0];
411 return {
412 .pixel_format = pixel_format,
413 .data = result,
414 };
415}
416
417} // namespace flutter
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
#define __has_feature(x)
static sk_sp< SkData > MakeZeroInitialized(size_t length)
Definition SkData.cpp:120
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:37
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...
std::unique_ptr< SurfaceFrame > AcquireFrame(const SkISize &frame_size) override
std::unique_ptr< GLContextResult > MakeRenderContextCurrent() override
std::shared_ptr< impeller::AiksContext > GetAiksContext() const override
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
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)
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....
static std::unique_ptr< SurfaceMTL > MakeFromMetalLayerDrawable(const std::shared_ptr< Context > &context, id< CAMetalDrawable > drawable, std::optional< IRect > clip_rect=std::nullopt)
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_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 ref_ptr.h:256
static constexpr SkIRect MakeEmpty()
Definition SkRect.h:45
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition SkRect.h:56
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:314
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:308
#define ERROR(message)
#define TRACE_EVENT0(category_group, name)