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 static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor(
104  const RenderTarget& desc) {
105  auto result = [MTLRenderPassDescriptor renderPassDescriptor];
106 
107  bool configured_attachment = desc.IterateAllColorAttachments(
108  [&result](size_t index, const ColorAttachment& attachment) -> bool {
109  return ConfigureColorAttachment(attachment,
110  result.colorAttachments[index]);
111  });
112 
113  if (!configured_attachment) {
114  VALIDATION_LOG << "Could not configure color attachments";
115  return nil;
116  }
117 
118  const auto& depth = desc.GetDepthAttachment();
119 
120  if (depth.has_value() &&
121  !ConfigureDepthAttachment(depth.value(), result.depthAttachment)) {
122  VALIDATION_LOG << "Could not configure depth attachment.";
123  return nil;
124  }
125 
126  const auto& stencil = desc.GetStencilAttachment();
127 
128  if (stencil.has_value() &&
129  !ConfigureStencilAttachment(stencil.value(), result.stencilAttachment)) {
130  VALIDATION_LOG << "Could not configure stencil attachment.";
131  return nil;
132  }
133 
134  return result;
135 }
136 
137 RenderPassMTL::RenderPassMTL(std::shared_ptr<const Context> context,
138  const RenderTarget& target,
139  id<MTLCommandBuffer> buffer)
140  : RenderPass(std::move(context), target),
141  buffer_(buffer),
142  desc_(ToMTLRenderPassDescriptor(GetRenderTarget())) {
143  if (!buffer_ || !desc_ || !render_target_.IsValid()) {
144  return;
145  }
146  encoder_ = [buffer_ renderCommandEncoderWithDescriptor:desc_];
147 
148  if (!encoder_) {
149  return;
150  }
151 #ifdef IMPELLER_DEBUG
152  is_metal_trace_active_ =
153  [[MTLCaptureManager sharedCaptureManager] isCapturing];
154 #endif // IMPELLER_DEBUG
155  pass_bindings_.SetEncoder(encoder_);
156  pass_bindings_.SetViewport(
157  Viewport{.rect = Rect::MakeSize(GetRenderTargetSize())});
158  pass_bindings_.SetScissor(IRect::MakeSize(GetRenderTargetSize()));
159  is_valid_ = true;
160 }
161 
162 RenderPassMTL::~RenderPassMTL() {
163  if (!did_finish_encoding_) {
164  [encoder_ endEncoding];
165  did_finish_encoding_ = true;
166  }
167 }
168 
169 bool RenderPassMTL::IsValid() const {
170  return is_valid_;
171 }
172 
173 void RenderPassMTL::OnSetLabel(std::string_view label) {
174 #ifdef IMPELLER_DEBUG
175  if (label.empty()) {
176  return;
177  }
178  encoder_.label = @(std::string(label).c_str());
179 #endif // IMPELLER_DEBUG
180 }
181 
182 bool RenderPassMTL::OnEncodeCommands(const Context& context) const {
183  did_finish_encoding_ = true;
184  [encoder_ endEncoding];
185  return true;
186 }
187 
188 static bool Bind(PassBindingsCacheMTL& pass,
189  ShaderStage stage,
190  size_t bind_index,
191  const BufferView& view) {
192  if (!view.GetBuffer()) {
193  return false;
194  }
195 
196  const DeviceBuffer* device_buffer = view.GetBuffer();
197  if (!device_buffer) {
198  return false;
199  }
200 
201  auto buffer = DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer();
202  // The Metal call is a void return and we don't want to make it on nil.
203  if (!buffer) {
204  return false;
205  }
206 
207  return pass.SetBuffer(stage, bind_index, view.GetRange().offset, buffer);
208 }
209 
210 static bool Bind(PassBindingsCacheMTL& pass,
211  ShaderStage stage,
212  size_t bind_index,
213  raw_ptr<const Sampler> sampler,
214  const Texture& texture) {
215  if (!sampler || !texture.IsValid()) {
216  return false;
217  }
218 
219  if (texture.NeedsMipmapGeneration()) {
220  // TODO(127697): generate mips when the GPU is available on iOS.
221 #if !FML_OS_IOS
223  << "Texture at binding index " << bind_index
224  << " has a mip count > 1, but the mipmap has not been generated.";
225  return false;
226 #endif // !FML_OS_IOS
227  }
228 
229  return pass.SetTexture(stage, bind_index,
230  TextureMTL::Cast(texture).GetMTLTexture()) &&
231  pass.SetSampler(stage, bind_index,
232  SamplerMTL::Cast(*sampler).GetMTLSamplerState());
233 }
234 
235 // |RenderPass|
236 void RenderPassMTL::SetPipeline(PipelineRef pipeline) {
237  const PipelineDescriptor& pipeline_desc = pipeline->GetDescriptor();
238  primitive_type_ = pipeline_desc.GetPrimitiveType();
239  pass_bindings_.SetRenderPipelineState(
240  PipelineMTL::Cast(*pipeline).GetMTLRenderPipelineState());
241  pass_bindings_.SetDepthStencilState(
242  PipelineMTL::Cast(*pipeline).GetMTLDepthStencilState());
243 
244  [encoder_ setFrontFacingWinding:pipeline_desc.GetWindingOrder() ==
245  WindingOrder::kClockwise
246  ? MTLWindingClockwise
247  : MTLWindingCounterClockwise];
248  [encoder_ setCullMode:ToMTLCullMode(pipeline_desc.GetCullMode())];
249  [encoder_ setTriangleFillMode:ToMTLTriangleFillMode(
250  pipeline_desc.GetPolygonMode())];
251  has_valid_pipeline_ = true;
252 }
253 
254 // |RenderPass|
255 void RenderPassMTL::SetCommandLabel(std::string_view label) {
256 #ifdef IMPELLER_DEBUG
257  if (is_metal_trace_active_) {
258  has_label_ = true;
259  std::string label_copy(label);
260  [encoder_ pushDebugGroup:@(label_copy.c_str())];
261  }
262 #endif // IMPELLER_DEBUG
263 }
264 
265 // |RenderPass|
266 void RenderPassMTL::SetStencilReference(uint32_t value) {
267  pass_bindings_.SetStencilRef(value);
268 }
269 
270 // |RenderPass|
271 void RenderPassMTL::SetBaseVertex(uint64_t value) {
272  base_vertex_ = value;
273 }
274 
275 // |RenderPass|
276 void RenderPassMTL::SetViewport(Viewport viewport) {
277  pass_bindings_.SetViewport(viewport);
278 }
279 
280 // |RenderPass|
281 void RenderPassMTL::SetScissor(IRect scissor) {
282  pass_bindings_.SetScissor(scissor);
283 }
284 
285 // |RenderPass|
286 void RenderPassMTL::SetElementCount(size_t count) {
287  vertex_count_ = count;
288 }
289 
290 // |RenderPass|
291 void RenderPassMTL::SetInstanceCount(size_t count) {
292  instance_count_ = count;
293 }
294 
295 // |RenderPass|
296 bool RenderPassMTL::SetVertexBuffer(BufferView vertex_buffers[],
297  size_t vertex_buffer_count) {
298  if (!ValidateVertexBuffers(vertex_buffers, vertex_buffer_count)) {
299  return false;
300  }
301 
302  for (size_t i = 0; i < vertex_buffer_count; i++) {
303  if (!Bind(pass_bindings_, ShaderStage::kVertex,
304  VertexDescriptor::kReservedVertexBufferIndex - i,
305  vertex_buffers[i])) {
306  return false;
307  }
308  }
309 
310  return true;
311 }
312 
313 // |RenderPass|
314 bool RenderPassMTL::SetIndexBuffer(BufferView index_buffer,
315  IndexType index_type) {
316  if (!ValidateIndexBuffer(index_buffer, index_type)) {
317  return false;
318  }
319 
320  if (index_type != IndexType::kNone) {
321  index_type_ = ToMTLIndexType(index_type);
322  index_buffer_ = std::move(index_buffer);
323  }
324 
325  return true;
326 }
327 
328 // |RenderPass|
329 fml::Status RenderPassMTL::Draw() {
330  if (!has_valid_pipeline_) {
331  return fml::Status(fml::StatusCode::kCancelled, "Invalid pipeline.");
332  }
333 
334  if (!index_buffer_) {
335  if (instance_count_ != 1u) {
336  [encoder_ drawPrimitives:ToMTLPrimitiveType(primitive_type_)
337  vertexStart:base_vertex_
338  vertexCount:vertex_count_
339  instanceCount:instance_count_
340  baseInstance:0u];
341  } else {
342  [encoder_ drawPrimitives:ToMTLPrimitiveType(primitive_type_)
343  vertexStart:base_vertex_
344  vertexCount:vertex_count_];
345  }
346  } else {
347  id<MTLBuffer> mtl_index_buffer =
348  DeviceBufferMTL::Cast(*index_buffer_.GetBuffer()).GetMTLBuffer();
349  if (instance_count_ != 1u) {
350  [encoder_ drawIndexedPrimitives:ToMTLPrimitiveType(primitive_type_)
351  indexCount:vertex_count_
352  indexType:index_type_
353  indexBuffer:mtl_index_buffer
354  indexBufferOffset:index_buffer_.GetRange().offset
355  instanceCount:instance_count_
356  baseVertex:base_vertex_
357  baseInstance:0u];
358  } else {
359  [encoder_ drawIndexedPrimitives:ToMTLPrimitiveType(primitive_type_)
360  indexCount:vertex_count_
361  indexType:index_type_
362  indexBuffer:mtl_index_buffer
363  indexBufferOffset:index_buffer_.GetRange().offset];
364  }
365  }
366 
367 #ifdef IMPELLER_DEBUG
368  if (has_label_) {
369  [encoder_ popDebugGroup];
370  }
371 #endif // IMPELLER_DEBUG
372 
373  vertex_count_ = 0u;
374  base_vertex_ = 0u;
375  instance_count_ = 1u;
376  index_buffer_ = {};
377  has_valid_pipeline_ = false;
378  has_label_ = false;
379 
380  return fml::Status();
381 }
382 
383 // |RenderPass|
384 bool RenderPassMTL::BindResource(ShaderStage stage,
386  const ShaderUniformSlot& slot,
387  const ShaderMetadata* metadata,
388  BufferView view) {
389  return Bind(pass_bindings_, stage, slot.ext_res_0, view);
390 }
391 
392 // |RenderPass|
393 bool RenderPassMTL::BindDynamicResource(
394  ShaderStage stage,
396  const ShaderUniformSlot& slot,
397  std::unique_ptr<ShaderMetadata> metadata,
398  BufferView view) {
399  return Bind(pass_bindings_, stage, slot.ext_res_0, view);
400 }
401 
402 // |RenderPass|
403 bool RenderPassMTL::BindResource(ShaderStage stage,
405  const SampledImageSlot& slot,
406  const ShaderMetadata* metadata,
407  std::shared_ptr<const Texture> texture,
408  raw_ptr<const Sampler> sampler) {
409  if (!texture) {
410  return false;
411  }
412  return Bind(pass_bindings_, stage, slot.texture_index, sampler, *texture);
413 }
414 
415 bool RenderPassMTL::BindDynamicResource(
416  ShaderStage stage,
418  const SampledImageSlot& slot,
419  std::unique_ptr<ShaderMetadata> metadata,
420  std::shared_ptr<const Texture> texture,
421  raw_ptr<const Sampler> sampler) {
422  if (!texture) {
423  return false;
424  }
425  return Bind(pass_bindings_, stage, slot.texture_index, sampler, *texture);
426 }
427 
428 } // 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:147
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)
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)
IRect64 IRect
Definition: rect.h:795
constexpr MTLLoadAction ToMTLLoadAction(LoadAction action)
Definition: formats_mtl.h:279
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:150
static bool ConfigureStencilAttachment(const StencilAttachment &desc, MTLRenderPassStencilAttachmentDescriptor *attachment)
constexpr MTLTriangleFillMode ToMTLTriangleFillMode(PolygonMode mode)
Definition: formats_mtl.h:170
constexpr MTLIndexType ToMTLIndexType(IndexType type)
Definition: formats_mtl.h:180
constexpr MTLStoreAction ToMTLStoreAction(StoreAction action)
Definition: formats_mtl.h:307
MTLClearColor ToMTLClearColor(const Color &color)
Definition: formats_mtl.h:374
static bool ConfigureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
constexpr MTLCullMode ToMTLCullMode(CullMode mode)
Definition: formats_mtl.h:189
Definition: comparable.h:95
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:658
LoadAction load_action
Definition: formats.h:659
std::shared_ptr< Texture > texture
Definition: formats.h:657
StoreAction store_action
Definition: formats.h:660
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