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