Flutter Impeller
render_pass_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/closure.h"
8 #include "flutter/fml/logging.h"
9 #include "flutter/fml/make_copyable.h"
10 #include "fml/status.h"
11 
13 #include "impeller/core/formats.h"
24 
25 namespace impeller {
26 
28  const Attachment& desc,
29  MTLRenderPassAttachmentDescriptor* attachment) {
30  bool needs_resolve =
33 
34  if (needs_resolve && !desc.resolve_texture) {
35  VALIDATION_LOG << "Resolve store action specified on attachment but no "
36  "resolve texture was specified.";
37  return false;
38  }
39 
40  if (desc.resolve_texture && !needs_resolve) {
41  VALIDATION_LOG << "A resolve texture was specified even though the store "
42  "action doesn't require it.";
43  return false;
44  }
45 
46  if (!desc.resolve_texture) {
47  return true;
48  }
49 
50  attachment.resolveTexture =
52 
53  return true;
54 }
55 
56 static bool ConfigureAttachment(const Attachment& desc,
57  MTLRenderPassAttachmentDescriptor* attachment) {
58  if (!desc.texture) {
59  return false;
60  }
61 
62  attachment.texture = TextureMTL::Cast(*desc.texture).GetMTLTexture();
63  attachment.loadAction = ToMTLLoadAction(desc.load_action);
64  attachment.storeAction = ToMTLStoreAction(desc.store_action);
65 
66  if (!ConfigureResolveTextureAttachment(desc, attachment)) {
67  return false;
68  }
69 
70  return true;
71 }
72 
74  const ColorAttachment& desc,
75  MTLRenderPassColorAttachmentDescriptor* attachment) {
76  if (!ConfigureAttachment(desc, attachment)) {
77  return false;
78  }
79  attachment.clearColor = ToMTLClearColor(desc.clear_color);
80  return true;
81 }
82 
84  const DepthAttachment& desc,
85  MTLRenderPassDepthAttachmentDescriptor* attachment) {
86  if (!ConfigureAttachment(desc, attachment)) {
87  return false;
88  }
89  attachment.clearDepth = desc.clear_depth;
90  return true;
91 }
92 
94  const StencilAttachment& desc,
95  MTLRenderPassStencilAttachmentDescriptor* attachment) {
96  if (!ConfigureAttachment(desc, attachment)) {
97  return false;
98  }
99  attachment.clearStencil = desc.clear_stencil;
100  return true;
101 }
102 
103 // TODO(csg): Move this to formats_mtl.h
104 static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor(
105  const RenderTarget& desc) {
106  auto result = [MTLRenderPassDescriptor renderPassDescriptor];
107 
108  const auto& colors = desc.GetColorAttachments();
109 
110  for (const auto& color : colors) {
111  if (!ConfigureColorAttachment(color.second,
112  result.colorAttachments[color.first])) {
113  VALIDATION_LOG << "Could not configure color attachment at index "
114  << color.first;
115  return nil;
116  }
117  }
118 
119  const auto& depth = desc.GetDepthAttachment();
120 
121  if (depth.has_value() &&
122  !ConfigureDepthAttachment(depth.value(), result.depthAttachment)) {
123  VALIDATION_LOG << "Could not configure depth attachment.";
124  return nil;
125  }
126 
127  const auto& stencil = desc.GetStencilAttachment();
128 
129  if (stencil.has_value() &&
130  !ConfigureStencilAttachment(stencil.value(), result.stencilAttachment)) {
131  VALIDATION_LOG << "Could not configure stencil attachment.";
132  return nil;
133  }
134 
135  return result;
136 }
137 
138 RenderPassMTL::RenderPassMTL(std::shared_ptr<const Context> context,
139  const RenderTarget& target,
140  id<MTLCommandBuffer> buffer)
141  : RenderPass(std::move(context), target),
142  buffer_(buffer),
143  desc_(ToMTLRenderPassDescriptor(GetRenderTarget())) {
144  if (!buffer_ || !desc_ || !render_target_.IsValid()) {
145  return;
146  }
147  encoder_ = [buffer_ renderCommandEncoderWithDescriptor:desc_];
148 
149  if (!encoder_) {
150  return;
151  }
152 #ifdef IMPELLER_DEBUG
153  is_metal_trace_active_ =
154  [[MTLCaptureManager sharedCaptureManager] isCapturing];
155 #endif // IMPELLER_DEBUG
156  pass_bindings_.SetEncoder(encoder_);
157  pass_bindings_.SetViewport(
158  Viewport{.rect = Rect::MakeSize(GetRenderTargetSize())});
159  pass_bindings_.SetScissor(IRect::MakeSize(GetRenderTargetSize()));
160  is_valid_ = true;
161 }
162 
163 RenderPassMTL::~RenderPassMTL() {
164  if (!did_finish_encoding_) {
165  [encoder_ endEncoding];
166  did_finish_encoding_ = true;
167  }
168 }
169 
170 bool RenderPassMTL::IsValid() const {
171  return is_valid_;
172 }
173 
174 void RenderPassMTL::OnSetLabel(std::string label) {
175 #ifdef IMPELLER_DEBUG
176  if (label.empty()) {
177  return;
178  }
179  encoder_.label = @(std::string(label).c_str());
180 #endif // IMPELLER_DEBUG
181 }
182 
183 bool RenderPassMTL::OnEncodeCommands(const Context& context) const {
184  did_finish_encoding_ = true;
185  [encoder_ endEncoding];
186  return true;
187 }
188 
189 static bool Bind(PassBindingsCacheMTL& pass,
190  ShaderStage stage,
191  size_t bind_index,
192  const BufferView& view) {
193  if (!view.buffer) {
194  return false;
195  }
196 
197  auto device_buffer = view.buffer;
198  if (!device_buffer) {
199  return false;
200  }
201 
202  auto buffer = DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer();
203  // The Metal call is a void return and we don't want to make it on nil.
204  if (!buffer) {
205  return false;
206  }
207 
208  return pass.SetBuffer(stage, bind_index, view.range.offset, buffer);
209 }
210 
211 static bool Bind(PassBindingsCacheMTL& pass,
212  ShaderStage stage,
213  size_t bind_index,
214  const std::unique_ptr<const Sampler>& sampler,
215  const Texture& texture) {
216  if (!sampler || !texture.IsValid()) {
217  return false;
218  }
219 
220  if (texture.NeedsMipmapGeneration()) {
221  // TODO(127697): generate mips when the GPU is available on iOS.
222 #if !FML_OS_IOS
224  << "Texture at binding index " << bind_index
225  << " has a mip count > 1, but the mipmap has not been generated.";
226  return false;
227 #endif // !FML_OS_IOS
228  }
229 
230  return pass.SetTexture(stage, bind_index,
231  TextureMTL::Cast(texture).GetMTLTexture()) &&
232  pass.SetSampler(stage, bind_index,
233  SamplerMTL::Cast(*sampler).GetMTLSamplerState());
234 }
235 
236 // |RenderPass|
237 void RenderPassMTL::SetPipeline(
238  const std::shared_ptr<Pipeline<PipelineDescriptor>>& pipeline) {
239  const PipelineDescriptor& pipeline_desc = pipeline->GetDescriptor();
240  primitive_type_ = pipeline_desc.GetPrimitiveType();
241  pass_bindings_.SetRenderPipelineState(
242  PipelineMTL::Cast(*pipeline).GetMTLRenderPipelineState());
243  pass_bindings_.SetDepthStencilState(
244  PipelineMTL::Cast(*pipeline).GetMTLDepthStencilState());
245 
246  [encoder_ setFrontFacingWinding:pipeline_desc.GetWindingOrder() ==
247  WindingOrder::kClockwise
248  ? MTLWindingClockwise
249  : MTLWindingCounterClockwise];
250  [encoder_ setCullMode:ToMTLCullMode(pipeline_desc.GetCullMode())];
251  [encoder_ setTriangleFillMode:ToMTLTriangleFillMode(
252  pipeline_desc.GetPolygonMode())];
253  has_valid_pipeline_ = true;
254 }
255 
256 // |RenderPass|
257 void RenderPassMTL::SetCommandLabel(std::string_view label) {
258 #ifdef IMPELLER_DEBUG
259  if (is_metal_trace_active_) {
260  has_label_ = true;
261  std::string label_copy(label);
262  [encoder_ pushDebugGroup:@(label_copy.c_str())];
263  }
264 #endif // IMPELLER_DEBUG
265 }
266 
267 // |RenderPass|
268 void RenderPassMTL::SetStencilReference(uint32_t value) {
269  [encoder_ setStencilReferenceValue:value];
270 }
271 
272 // |RenderPass|
273 void RenderPassMTL::SetBaseVertex(uint64_t value) {
274  base_vertex_ = value;
275 }
276 
277 // |RenderPass|
278 void RenderPassMTL::SetViewport(Viewport viewport) {
279  pass_bindings_.SetViewport(viewport);
280 }
281 
282 // |RenderPass|
283 void RenderPassMTL::SetScissor(IRect scissor) {
284  pass_bindings_.SetScissor(scissor);
285 }
286 
287 // |RenderPass|
288 void RenderPassMTL::SetInstanceCount(size_t count) {
289  instance_count_ = count;
290 }
291 
292 // |RenderPass|
293 bool RenderPassMTL::SetVertexBuffer(VertexBuffer buffer) {
294  if (buffer.index_type == IndexType::kUnknown) {
295  return false;
296  }
297 
298  if (!Bind(pass_bindings_, ShaderStage::kVertex,
299  VertexDescriptor::kReservedVertexBufferIndex,
300  buffer.vertex_buffer)) {
301  return false;
302  }
303 
304  vertex_count_ = buffer.vertex_count;
305  if (buffer.index_type != IndexType::kNone) {
306  index_type_ = ToMTLIndexType(buffer.index_type);
307  index_buffer_ = std::move(buffer.index_buffer);
308  }
309  return true;
310 }
311 
312 // |RenderPass|
313 fml::Status RenderPassMTL::Draw() {
314  if (!has_valid_pipeline_) {
315  return fml::Status(fml::StatusCode::kCancelled, "Invalid pipeline.");
316  }
317 
318  if (!index_buffer_) {
319  if (instance_count_ != 1u) {
320  [encoder_ drawPrimitives:ToMTLPrimitiveType(primitive_type_)
321  vertexStart:base_vertex_
322  vertexCount:vertex_count_
323  instanceCount:instance_count_
324  baseInstance:0u];
325  } else {
326  [encoder_ drawPrimitives:ToMTLPrimitiveType(primitive_type_)
327  vertexStart:base_vertex_
328  vertexCount:vertex_count_];
329  }
330  } else {
331  id<MTLBuffer> mtl_index_buffer =
332  DeviceBufferMTL::Cast(*index_buffer_.buffer).GetMTLBuffer();
333  if (instance_count_ != 1u) {
334  [encoder_ drawIndexedPrimitives:ToMTLPrimitiveType(primitive_type_)
335  indexCount:vertex_count_
336  indexType:index_type_
337  indexBuffer:mtl_index_buffer
338  indexBufferOffset:index_buffer_.range.offset
339  instanceCount:instance_count_
340  baseVertex:base_vertex_
341  baseInstance:0u];
342  } else {
343  [encoder_ drawIndexedPrimitives:ToMTLPrimitiveType(primitive_type_)
344  indexCount:vertex_count_
345  indexType:index_type_
346  indexBuffer:mtl_index_buffer
347  indexBufferOffset:index_buffer_.range.offset];
348  }
349  }
350 
351 #ifdef IMPELLER_DEBUG
352  if (has_label_) {
353  [encoder_ popDebugGroup];
354  }
355 #endif // IMPELLER_DEBUG
356 
357  vertex_count_ = 0u;
358  base_vertex_ = 0u;
359  instance_count_ = 1u;
360  index_buffer_ = {};
361  has_valid_pipeline_ = false;
362  has_label_ = false;
363 
364  return fml::Status();
365 }
366 
367 // |RenderPass|
368 bool RenderPassMTL::BindResource(ShaderStage stage,
369  DescriptorType type,
370  const ShaderUniformSlot& slot,
371  const ShaderMetadata& metadata,
372  BufferView view) {
373  return Bind(pass_bindings_, stage, slot.ext_res_0, view);
374 }
375 
376 // |RenderPass|
377 bool RenderPassMTL::BindResource(
378  ShaderStage stage,
379  DescriptorType type,
380  const ShaderUniformSlot& slot,
381  const std::shared_ptr<const ShaderMetadata>& metadata,
382  BufferView view) {
383  return Bind(pass_bindings_, stage, slot.ext_res_0, view);
384 }
385 
386 // |RenderPass|
387 bool RenderPassMTL::BindResource(
388  ShaderStage stage,
389  DescriptorType type,
390  const SampledImageSlot& slot,
391  const ShaderMetadata& metadata,
392  std::shared_ptr<const Texture> texture,
393  const std::unique_ptr<const Sampler>& sampler) {
394  return Bind(pass_bindings_, stage, slot.texture_index, sampler, *texture);
395 }
396 
397 } // namespace impeller
impeller::StoreAction::kMultisampleResolve
@ kMultisampleResolve
impeller::Texture::NeedsMipmapGeneration
bool NeedsMipmapGeneration() const
Definition: texture.cc:85
host_buffer.h
impeller::StoreAction::kStoreAndMultisampleResolve
@ kStoreAndMultisampleResolve
impeller::ConfigureColorAttachment
static bool ConfigureColorAttachment(const ColorAttachment &desc, MTLRenderPassColorAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:73
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:642
impeller::ConfigureStencilAttachment
static bool ConfigureStencilAttachment(const StencilAttachment &desc, MTLRenderPassStencilAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:93
context_mtl.h
impeller::Bind
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const std::unique_ptr< const Sampler > &sampler, const Texture &texture)
Definition: render_pass_mtl.mm:211
render_pass_mtl.h
impeller::ColorAttachment
Definition: formats.h:647
impeller::Range::offset
size_t offset
Definition: range.h:15
impeller::ConfigureAttachment
static bool ConfigureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:56
formats.h
impeller::TextureMTL::GetMTLTexture
id< MTLTexture > GetMTLTexture() const
Definition: texture_mtl.mm:116
formats_mtl.h
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::ToMTLStoreAction
constexpr MTLStoreAction ToMTLStoreAction(StoreAction action)
Definition: formats_mtl.h:304
impeller::PassBindingsCacheMTL::SetBuffer
bool SetBuffer(ShaderStage stage, uint64_t index, uint64_t offset, id< MTLBuffer > buffer)
Definition: pass_bindings_cache_mtl.mm:31
impeller::PassBindingsCacheMTL
Ensures that bindings on the pass are not redundantly set or updated. Avoids making the driver do add...
Definition: pass_bindings_cache_mtl.h:24
impeller::BufferView::range
Range range
Definition: buffer_view.h:17
impeller::RenderTarget::GetColorAttachments
const std::map< size_t, ColorAttachment > & GetColorAttachments() const
Definition: render_target.cc:198
impeller::Texture::IsValid
virtual bool IsValid() const =0
pipeline_mtl.h
impeller::ToMTLClearColor
MTLClearColor ToMTLClearColor(const Color &color)
Definition: formats_mtl.h:369
vertex_descriptor.h
command.h
impeller::StencilAttachment
Definition: formats.h:655
impeller::ConfigureDepthAttachment
static bool ConfigureDepthAttachment(const DepthAttachment &desc, MTLRenderPassDepthAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:83
impeller::Texture
Definition: texture.h:17
impeller::RenderTarget::GetDepthAttachment
const std::optional< DepthAttachment > & GetDepthAttachment() const
Definition: render_target.cc:203
impeller::ColorAttachment::clear_color
Color clear_color
Definition: formats.h:648
impeller::ToMTLLoadAction
constexpr MTLLoadAction ToMTLLoadAction(LoadAction action)
Definition: formats_mtl.h:276
impeller::ToMTLRenderPassDescriptor
static MTLRenderPassDescriptor * ToMTLRenderPassDescriptor(const RenderTarget &desc)
Definition: render_pass_mtl.mm:104
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:639
impeller::ToMTLPrimitiveType
constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type)
Definition: formats_mtl.h:151
backend_cast.h
impeller::Attachment
Definition: formats.h:638
impeller::RenderTarget
Definition: render_target.h:38
impeller::StencilAttachment::clear_stencil
uint32_t clear_stencil
Definition: formats.h:656
impeller::ConfigureResolveTextureAttachment
static bool ConfigureResolveTextureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:27
impeller::IRect
TRect< int64_t > IRect
Definition: rect.h:662
impeller::PassBindingsCacheMTL::SetTexture
bool SetTexture(ShaderStage stage, uint64_t index, id< MTLTexture > texture)
Definition: pass_bindings_cache_mtl.mm:75
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::BufferView
Definition: buffer_view.h:15
impeller::Attachment::resolve_texture
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:640
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
std
Definition: comparable.h:95
texture_mtl.h
impeller::BackendCast< TextureMTL, Texture >::Cast
static TextureMTL & Cast(Texture &base)
Definition: backend_cast.h:13
impeller::BufferView::buffer
std::shared_ptr< const DeviceBuffer > buffer
Definition: buffer_view.h:16
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:641
impeller::ToMTLIndexType
constexpr MTLIndexType ToMTLIndexType(IndexType type)
Definition: formats_mtl.h:177
impeller::DepthAttachment::clear_depth
double clear_depth
Definition: formats.h:652
sampler_mtl.h
impeller::PassBindingsCacheMTL::SetSampler
bool SetSampler(ShaderStage stage, uint64_t index, id< MTLSamplerState > sampler)
Definition: pass_bindings_cache_mtl.mm:99
impeller::DepthAttachment
Definition: formats.h:651
impeller::RenderTarget::GetStencilAttachment
const std::optional< StencilAttachment > & GetStencilAttachment() const
Definition: render_target.cc:207
shader_types.h
impeller::ToMTLCullMode
constexpr MTLCullMode ToMTLCullMode(CullMode mode)
Definition: formats_mtl.h:186
impeller
Definition: aiks_blur_unittests.cc:20
impeller::ToMTLTriangleFillMode
constexpr MTLTriangleFillMode ToMTLTriangleFillMode(PolygonMode mode)
Definition: formats_mtl.h:167
device_buffer_mtl.h
impeller::DescriptorType
DescriptorType
Definition: shader_types.h:153