| Flutter Impeller |

surface_mtl.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 "impeller/renderer/backend/metal/surface_mtl.h"
6
7 #include "flutter/fml/trace_event.h"
8 #include "flutter/impeller/renderer/command_buffer.h"
9 #include "impeller/base/validation.h"
10 #include "impeller/core/formats.h"
11 #include "impeller/core/texture_descriptor.h"
12 #include "impeller/renderer/backend/metal/context_mtl.h"
13 #include "impeller/renderer/backend/metal/formats_mtl.h"
14 #include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
15 #include "impeller/renderer/backend/metal/texture_mtl.h"
16 #include "impeller/renderer/render_target.h"
17
18 static_assert(__has_feature(objc_arc), "ARC must be enabled.");
19
20 @protocol FlutterMetalDrawable <MTLDrawable>
21 - (void)flutterPrepareForPresent:(nonnull id<MTLCommandBuffer>)commandBuffer;
22 @end
23
24 namespace impeller {
25
26 #pragma GCC diagnostic push
27 #pragma GCC diagnostic ignored "-Wunguarded-availability-new"
28
29 id<CAMetalDrawable> SurfaceMTL::GetMetalDrawableAndValidate(
30 const std::shared_ptr<Context>& context,
31 CAMetalLayer* layer) {
32 TRACE_EVENT0("impeller", "SurfaceMTL::WrapCurrentMetalLayerDrawable");
33
34 if (context == nullptr || !context->IsValid() || layer == nil) {
35 return nullptr;
36 }
37
38 id<CAMetalDrawable> current_drawable = nil;
39 {
40 TRACE_EVENT0("impeller", "WaitForNextDrawable");
41 current_drawable = [layer nextDrawable];
42 }
43
44 if (!current_drawable) {
45 VALIDATION_LOG << "Could not acquire current drawable.";
46 return nullptr;
47 }
48 return current_drawable;
49 }
50
51 static std::optional<RenderTarget> WrapTextureWithRenderTarget(
52 const std::shared_ptr<SwapchainTransientsMTL>& transients,
53 id<MTLTexture> texture,
54 bool requires_blit,
55 std::optional<IRect> clip_rect) {
56 ISize root_size = {static_cast<ISize::Type>(texture.width),
57 static_cast<ISize::Type>(texture.height)};
58 PixelFormat format = FromMTLPixelFormat(texture.pixelFormat);
59 if (format == PixelFormat::kUnknown) {
60 VALIDATION_LOG << "Unknown drawable color format.";
61 return std::nullopt;
62 }
63
64 transients->SetSizeAndFormat(root_size, format);
65
66 TextureDescriptor resolve_tex_desc;
67 resolve_tex_desc.format = FromMTLPixelFormat(texture.pixelFormat);
68 resolve_tex_desc.size = root_size;
69 resolve_tex_desc.usage =
70 TextureUsage::kRenderTarget | TextureUsage::kShaderRead;
71 resolve_tex_desc.sample_count = SampleCount::kCount1;
72 resolve_tex_desc.storage_mode = StorageMode::kDevicePrivate;
73
74 // Create color resolve texture.
75 std::shared_ptr<Texture> resolve_tex;
76 if (requires_blit) {
77 resolve_tex = transients->GetResolveTexture();
78 } else {
79 resolve_tex = TextureMTL::Create(resolve_tex_desc, texture);
80 }
81
82 ColorAttachment color0;
83 color0.texture = transients->GetMSAATexture();
84 color0.clear_color = Color::DarkSlateGray();
85 color0.load_action = LoadAction::kClear;
86 color0.store_action = StoreAction::kMultisampleResolve;
87 color0.resolve_texture = std::move(resolve_tex);
88
89 DepthAttachment depth0;
90 depth0.load_action =
91 RenderTarget::kDefaultStencilAttachmentConfig.load_action;
92 depth0.store_action =
93 RenderTarget::kDefaultStencilAttachmentConfig.store_action;
94 depth0.clear_depth = 0u;
95 depth0.texture = transients->GetDepthStencilTexture();
96
97 StencilAttachment stencil0;
98 stencil0.load_action =
99 RenderTarget::kDefaultStencilAttachmentConfig.load_action;
100 stencil0.store_action =
101 RenderTarget::kDefaultStencilAttachmentConfig.store_action;
102 stencil0.clear_stencil = 0u;
103 stencil0.texture = transients->GetDepthStencilTexture();
104
105 RenderTarget render_target;
106 render_target.SetColorAttachment(color0, 0u);
107 render_target.SetDepthAttachment(std::move(depth0));
108 render_target.SetStencilAttachment(std::move(stencil0));
109
110 return render_target;
111 }
112
113 std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromMetalLayerDrawable(
114 const std::shared_ptr<Context>& context,
115 id<CAMetalDrawable> drawable,
116 const std::shared_ptr<SwapchainTransientsMTL>& transients,
117 std::optional<IRect> clip_rect) {
118 return SurfaceMTL::MakeFromTexture(context, drawable.texture, transients,
119 clip_rect, drawable);
120 }
121
122 std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromTexture(
123 const std::shared_ptr<Context>& context,
124 id<MTLTexture> texture,
125 const std::shared_ptr<SwapchainTransientsMTL>& transients,
126 std::optional<IRect> clip_rect,
127 id<CAMetalDrawable> drawable) {
128 bool partial_repaint_blit_required = ShouldPerformPartialRepaint(clip_rect);
129
130 // The returned render target is the texture that Impeller will render the
131 // root pass to. If partial repaint is in use, this may be a new texture which
132 // is smaller than the given MTLTexture.
133 auto render_target = WrapTextureWithRenderTarget(
134 transients, texture, partial_repaint_blit_required, clip_rect);
135 if (!render_target) {
136 return nullptr;
137 }
138
139 // If partial repainting, set a "source" texture. The presence of a source
140 // texture and clip rect instructs the surface to blit this texture to the
141 // destination texture.
142 auto source_texture = partial_repaint_blit_required
143 ? render_target->GetRenderTargetTexture()
144 : nullptr;
145
146 // The final "destination" texture is the texture that will be presented. In
147 // this case, it's always the given drawable.
148 std::shared_ptr<Texture> destination_texture;
149 if (partial_repaint_blit_required) {
150 // If blitting for partial repaint, we need to wrap the drawable. Simply
151 // reuse the texture descriptor that was already formed for the new render
152 // target, but override the size with the drawable's size.
153 auto destination_descriptor =
154 render_target->GetRenderTargetTexture()->GetTextureDescriptor();
155 destination_descriptor.size = {static_cast<ISize::Type>(texture.width),
156 static_cast<ISize::Type>(texture.height)};
157 destination_texture = TextureMTL::Wrapper(destination_descriptor, texture);
158 } else {
159 // When not partial repaint blit is needed, the render target texture _is_
160 // the drawable texture.
161 destination_texture = render_target->GetRenderTargetTexture();
162 }
163
164 return std::unique_ptr<SurfaceMTL>(new SurfaceMTL(
165 context, // context
166 *render_target, // target
167 render_target->GetRenderTargetTexture(), // resolve_texture
168 drawable, // drawable
169 source_texture, // source_texture
170 destination_texture, // destination_texture
171 partial_repaint_blit_required, // requires_blit
172 clip_rect // clip_rect
173 ));
174 }
175
176 SurfaceMTL::SurfaceMTL(const std::weak_ptr<Context>& context,
177 const RenderTarget& target,
178 std::shared_ptr<Texture> resolve_texture,
179 id<CAMetalDrawable> drawable,
180 std::shared_ptr<Texture> source_texture,
181 std::shared_ptr<Texture> destination_texture,
182 bool requires_blit,
183 std::optional<IRect> clip_rect)
184 : Surface(target),
185 context_(context),
186 resolve_texture_(std::move(resolve_texture)),
187 drawable_(drawable),
188 source_texture_(std::move(source_texture)),
189 destination_texture_(std::move(destination_texture)),
190 requires_blit_(requires_blit),
191 clip_rect_(clip_rect) {}
192
193 // |Surface|
194 SurfaceMTL::~SurfaceMTL() = default;
195
196 bool SurfaceMTL::ShouldPerformPartialRepaint(std::optional<IRect> damage_rect) {
197 // compositor_context.cc will conditionally disable partial repaint if the
198 // damage region is large. If that happened, then a nullopt damage rect
199 // will be provided here.
200 if (!damage_rect.has_value()) {
201 return false;
202 }
203 // If the damage rect is 0 in at least one dimension, partial repaint isn't
204 // performed as we skip right to present.
205 if (damage_rect->IsEmpty()) {
206 return false;
207 }
208 return true;
209 }
210
211 // |Surface|
212 IRect SurfaceMTL::coverage() const {
213 return IRect::MakeSize(resolve_texture_->GetSize());
214 }
215
216 bool SurfaceMTL::PreparePresent() const {
217 auto context = context_.lock();
218 if (!context) {
219 return false;
220 }
221
222 #ifdef IMPELLER_DEBUG
223 context->GetResourceAllocator()->DebugTraceMemoryStatistics();
224 if (frame_boundary_) {
225 ContextMTL::Cast(context.get())->GetCaptureManager()->FinishCapture();
226 }
227 #endif // IMPELLER_DEBUG
228
229 if (requires_blit_) {
230 if (!(source_texture_ && destination_texture_)) {
231 return false;
232 }
233
234 auto blit_command_buffer = context->CreateCommandBuffer();
235 if (!blit_command_buffer) {
236 return false;
237 }
238 auto blit_pass = blit_command_buffer->CreateBlitPass();
239 if (!clip_rect_.has_value()) {
240 VALIDATION_LOG << "Missing clip rectangle.";
241 return false;
242 }
243 blit_pass->AddCopy(source_texture_, destination_texture_, clip_rect_,
244 clip_rect_->GetOrigin());
245 blit_pass->EncodeCommands();
246 if (!context->GetCommandQueue()->Submit({blit_command_buffer}).ok()) {
247 return false;
248 }
249 }
250 #ifdef IMPELLER_DEBUG
251 ContextMTL::Cast(context.get())->GetGPUTracer()->MarkFrameEnd();
252 #endif // IMPELLER_DEBUG
253 prepared_ = true;
254 return true;
255 }
256
257 // |Surface|
258 bool SurfaceMTL::Present() const {
259 if (!prepared_) {
260 PreparePresent();
261 }
262 auto context = context_.lock();
263 if (!context) {
264 return false;
265 }
266
267 if (drawable_) {
268 id<MTLCommandBuffer> command_buffer =
269 ContextMTL::Cast(context.get())
270 ->CreateMTLCommandBuffer("Present Waiter Command Buffer");
271
272 id<CAMetalDrawable> metal_drawable =
273 reinterpret_cast<id<CAMetalDrawable>>(drawable_);
274 if ([metal_drawable conformsToProtocol:@protocol(FlutterMetalDrawable)]) {
275 [(id<FlutterMetalDrawable>)metal_drawable
276 flutterPrepareForPresent:command_buffer];
277 }
278
279 // Intel iOS simulators do not seem to give backpressure on Metal drawable
280 // aquisition, which can result in Impeller running head of the GPU
281 // workload by dozens of frames. Slow this process down by blocking
282 // on submit until the last command buffer is at least scheduled.
283 #if defined(FML_OS_IOS_SIMULATOR) && defined(FML_ARCH_CPU_X86_64)
284 constexpr bool alwaysWaitForScheduling = true;
285 #else
286 constexpr bool alwaysWaitForScheduling = false;
287 #endif // defined(FML_OS_IOS_SIMULATOR) && defined(FML_ARCH_CPU_X86_64)
288
289 // If the threads have been merged, or there is a pending frame capture,
290 // then block on cmd buffer scheduling to ensure that the
291 // transaction/capture work correctly.
292 if (present_with_transaction_ || [[NSThread currentThread] isMainThread] ||
293 [[MTLCaptureManager sharedCaptureManager] isCapturing] ||
294 alwaysWaitForScheduling) {
295 TRACE_EVENT0("flutter", "waitUntilScheduled");
296 [command_buffer commit];
297 #if defined(FML_OS_IOS_SIMULATOR) && defined(FML_ARCH_CPU_X86_64)
298 [command_buffer waitUntilCompleted];
299 #else
300 [command_buffer waitUntilScheduled];
301 #endif // defined(FML_OS_IOS_SIMULATOR) && defined(FML_ARCH_CPU_X86_64)
302 [drawable_ present];
303 } else {
304 // The drawable may come from a FlutterMetalLayer, so it can't be
305 // presented through the command buffer.
306 id<CAMetalDrawable> drawable = drawable_;
307 [command_buffer addScheduledHandler:^(id<MTLCommandBuffer> buffer) {
308 [drawable present];
309 }];
310 [command_buffer commit];
311 }
312 }
313
314 return true;
315 }
316 #pragma GCC diagnostic pop
317
318 } // namespace impeller
impeller::BackendCast< ContextMTL, Context >::Cast
static ContextMTL & Cast(Context &base)
Definition: backend_cast.h:13
impeller::ContextMTL::CreateCommandBuffer
std::shared_ptr< CommandBuffer > CreateCommandBuffer() const override
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
Definition: context_mtl.mm:335
impeller::ContextMTL::CreateMTLCommandBuffer
id< MTLCommandBuffer > CreateMTLCommandBuffer(const std::string &label) const
Definition: context_mtl.mm:390
impeller::RenderTarget
Definition: render_target.h:38
impeller::RenderTarget::SetColorAttachment
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
Definition: render_target.cc:199
impeller::RenderTarget::kDefaultStencilAttachmentConfig
static constexpr AttachmentConfig kDefaultStencilAttachmentConfig
Definition: render_target.h:68
impeller::RenderTarget::SetDepthAttachment
RenderTarget & SetDepthAttachment(std::optional< DepthAttachment > attachment)
Definition: render_target.cc:213
impeller::RenderTarget::SetStencilAttachment
RenderTarget & SetStencilAttachment(std::optional< StencilAttachment > attachment)
Definition: render_target.cc:223
impeller::Surface
Definition: surface.h:12
impeller::SurfaceMTL
Definition: surface_mtl.h:18
impeller::SurfaceMTL::Present
bool Present() const override
Definition: surface_mtl.mm:258
impeller::SurfaceMTL::coverage
IRect coverage() const
Definition: surface_mtl.mm:212
impeller::SurfaceMTL::MakeFromMetalLayerDrawable
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)
Definition: surface_mtl.mm:113
impeller::SurfaceMTL::GetMetalDrawableAndValidate
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:29
impeller::SurfaceMTL::PreparePresent
bool PreparePresent() const
Perform the final blit and trigger end of frame workloads.
Definition: surface_mtl.mm:216
impeller::SurfaceMTL::~SurfaceMTL
~SurfaceMTL() override
impeller::SurfaceMTL::MakeFromTexture
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)
Definition: surface_mtl.mm:122
impeller::SurfaceMTL::drawable
id< MTLDrawable > drawable() const
Definition: surface_mtl.h:59
impeller::TextureMTL::Wrapper
static std::shared_ptr< TextureMTL > Wrapper(TextureDescriptor desc, id< MTLTexture > texture, std::function< void()> deletion_proc=nullptr)
Definition: texture_mtl.mm:42
impeller::TextureMTL::Create
static std::shared_ptr< TextureMTL > Create(TextureDescriptor desc, id< MTLTexture > texture)
Definition: texture_mtl.mm:59
command_buffer.h
formats.h
formats_mtl.h
impeller
Definition: allocation.cc:12
impeller::FromMTLPixelFormat
constexpr PixelFormat FromMTLPixelFormat(MTLPixelFormat format)
Definition: formats_mtl.h:22
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::PixelFormat::kUnknown
@ kUnknown
impeller::LoadAction::kClear
@ kClear
impeller::StoreAction::kMultisampleResolve
@ kMultisampleResolve
impeller::WrapTextureWithRenderTarget
static std::optional< RenderTarget > WrapTextureWithRenderTarget(const std::shared_ptr< SwapchainTransientsMTL > &transients, id< MTLTexture > texture, bool requires_blit, std::optional< IRect > clip_rect)
Definition: surface_mtl.mm:51
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::TextureUsage::kShaderRead
@ kShaderRead
impeller::SampleCount::kCount1
@ kCount1
std
Definition: comparable.h:95
FlutterMetalDrawable-p
Definition: surface_mtl.mm:20
render_target.h
context_mtl.h
surface_mtl.h
impeller::Attachment::resolve_texture
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:658
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:659
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:657
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:660
impeller::ColorAttachment
Definition: formats.h:665
impeller::ColorAttachment::clear_color
Color clear_color
Definition: formats.h:666
impeller::Color::DarkSlateGray
static constexpr Color DarkSlateGray()
Definition: color.h:418
impeller::DepthAttachment
Definition: formats.h:669
impeller::DepthAttachment::clear_depth
double clear_depth
Definition: formats.h:670
impeller::RenderTarget::AttachmentConfig::load_action
LoadAction load_action
Definition: render_target.h:42
impeller::RenderTarget::AttachmentConfig::store_action
StoreAction store_action
Definition: render_target.h:43
impeller::StencilAttachment
Definition: formats.h:673
impeller::StencilAttachment::clear_stencil
uint32_t clear_stencil
Definition: formats.h:674
impeller::TRect
Definition: rect.h:122
impeller::TRect::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
impeller::TSize
Definition: size.h:25
impeller::TSize::Type
T Type
Definition: size.h:26
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:44
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
impeller::TextureDescriptor::sample_count
SampleCount sample_count
Definition: texture_descriptor.h:45
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
swapchain_transients_mtl.h
texture_descriptor.h
texture_mtl.h
validation.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91