Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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
6
7#import <Metal/Metal.h>
8#import <QuartzCore/QuartzCore.h>
9#include "flow/surface.h"
10#include "flow/surface_frame.h"
13
16#include "flutter/fml/mapping.h"
21
22static_assert(__has_feature(objc_arc), "ARC must be enabled.");
23
24namespace flutter {
25
28 const std::shared_ptr<impeller::AiksContext>& context,
29 bool render_to_surface)
30 : delegate_(delegate),
31 render_target_type_(delegate->GetRenderTargetType()),
32 aiks_context_(context),
33 render_to_surface_(render_to_surface) {
34 // If this preference is explicitly set, we allow for disabling partial repaint.
35 NSNumber* disablePartialRepaint =
36 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"];
37 if (disablePartialRepaint != nil) {
38 disable_partial_repaint_ = disablePartialRepaint.boolValue;
39 }
40 if (aiks_context_) {
41 swapchain_transients_ = std::make_shared<impeller::SwapchainTransientsMTL>(
42 aiks_context_->GetContext()->GetResourceAllocator());
43 }
44}
45
47
48// |Surface|
50 return !!aiks_context_ && aiks_context_->IsValid();
51}
52
53// |Surface|
54std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrame(const DlISize& frame_size) {
55 TRACE_EVENT0("impeller", "GPUSurfaceMetalImpeller::AcquireFrame");
56
57 if (!IsValid()) {
58 FML_LOG(ERROR) << "Metal surface was invalid.";
59 return nullptr;
60 }
61
62 if (frame_size.IsEmpty()) {
63 FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
64 return nullptr;
65 }
66
67 if (!render_to_surface_) {
68#ifdef IMPELLER_DEBUG
69 impeller::ContextMTL::Cast(*aiks_context_->GetContext()).GetCaptureManager()->StartCapture();
70#endif // IMPELLER_DEBUG
71 return std::make_unique<SurfaceFrame>(
72 nullptr, SurfaceFrame::FramebufferInfo(),
73 [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; },
74#ifdef IMPELLER_DEBUG
75 [context = aiks_context_->GetContext()](const SurfaceFrame& surface_frame) {
76 impeller::ContextMTL::Cast(*context).GetCaptureManager()->FinishCapture();
77 return true;
78 },
79#else
80 [](const SurfaceFrame& surface_frame) { return true; },
81#endif // IMPELLER_DEBUG
82 frame_size);
83 }
84
85 switch (render_target_type_) {
87 return AcquireFrameFromCAMetalLayer(frame_size);
89 return AcquireFrameFromMTLTexture(frame_size);
90 default:
91 FML_CHECK(false) << "Unknown MTLRenderTargetType type.";
92 }
93
94 return nullptr;
95}
96
97std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLayer(
98 const DlISize& frame_size) {
99 CAMetalLayer* layer = (__bridge CAMetalLayer*)delegate_->GetCAMetalLayer(frame_size);
100 if (!layer) {
101 FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder.";
102 return nullptr;
103 }
104
105 id<CAMetalDrawable> drawable =
106 impeller::SurfaceMTL::GetMetalDrawableAndValidate(aiks_context_->GetContext(), layer);
107 if (!drawable) {
108 return nullptr;
109 }
111 last_texture_ = drawable.texture;
112 }
113
114#ifdef IMPELLER_DEBUG
115 impeller::ContextMTL::Cast(*aiks_context_->GetContext()).GetCaptureManager()->StartCapture();
116#endif // IMPELLER_DEBUG
117
118 __weak id<MTLTexture> weak_last_texture = last_texture_;
119 __weak CAMetalLayer* weak_layer = layer;
120 SurfaceFrame::EncodeCallback encode_callback =
121 fml::MakeCopyable([damage = damage_,
122 disable_partial_repaint = disable_partial_repaint_, //
123 aiks_context = aiks_context_, //
124 drawable, //
125 weak_last_texture, //
126 weak_layer, //
127 swapchain_transients = swapchain_transients_ //
128 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
129 id<MTLTexture> strong_last_texture = weak_last_texture;
130 CAMetalLayer* strong_layer = weak_layer;
131 if (!strong_last_texture || !strong_layer) {
132 return false;
133 }
134 strong_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction;
135 if (!aiks_context) {
136 return false;
137 }
138
139 auto display_list = surface_frame.BuildDisplayList();
140 if (!display_list) {
141 FML_LOG(ERROR) << "Could not build display list for surface frame.";
142 return false;
143 }
144
145 if (!disable_partial_repaint && damage) {
146 void* texture = (__bridge void*)strong_last_texture;
147 for (auto& entry : *damage) {
148 if (entry.first != texture) {
149 // Accumulate damage for other framebuffers
150 if (surface_frame.submit_info().frame_damage) {
151 entry.second = entry.second.Union(*surface_frame.submit_info().frame_damage);
152 }
153 }
154 }
155 // Reset accumulated damage for current framebuffer
156 (*damage)[texture] = DlIRect();
157 }
158
159 std::optional<impeller::IRect> clip_rect;
160 if (surface_frame.submit_info().buffer_damage.has_value()) {
161 auto buffer_damage = surface_frame.submit_info().buffer_damage;
162 clip_rect =
163 impeller::IRect::MakeLTRB(buffer_damage->GetLeft(), buffer_damage->GetTop(),
164 buffer_damage->GetRight(), buffer_damage->GetBottom());
165 }
166
168 aiks_context->GetContext(), drawable, swapchain_transients, clip_rect);
169
170 // The surface may be null if we failed to allocate the onscreen render target
171 // due to running out of memory.
172 if (!surface) {
173 return false;
174 }
175 surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction);
176
177 if (clip_rect && clip_rect->IsEmpty()) {
178 if (!surface->PreparePresent()) {
179 return false;
180 }
181 surface_frame.set_user_data(std::move(surface));
182 return true;
183 }
184
185 impeller::Rect cull_rect = impeller::Rect::Make(surface->coverage());
186 surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary);
187
188 const bool reset_host_buffer = surface_frame.submit_info().frame_boundary;
189 auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
190 surface->GetRenderTarget(), //
191 display_list, //
192 cull_rect, //
193 /*reset_host_buffer=*/reset_host_buffer //
194 );
195 if (!render_result) {
196 return false;
197 }
198
199 if (!surface->PreparePresent()) {
200 return false;
201 }
202 surface_frame.set_user_data(std::move(surface));
203 return true;
204 });
205
206 SurfaceFrame::FramebufferInfo framebuffer_info;
207 framebuffer_info.supports_readback = true;
208
209 if (!disable_partial_repaint_) {
210 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
211 // front buffer)
212 void* texture = (__bridge void*)drawable.texture;
213 auto i = damage_->find(texture);
214 if (i != damage_->end()) {
215 framebuffer_info.existing_damage = i->second;
216 }
217 framebuffer_info.supports_partial_repaint = true;
218 }
219
220 return std::make_unique<SurfaceFrame>(
221 nullptr, // surface
222 framebuffer_info, // framebuffer info
223 encode_callback, // submit callback
224 [](SurfaceFrame& surface_frame) { return surface_frame.take_user_data()->Present(); },
225 frame_size, // frame size
226 nullptr, // context result
227 true // display list fallback
228 );
229}
230
231std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTexture(
232 const DlISize& frame_size) {
233 GPUMTLTextureInfo texture_info = delegate_->GetMTLTexture(frame_size);
234 id<MTLTexture> mtl_texture = (__bridge id<MTLTexture>)texture_info.texture;
235 if (!mtl_texture) {
236 FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
237 return nullptr;
238 }
239
241 last_texture_ = mtl_texture;
242 }
243
244#ifdef IMPELLER_DEBUG
245 impeller::ContextMTL::Cast(*aiks_context_->GetContext()).GetCaptureManager()->StartCapture();
246#endif // IMPELLER_DEBUG
247
248 __weak id<MTLTexture> weak_texture = mtl_texture;
249 SurfaceFrame::EncodeCallback encode_callback =
250 fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
251 damage = damage_,
252 aiks_context = aiks_context_, //
253 weak_texture, //
254 swapchain_transients = swapchain_transients_ //
255 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
256 id<MTLTexture> strong_texture = weak_texture;
257 if (!strong_texture) {
258 return false;
259 }
260 if (!aiks_context) {
261 return false;
262 }
263 auto display_list = surface_frame.BuildDisplayList();
264 if (!display_list) {
265 FML_LOG(ERROR) << "Could not build display list for surface frame.";
266 return false;
267 }
268
269 if (!disable_partial_repaint && damage) {
270 void* texture_ptr = (__bridge void*)strong_texture;
271 for (auto& entry : *damage) {
272 if (entry.first != texture_ptr) {
273 // Accumulate damage for other framebuffers
274 if (surface_frame.submit_info().frame_damage) {
275 entry.second = entry.second.Union(*surface_frame.submit_info().frame_damage);
276 }
277 }
278 }
279 // Reset accumulated damage for current framebuffer
280 (*damage)[texture_ptr] = DlIRect();
281 }
282
283 std::optional<impeller::IRect> clip_rect;
284 if (surface_frame.submit_info().buffer_damage.has_value()) {
285 auto buffer_damage = surface_frame.submit_info().buffer_damage;
286 clip_rect =
287 impeller::IRect::MakeLTRB(buffer_damage->GetLeft(), buffer_damage->GetTop(),
288 buffer_damage->GetRight(), buffer_damage->GetBottom());
289 }
290
292 aiks_context->GetContext(), strong_texture, swapchain_transients, clip_rect);
293
294 surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction);
295
296 if (clip_rect && clip_rect->IsEmpty()) {
297 if (!surface->PreparePresent()) {
298 return false;
299 }
300 return surface->Present();
301 }
302
303 impeller::Rect cull_rect = impeller::Rect::Make(surface->coverage());
304 auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
305 surface->GetRenderTarget(), //
306 display_list, //
307 cull_rect, //
308 /*reset_host_buffer=*/true //
309 );
310 if (!render_result) {
311 FML_LOG(ERROR) << "Failed to render Impeller frame";
312 return false;
313 }
314 if (!surface->PreparePresent()) {
315 return false;
316 }
317 return surface->PreparePresent();
318 });
319
320 SurfaceFrame::SubmitCallback submit_callback =
321 [texture_info, delegate = delegate_](const SurfaceFrame& surface_frame) {
322 return delegate->PresentTexture(texture_info);
323 };
324
325 SurfaceFrame::FramebufferInfo framebuffer_info;
326 framebuffer_info.supports_readback = true;
327
328 if (!disable_partial_repaint_) {
329 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
330 // front buffer)
331 void* texture = (__bridge void*)mtl_texture;
332 auto i = damage_->find(texture);
333 if (i != damage_->end()) {
334 framebuffer_info.existing_damage = i->second;
335 }
336 framebuffer_info.supports_partial_repaint = true;
337 }
338
339 return std::make_unique<SurfaceFrame>(nullptr, // surface
340 framebuffer_info, // framebuffer info
341 encode_callback,
342 submit_callback, // submit callback
343 frame_size, // frame size
344 nullptr, // context result
345 true // display list fallback
346 );
347}
348
349// |Surface|
350DlMatrix GPUSurfaceMetalImpeller::GetRootTransformation() const {
351 // This backend does not currently support root surface transformations. Just
352 // return identity.
353 return {};
354}
355
356// |Surface|
357GrDirectContext* GPUSurfaceMetalImpeller::GetContext() {
358 return nullptr;
359}
360
361// |Surface|
362std::unique_ptr<GLContextResult> GPUSurfaceMetalImpeller::MakeRenderContextCurrent() {
363 // This backend has no such concept.
364 return std::make_unique<GLContextDefaultResult>(true);
365}
366
367bool GPUSurfaceMetalImpeller::AllowsDrawingWhenGpuDisabled() const {
368 return delegate_->AllowsDrawingWhenGpuDisabled();
369}
370
371// |Surface|
372bool GPUSurfaceMetalImpeller::EnableRasterCache() const {
373 return false;
374}
375
376// |Surface|
377std::shared_ptr<impeller::AiksContext> GPUSurfaceMetalImpeller::GetAiksContext() const {
378 return aiks_context_;
379}
380
382 if (!(last_texture_ && [last_texture_ conformsToProtocol:@protocol(MTLTexture)])) {
383 return {};
384 }
385 id<MTLTexture> texture = last_texture_;
386 int bytesPerPixel = 0;
387 std::string pixel_format;
388 switch (texture.pixelFormat) {
389 case MTLPixelFormatBGR10_XR:
390 bytesPerPixel = 4;
391 pixel_format = "MTLPixelFormatBGR10_XR";
392 break;
393 case MTLPixelFormatBGRA10_XR:
394 bytesPerPixel = 8;
395 pixel_format = "MTLPixelFormatBGRA10_XR";
396 break;
397 case MTLPixelFormatBGRA8Unorm:
398 bytesPerPixel = 4;
399 pixel_format = "MTLPixelFormatBGRA8Unorm";
400 break;
401 case MTLPixelFormatRGBA16Float:
402 bytesPerPixel = 8;
403 pixel_format = "MTLPixelFormatRGBA16Float";
404 break;
405 default:
406 return {};
407 }
408
409 // Zero initialized so that errors are easier to find at the cost of
410 // performance.
411 sk_sp<SkData> result =
412 SkData::MakeZeroInitialized(texture.width * texture.height * bytesPerPixel);
413 [texture getBytes:result->writable_data()
414 bytesPerRow:texture.width * bytesPerPixel
415 fromRegion:MTLRegionMake2D(0, 0, texture.width, texture.height)
416 mipmapLevel:0];
417 return {
418 .pixel_format = pixel_format,
419 .data = result,
420 };
421}
422
423} // namespace flutter
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 DlISize &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 DlISize &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::AiksContext > &context, bool render_to_surface=true)
virtual Surface::SurfaceData GetSurfaceData() const override
std::function< bool(SurfaceFrame &surface_frame, DlCanvas *canvas)> EncodeCallback
std::function< bool(SurfaceFrame &surface_frame)> SubmitCallback
static ContextMTL & Cast(Context &base)
static std::unique_ptr< SurfaceMTL > MakeFromMetalLayerDrawable(const std::shared_ptr< Context > &context, id< CAMetalDrawable > drawable, const std::shared_ptr< SwapchainTransientsMTL > &transients, std::optional< IRect > clip_rect=std::nullopt)
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 > MakeFromTexture(const std::shared_ptr< Context > &context, id< MTLTexture > texture, const std::shared_ptr< SwapchainTransientsMTL > &transients, std::optional< IRect > clip_rect, id< CAMetalDrawable > drawable=nil)
MockDelegate delegate_
VkSurfaceKHR surface
Definition main.cc:65
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
FlTexture * texture
impeller::Matrix DlMatrix
impeller::ISize32 DlISize
impeller::IRect32 DlIRect
internal::CopyableLambda< T > MakeCopyable(T lambda)
bool RenderToTarget(ContentContext &context, RenderTarget render_target, const sk_sp< flutter::DisplayList > &display_list, Rect cull_rect, bool reset_host_buffer, bool is_onscreen)
Render the provided display list to the render target.
static constexpr bool kSurfaceDataAccessible
Definition settings.h:107
A screenshot of the surface's raw data.
Definition surface.h:29
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:181
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition size.h:123
#define TRACE_EVENT0(category_group, name)