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
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 return std::make_unique<SurfaceFrame>(
69 nullptr, SurfaceFrame::FramebufferInfo(),
70 [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; },
71 [](const SurfaceFrame& surface_frame) { return true; }, frame_size);
72 }
73
74 switch (render_target_type_) {
76 return AcquireFrameFromCAMetalLayer(frame_size);
78 return AcquireFrameFromMTLTexture(frame_size);
79 default:
80 FML_CHECK(false) << "Unknown MTLRenderTargetType type.";
81 }
82
83 return nullptr;
84}
85
86std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLayer(
87 const DlISize& frame_size) {
88 CAMetalLayer* layer = (__bridge CAMetalLayer*)delegate_->GetCAMetalLayer(frame_size);
89 if (!layer) {
90 FML_LOG(ERROR) << "Invalid CAMetalLayer given by the embedder.";
91 return nullptr;
92 }
93
94 id<CAMetalDrawable> drawable =
95 impeller::SurfaceMTL::GetMetalDrawableAndValidate(aiks_context_->GetContext(), layer);
96 if (!drawable) {
97 return nullptr;
98 }
100 last_texture_ = drawable.texture;
101 }
102
103#ifdef IMPELLER_DEBUG
104 impeller::ContextMTL::Cast(*aiks_context_->GetContext()).GetCaptureManager()->StartCapture();
105#endif // IMPELLER_DEBUG
106
107 __weak id<MTLTexture> weak_last_texture = last_texture_;
108 __weak CAMetalLayer* weak_layer = layer;
109 SurfaceFrame::EncodeCallback encode_callback =
110 fml::MakeCopyable([damage = damage_,
111 disable_partial_repaint = disable_partial_repaint_, //
112 aiks_context = aiks_context_, //
113 drawable, //
114 weak_last_texture, //
115 weak_layer, //
116 swapchain_transients = swapchain_transients_ //
117 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
118 id<MTLTexture> strong_last_texture = weak_last_texture;
119 CAMetalLayer* strong_layer = weak_layer;
120 if (!strong_last_texture || !strong_layer) {
121 return false;
122 }
123 strong_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction;
124 if (!aiks_context) {
125 return false;
126 }
127
128 auto display_list = surface_frame.BuildDisplayList();
129 if (!display_list) {
130 FML_LOG(ERROR) << "Could not build display list for surface frame.";
131 return false;
132 }
133
134 if (!disable_partial_repaint && damage) {
135 void* texture = (__bridge void*)strong_last_texture;
136 for (auto& entry : *damage) {
137 if (entry.first != texture) {
138 // Accumulate damage for other framebuffers
139 if (surface_frame.submit_info().frame_damage) {
140 entry.second = entry.second.Union(*surface_frame.submit_info().frame_damage);
141 }
142 }
143 }
144 // Reset accumulated damage for current framebuffer
145 (*damage)[texture] = DlIRect();
146 }
147
148 std::optional<impeller::IRect> clip_rect;
149 if (surface_frame.submit_info().buffer_damage.has_value()) {
150 auto buffer_damage = surface_frame.submit_info().buffer_damage;
151 clip_rect =
152 impeller::IRect::MakeLTRB(buffer_damage->GetLeft(), buffer_damage->GetTop(),
153 buffer_damage->GetRight(), buffer_damage->GetBottom());
154 }
155
157 aiks_context->GetContext(), drawable, swapchain_transients, clip_rect);
158
159 // The surface may be null if we failed to allocate the onscreen render target
160 // due to running out of memory.
161 if (!surface) {
162 return false;
163 }
164 surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction);
165
166 if (clip_rect && clip_rect->IsEmpty()) {
167 if (!surface->PreparePresent()) {
168 return false;
169 }
170 surface_frame.set_user_data(std::move(surface));
171 return true;
172 }
173
174 impeller::Rect cull_rect = impeller::Rect::Make(surface->coverage());
175 surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary);
176
177 const bool reset_host_buffer = surface_frame.submit_info().frame_boundary;
178 auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
179 surface->GetRenderTarget(), //
180 display_list, //
181 cull_rect, //
182 /*reset_host_buffer=*/reset_host_buffer //
183 );
184 if (!render_result) {
185 return false;
186 }
187
188 if (!surface->PreparePresent()) {
189 return false;
190 }
191 surface_frame.set_user_data(std::move(surface));
192 return true;
193 });
194
195 SurfaceFrame::FramebufferInfo framebuffer_info;
196 framebuffer_info.supports_readback = true;
197
198 if (!disable_partial_repaint_) {
199 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
200 // front buffer)
201 void* texture = (__bridge void*)drawable.texture;
202 auto i = damage_->find(texture);
203 if (i != damage_->end()) {
204 framebuffer_info.existing_damage = i->second;
205 }
206 framebuffer_info.supports_partial_repaint = true;
207 }
208
209 return std::make_unique<SurfaceFrame>(
210 nullptr, // surface
211 framebuffer_info, // framebuffer info
212 encode_callback, // submit callback
213 [](SurfaceFrame& surface_frame) { return surface_frame.take_user_data()->Present(); },
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 DlISize& frame_size) {
222 GPUMTLTextureInfo texture_info = delegate_->GetMTLTexture(frame_size);
223 id<MTLTexture> mtl_texture = (__bridge id<MTLTexture>)texture_info.texture;
224 if (!mtl_texture) {
225 FML_LOG(ERROR) << "Invalid MTLTexture given by the embedder.";
226 return nullptr;
227 }
228
230 last_texture_ = mtl_texture;
231 }
232
233#ifdef IMPELLER_DEBUG
234 impeller::ContextMTL::Cast(*aiks_context_->GetContext()).GetCaptureManager()->StartCapture();
235#endif // IMPELLER_DEBUG
236
237 __weak id<MTLTexture> weak_texture = mtl_texture;
238 SurfaceFrame::EncodeCallback encode_callback =
239 fml::MakeCopyable([disable_partial_repaint = disable_partial_repaint_, //
240 damage = damage_,
241 aiks_context = aiks_context_, //
242 weak_texture, //
243 swapchain_transients = swapchain_transients_ //
244 ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool {
245 id<MTLTexture> strong_texture = weak_texture;
246 if (!strong_texture) {
247 return false;
248 }
249 if (!aiks_context) {
250 return false;
251 }
252 auto display_list = surface_frame.BuildDisplayList();
253 if (!display_list) {
254 FML_LOG(ERROR) << "Could not build display list for surface frame.";
255 return false;
256 }
257
258 if (!disable_partial_repaint && damage) {
259 void* texture_ptr = (__bridge void*)strong_texture;
260 for (auto& entry : *damage) {
261 if (entry.first != texture_ptr) {
262 // Accumulate damage for other framebuffers
263 if (surface_frame.submit_info().frame_damage) {
264 entry.second = entry.second.Union(*surface_frame.submit_info().frame_damage);
265 }
266 }
267 }
268 // Reset accumulated damage for current framebuffer
269 (*damage)[texture_ptr] = DlIRect();
270 }
271
272 std::optional<impeller::IRect> clip_rect;
273 if (surface_frame.submit_info().buffer_damage.has_value()) {
274 auto buffer_damage = surface_frame.submit_info().buffer_damage;
275 clip_rect =
276 impeller::IRect::MakeLTRB(buffer_damage->GetLeft(), buffer_damage->GetTop(),
277 buffer_damage->GetRight(), buffer_damage->GetBottom());
278 }
279
281 aiks_context->GetContext(), strong_texture, swapchain_transients, clip_rect);
282
283 surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction);
284
285 if (clip_rect && clip_rect->IsEmpty()) {
286 if (!surface->PreparePresent()) {
287 return false;
288 }
289 return surface->Present();
290 }
291
292 impeller::Rect cull_rect = impeller::Rect::Make(surface->coverage());
293 auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
294 surface->GetRenderTarget(), //
295 display_list, //
296 cull_rect, //
297 /*reset_host_buffer=*/true //
298 );
299 if (!render_result) {
300 FML_LOG(ERROR) << "Failed to render Impeller frame";
301 return false;
302 }
303 if (!surface->PreparePresent()) {
304 return false;
305 }
306 return surface->PreparePresent();
307 });
308
309 SurfaceFrame::SubmitCallback submit_callback =
310 [texture_info, delegate = delegate_](const SurfaceFrame& surface_frame) {
311 return delegate->PresentTexture(texture_info);
312 };
313
314 SurfaceFrame::FramebufferInfo framebuffer_info;
315 framebuffer_info.supports_readback = true;
316
317 if (!disable_partial_repaint_) {
318 // Provide accumulated damage to rasterizer (area in current framebuffer that lags behind
319 // front buffer)
320 void* texture = (__bridge void*)mtl_texture;
321 auto i = damage_->find(texture);
322 if (i != damage_->end()) {
323 framebuffer_info.existing_damage = i->second;
324 }
325 framebuffer_info.supports_partial_repaint = true;
326 }
327
328 return std::make_unique<SurfaceFrame>(nullptr, // surface
329 framebuffer_info, // framebuffer info
330 encode_callback,
331 submit_callback, // submit callback
332 frame_size, // frame size
333 nullptr, // context result
334 true // display list fallback
335 );
336}
337
338// |Surface|
339DlMatrix GPUSurfaceMetalImpeller::GetRootTransformation() const {
340 // This backend does not currently support root surface transformations. Just
341 // return identity.
342 return {};
343}
344
345// |Surface|
346GrDirectContext* GPUSurfaceMetalImpeller::GetContext() {
347 return nullptr;
348}
349
350// |Surface|
351std::unique_ptr<GLContextResult> GPUSurfaceMetalImpeller::MakeRenderContextCurrent() {
352 // This backend has no such concept.
353 return std::make_unique<GLContextDefaultResult>(true);
354}
355
356bool GPUSurfaceMetalImpeller::AllowsDrawingWhenGpuDisabled() const {
357 return delegate_->AllowsDrawingWhenGpuDisabled();
358}
359
360// |Surface|
361bool GPUSurfaceMetalImpeller::EnableRasterCache() const {
362 return false;
363}
364
365// |Surface|
366std::shared_ptr<impeller::AiksContext> GPUSurfaceMetalImpeller::GetAiksContext() const {
367 return aiks_context_;
368}
369
371 if (!(last_texture_ && [last_texture_ conformsToProtocol:@protocol(MTLTexture)])) {
372 return {};
373 }
374 id<MTLTexture> texture = last_texture_;
375 int bytesPerPixel = 0;
376 std::string pixel_format;
377 switch (texture.pixelFormat) {
378 case MTLPixelFormatBGR10_XR:
379 bytesPerPixel = 4;
380 pixel_format = "MTLPixelFormatBGR10_XR";
381 break;
382 case MTLPixelFormatBGRA10_XR:
383 bytesPerPixel = 8;
384 pixel_format = "MTLPixelFormatBGRA10_XR";
385 break;
386 case MTLPixelFormatBGRA8Unorm:
387 bytesPerPixel = 4;
388 pixel_format = "MTLPixelFormatBGRA8Unorm";
389 break;
390 case MTLPixelFormatRGBA16Float:
391 bytesPerPixel = 8;
392 pixel_format = "MTLPixelFormatRGBA16Float";
393 break;
394 default:
395 return {};
396 }
397
398 // Zero initialized so that errors are easier to find at the cost of
399 // performance.
400 sk_sp<SkData> result =
401 SkData::MakeZeroInitialized(texture.width * texture.height * bytesPerPixel);
402 [texture getBytes:result->writable_data()
403 bytesPerRow:texture.width * bytesPerPixel
404 fromRegion:MTLRegionMake2D(0, 0, texture.width, texture.height)
405 mipmapLevel:0];
406 return {
407 .pixel_format = pixel_format,
408 .data = result,
409 };
410}
411
412} // 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:157
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)