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 
6 
7 #include "flutter/fml/trace_event.h"
15 
16 @protocol FlutterMetalDrawable <MTLDrawable>
17 - (void)flutterPrepareForPresent:(nonnull id<MTLCommandBuffer>)commandBuffer;
18 @end
19 
20 namespace impeller {
21 
22 #pragma GCC diagnostic push
23 #pragma GCC diagnostic ignored "-Wunguarded-availability-new"
24 
26  const std::shared_ptr<Context>& context,
27  CAMetalLayer* layer) {
28  TRACE_EVENT0("impeller", "SurfaceMTL::WrapCurrentMetalLayerDrawable");
29 
30  if (context == nullptr || !context->IsValid() || layer == nil) {
31  return nullptr;
32  }
33 
34  id<CAMetalDrawable> current_drawable = nil;
35  {
36  TRACE_EVENT0("impeller", "WaitForNextDrawable");
37  current_drawable = [layer nextDrawable];
38  }
39 
40  if (!current_drawable) {
41  VALIDATION_LOG << "Could not acquire current drawable.";
42  return nullptr;
43  }
44  return current_drawable;
45 }
46 
47 static std::optional<RenderTarget> WrapTextureWithRenderTarget(
48  Allocator& allocator,
49  id<MTLTexture> texture,
50  bool requires_blit,
51  std::optional<IRect> clip_rect) {
52  // compositor_context.cc will offset the rendering by the clip origin. Here we
53  // shrink to the size of the clip. This has the same effect as clipping the
54  // rendering but also creates smaller intermediate passes.
55  ISize root_size;
56  if (requires_blit) {
57  if (!clip_rect.has_value()) {
58  VALIDATION_LOG << "Missing clip rectangle.";
59  return std::nullopt;
60  }
61  root_size = ISize(clip_rect->GetWidth(), clip_rect->GetHeight());
62  } else {
63  root_size = {static_cast<ISize::Type>(texture.width),
64  static_cast<ISize::Type>(texture.height)};
65  }
66 
67  TextureDescriptor resolve_tex_desc;
68  resolve_tex_desc.format = FromMTLPixelFormat(texture.pixelFormat);
69  resolve_tex_desc.size = root_size;
70  resolve_tex_desc.usage =
72  resolve_tex_desc.sample_count = SampleCount::kCount1;
73  resolve_tex_desc.storage_mode = StorageMode::kDevicePrivate;
74 
75  if (resolve_tex_desc.format == PixelFormat::kUnknown) {
76  VALIDATION_LOG << "Unknown drawable color format.";
77  return std::nullopt;
78  }
79 
80  // Create color resolve texture.
81  std::shared_ptr<Texture> resolve_tex;
82  if (requires_blit) {
83  resolve_tex_desc.compression_type = CompressionType::kLossy;
84  resolve_tex = allocator.CreateTexture(resolve_tex_desc);
85  } else {
86  resolve_tex = TextureMTL::Create(resolve_tex_desc, texture);
87  }
88 
89  if (!resolve_tex) {
90  VALIDATION_LOG << "Could not wrap resolve texture.";
91  return std::nullopt;
92  }
93  resolve_tex->SetLabel("ImpellerOnscreenResolve");
94 
95  TextureDescriptor msaa_tex_desc;
98  msaa_tex_desc.sample_count = SampleCount::kCount4;
99  msaa_tex_desc.format = resolve_tex->GetTextureDescriptor().format;
100  msaa_tex_desc.size = resolve_tex->GetSize();
101  msaa_tex_desc.usage = TextureUsage::kRenderTarget;
102 
103  auto msaa_tex = allocator.CreateTexture(msaa_tex_desc);
104  if (!msaa_tex) {
105  VALIDATION_LOG << "Could not allocate MSAA color texture.";
106  return std::nullopt;
107  }
108  msaa_tex->SetLabel("ImpellerOnscreenColorMSAA");
109 
110  ColorAttachment color0;
111  color0.texture = msaa_tex;
115  color0.resolve_texture = std::move(resolve_tex);
116 
117  auto render_target_desc = std::make_optional<RenderTarget>();
118  render_target_desc->SetColorAttachment(color0, 0u);
119 
120  return render_target_desc;
121 }
122 
123 std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromMetalLayerDrawable(
124  const std::shared_ptr<Context>& context,
125  id<CAMetalDrawable> drawable,
126  std::optional<IRect> clip_rect) {
127  return SurfaceMTL::MakeFromTexture(context, drawable.texture, clip_rect,
128  drawable);
129 }
130 
131 std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromTexture(
132  const std::shared_ptr<Context>& context,
133  id<MTLTexture> texture,
134  std::optional<IRect> clip_rect,
135  id<CAMetalDrawable> drawable) {
136  bool partial_repaint_blit_required = ShouldPerformPartialRepaint(clip_rect);
137 
138  // The returned render target is the texture that Impeller will render the
139  // root pass to. If partial repaint is in use, this may be a new texture which
140  // is smaller than the given MTLTexture.
141  auto render_target =
142  WrapTextureWithRenderTarget(*context->GetResourceAllocator(), texture,
143  partial_repaint_blit_required, clip_rect);
144  if (!render_target) {
145  return nullptr;
146  }
147 
148  // If partial repainting, set a "source" texture. The presence of a source
149  // texture and clip rect instructs the surface to blit this texture to the
150  // destination texture.
151  auto source_texture = partial_repaint_blit_required
152  ? render_target->GetRenderTargetTexture()
153  : nullptr;
154 
155  // The final "destination" texture is the texture that will be presented. In
156  // this case, it's always the given drawable.
157  std::shared_ptr<Texture> destination_texture;
158  if (partial_repaint_blit_required) {
159  // If blitting for partial repaint, we need to wrap the drawable. Simply
160  // reuse the texture descriptor that was already formed for the new render
161  // target, but override the size with the drawable's size.
162  auto destination_descriptor =
163  render_target->GetRenderTargetTexture()->GetTextureDescriptor();
164  destination_descriptor.size = {static_cast<ISize::Type>(texture.width),
165  static_cast<ISize::Type>(texture.height)};
166  destination_texture = TextureMTL::Wrapper(destination_descriptor, texture);
167  } else {
168  // When not partial repaint blit is needed, the render target texture _is_
169  // the drawable texture.
170  destination_texture = render_target->GetRenderTargetTexture();
171  }
172 
173  return std::unique_ptr<SurfaceMTL>(new SurfaceMTL(
174  context, // context
175  *render_target, // target
176  render_target->GetRenderTargetTexture(), // resolve_texture
177  drawable, // drawable
178  source_texture, // source_texture
179  destination_texture, // destination_texture
180  partial_repaint_blit_required, // requires_blit
181  clip_rect // clip_rect
182  ));
183 }
184 
185 SurfaceMTL::SurfaceMTL(const std::weak_ptr<Context>& context,
186  const RenderTarget& target,
187  std::shared_ptr<Texture> resolve_texture,
188  id<CAMetalDrawable> drawable,
189  std::shared_ptr<Texture> source_texture,
190  std::shared_ptr<Texture> destination_texture,
191  bool requires_blit,
192  std::optional<IRect> clip_rect)
193  : Surface(target),
194  context_(context),
195  resolve_texture_(std::move(resolve_texture)),
196  drawable_(drawable),
197  source_texture_(std::move(source_texture)),
198  destination_texture_(std::move(destination_texture)),
199  requires_blit_(requires_blit),
200  clip_rect_(clip_rect) {}
201 
202 // |Surface|
203 SurfaceMTL::~SurfaceMTL() = default;
204 
205 bool SurfaceMTL::ShouldPerformPartialRepaint(std::optional<IRect> damage_rect) {
206  // compositor_context.cc will conditionally disable partial repaint if the
207  // damage region is large. If that happened, then a nullopt damage rect
208  // will be provided here.
209  if (!damage_rect.has_value()) {
210  return false;
211  }
212  // If the damage rect is 0 in at least one dimension, partial repaint isn't
213  // performed as we skip right to present.
214  if (damage_rect->IsEmpty()) {
215  return false;
216  }
217  return true;
218 }
219 
220 // |Surface|
222  return IRect::MakeSize(resolve_texture_->GetSize());
223 }
224 
225 // |Surface|
226 bool SurfaceMTL::Present() const {
227  auto context = context_.lock();
228  if (!context) {
229  return false;
230  }
231 
232  if (requires_blit_) {
233  if (!(source_texture_ && destination_texture_)) {
234  return false;
235  }
236 
237  auto blit_command_buffer = context->CreateCommandBuffer();
238  if (!blit_command_buffer) {
239  return false;
240  }
241  auto blit_pass = blit_command_buffer->CreateBlitPass();
242  if (!clip_rect_.has_value()) {
243  VALIDATION_LOG << "Missing clip rectangle.";
244  return false;
245  }
246  blit_pass->AddCopy(source_texture_, destination_texture_, std::nullopt,
247  clip_rect_->GetOrigin());
248  blit_pass->EncodeCommands(context->GetResourceAllocator());
249  if (!context->GetCommandQueue()->Submit({blit_command_buffer}).ok()) {
250  return false;
251  }
252  }
253 #ifdef IMPELLER_DEBUG
254  ContextMTL::Cast(context.get())->GetGPUTracer()->MarkFrameEnd();
255 #endif // IMPELLER_DEBUG
256 
257  if (drawable_) {
258  id<MTLCommandBuffer> command_buffer =
259  ContextMTL::Cast(context.get())
260  ->CreateMTLCommandBuffer("Present Waiter Command Buffer");
261 
262  id<CAMetalDrawable> metal_drawable =
263  reinterpret_cast<id<CAMetalDrawable>>(drawable_);
264  if ([metal_drawable conformsToProtocol:@protocol(FlutterMetalDrawable)]) {
266  flutterPrepareForPresent:command_buffer];
267  }
268 
269  // If the threads have been merged, or there is a pending frame capture,
270  // then block on cmd buffer scheduling to ensure that the
271  // transaction/capture work correctly.
272  if ([[NSThread currentThread] isMainThread] ||
273  [[MTLCaptureManager sharedCaptureManager] isCapturing]) {
274  TRACE_EVENT0("flutter", "waitUntilScheduled");
275  [command_buffer commit];
276  [command_buffer waitUntilScheduled];
277  [drawable_ present];
278  } else {
279  // The drawable may come from a FlutterMetalLayer, so it can't be
280  // presented through the command buffer.
281  id<CAMetalDrawable> drawable = drawable_;
282  [command_buffer addScheduledHandler:^(id<MTLCommandBuffer> buffer) {
283  [drawable present];
284  }];
285  [command_buffer commit];
286  }
287  }
288 
289  return true;
290 }
291 #pragma GCC diagnostic pop
292 
293 } // namespace impeller
impeller::StoreAction::kMultisampleResolve
@ kMultisampleResolve
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:642
impeller::SurfaceMTL
Definition: surface_mtl.h:18
context_mtl.h
impeller::ColorAttachment
Definition: formats.h:647
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:40
impeller::SurfaceMTL::coverage
IRect coverage() const
Definition: surface_mtl.mm:221
texture_descriptor.h
formats_mtl.h
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::Allocator::CreateTexture
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Definition: allocator.cc:49
impeller::TextureDescriptor::sample_count
SampleCount sample_count
Definition: texture_descriptor.h:44
validation.h
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:43
impeller::TSize< int64_t >::Type
int64_t Type
Definition: size.h:20
impeller::Surface
Definition: surface.h:18
impeller::WrapTextureWithRenderTarget
static std::optional< RenderTarget > WrapTextureWithRenderTarget(Allocator &allocator, id< MTLTexture > texture, bool requires_blit, std::optional< IRect > clip_rect)
Definition: surface_mtl.mm:47
impeller::TextureDescriptor::type
TextureType type
Definition: texture_descriptor.h:39
impeller::TextureType::kTexture2DMultisample
@ kTexture2DMultisample
impeller::Color::DarkSlateGray
static constexpr Color DarkSlateGray()
Definition: color.h:410
impeller::TSize< int64_t >
impeller::LoadAction::kClear
@ kClear
impeller::StorageMode::kDeviceTransient
@ kDeviceTransient
impeller::ColorAttachment::clear_color
Color clear_color
Definition: formats.h:648
impeller::SurfaceMTL::~SurfaceMTL
~SurfaceMTL() override
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:639
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
FlutterMetalDrawable-p
Definition: surface_mtl.mm:16
impeller::SurfaceMTL::MakeFromMetalLayerDrawable
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
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:22
impeller::TextureMTL::Create
static std::shared_ptr< TextureMTL > Create(TextureDescriptor desc, id< MTLTexture > texture)
Definition: texture_mtl.mm:57
impeller::RenderTarget
Definition: render_target.h:38
impeller::SurfaceMTL::MakeFromTexture
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
impeller::CompressionType::kLossy
@ kLossy
impeller::SurfaceMTL::drawable
id< MTLDrawable > drawable() const
Definition: surface_mtl.h:57
impeller::TextureMTL::Wrapper
static std::shared_ptr< TextureMTL > Wrapper(TextureDescriptor desc, id< MTLTexture > texture, std::function< void()> deletion_proc=nullptr)
Definition: texture_mtl.mm:40
surface_mtl.h
impeller::PixelFormat::kUnknown
@ kUnknown
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:41
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
command_buffer.h
impeller::Attachment::resolve_texture
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:640
impeller::TRect< int64_t >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
std
Definition: comparable.h:95
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:25
texture_mtl.h
impeller::BackendCast< ContextMTL, Context >::Cast
static ContextMTL & Cast(Context &base)
Definition: backend_cast.h:13
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:641
impeller::ContextMTL::CreateMTLCommandBuffer
id< MTLCommandBuffer > CreateMTLCommandBuffer(const std::string &label) const
Definition: context_mtl.mm:366
impeller::TextureUsage::kShaderRead
@ kShaderRead
impeller::FromMTLPixelFormat
constexpr PixelFormat FromMTLPixelFormat(MTLPixelFormat format)
Definition: formats_mtl.h:23
impeller::SampleCount::kCount1
@ kCount1
impeller::SampleCount::kCount4
@ kCount4
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:38
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:37
render_target.h
impeller::TextureDescriptor::compression_type
CompressionType compression_type
Definition: texture_descriptor.h:45
impeller
Definition: aiks_blur_unittests.cc:20
impeller::TRect< int64_t >
impeller::SurfaceMTL::Present
bool Present() const override
Definition: surface_mtl.mm:226