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