| 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
5 #include "impeller/renderer/backend/metal/render_pass_mtl.h"
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
12 #include "impeller/base/backend_cast.h"
13 #include "impeller/core/formats.h"
14 #include "impeller/core/host_buffer.h"
15 #include "impeller/core/shader_types.h"
16 #include "impeller/renderer/backend/metal/context_mtl.h"
17 #include "impeller/renderer/backend/metal/device_buffer_mtl.h"
18 #include "impeller/renderer/backend/metal/formats_mtl.h"
19 #include "impeller/renderer/backend/metal/pipeline_mtl.h"
20 #include "impeller/renderer/backend/metal/sampler_mtl.h"
21 #include "impeller/renderer/backend/metal/texture_mtl.h"
22 #include "impeller/renderer/command.h"
23 #include "impeller/renderer/pipeline_descriptor.h"
24 #include "impeller/renderer/pipeline_library.h"
25 #include "impeller/renderer/vertex_descriptor.h"
26
27 namespace impeller {
28
29 static bool ConfigureResolveTextureAttachment(
30 const Attachment& desc,
31 MTLRenderPassAttachmentDescriptor* attachment) {
32 bool needs_resolve =
33 desc.store_action == StoreAction::kMultisampleResolve ||
34 desc.store_action == StoreAction::kStoreAndMultisampleResolve;
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 =
53 TextureMTL::Cast(*desc.resolve_texture).GetMTLTexture();
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
75 static bool ConfigureColorAttachment(
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
85 static bool ConfigureDepthAttachment(
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
95 static bool ConfigureStencilAttachment(
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
224 VALIDATION_LOG
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,
388 DescriptorType type,
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,
398 DescriptorType type,
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,
407 DescriptorType type,
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,
420 DescriptorType type,
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
backend_cast.h
type
GLenum type
Definition: blit_command_gles.cc:153
impeller::BackendCast< TextureMTL, Texture >::Cast
static TextureMTL & Cast(Texture &base)
Definition: backend_cast.h:13
impeller::DeviceBuffer
Definition: device_buffer.h:18
impeller::RenderTarget
Definition: render_target.h:38
impeller::RenderTarget::IterateAllColorAttachments
bool IterateAllColorAttachments(const std::function< bool(size_t index, const ColorAttachment &attachment)> &iterator) const
Definition: render_target.cc:96
impeller::RenderTarget::GetDepthAttachment
const std::optional< DepthAttachment > & GetDepthAttachment() const
Definition: render_target.cc:247
impeller::RenderTarget::GetStencilAttachment
const std::optional< StencilAttachment > & GetStencilAttachment() const
Definition: render_target.cc:251
impeller::Texture
Definition: texture.h:17
impeller::Texture::NeedsMipmapGeneration
bool NeedsMipmapGeneration() const
Definition: texture.cc:85
impeller::Texture::IsValid
virtual bool IsValid() const =0
impeller::TextureMTL::GetMTLTexture
id< MTLTexture > GetMTLTexture() const
Definition: texture_mtl.mm:149
impeller::raw_ptr
A wrapper around a raw ptr that adds additional unopt mode only checks.
Definition: raw_ptr.h:15
command.h
desc_
std::optional< PipelineDescriptor > desc_
Definition: content_context.cc:77
formats.h
device_buffer_mtl.h
value
int32_t value
Definition: dl_golden_unittests.cc:459
formats_mtl.h
host_buffer.h
impeller
Definition: allocation.cc:12
impeller::ConfigureColorAttachment
static bool ConfigureColorAttachment(const ColorAttachment &desc, MTLRenderPassColorAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:75
impeller::IRect32
TRect< int32_t > IRect32
Definition: rect.h:789
impeller::IndexType
IndexType
Definition: formats.h:346
impeller::ConfigureDepthAttachment
static bool ConfigureDepthAttachment(const DepthAttachment &desc, MTLRenderPassDepthAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:85
impeller::PipelineRef
raw_ptr< Pipeline< PipelineDescriptor > > PipelineRef
A raw ptr to a pipeline object.
Definition: pipeline.h:89
impeller::ToMTLRenderPassDescriptor
static MTLRenderPassDescriptor * ToMTLRenderPassDescriptor(const RenderTarget &desc)
Definition: render_pass_mtl.mm:105
impeller::ToMTLLoadAction
constexpr MTLLoadAction ToMTLLoadAction(LoadAction action)
Definition: formats_mtl.h:281
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::Bind
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, raw_ptr< const Sampler > sampler, const Texture &texture)
Definition: render_pass_mtl.mm:212
impeller::StoreAction::kStoreAndMultisampleResolve
@ kStoreAndMultisampleResolve
impeller::StoreAction::kMultisampleResolve
@ kMultisampleResolve
impeller::ConfigureResolveTextureAttachment
static bool ConfigureResolveTextureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:29
impeller::ToMTLPrimitiveType
constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type)
Definition: formats_mtl.h:152
impeller::DescriptorType
DescriptorType
Definition: shader_types.h:158
impeller::ConfigureStencilAttachment
static bool ConfigureStencilAttachment(const StencilAttachment &desc, MTLRenderPassStencilAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:95
impeller::ToMTLTriangleFillMode
constexpr MTLTriangleFillMode ToMTLTriangleFillMode(PolygonMode mode)
Definition: formats_mtl.h:172
impeller::ToMTLIndexType
constexpr MTLIndexType ToMTLIndexType(IndexType type)
Definition: formats_mtl.h:182
impeller::ToMTLStoreAction
constexpr MTLStoreAction ToMTLStoreAction(StoreAction action)
Definition: formats_mtl.h:309
impeller::ToMTLClearColor
MTLClearColor ToMTLClearColor(const Color &color)
Definition: formats_mtl.h:376
impeller::ConfigureAttachment
static bool ConfigureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
Definition: render_pass_mtl.mm:58
impeller::ToMTLCullMode
constexpr MTLCullMode ToMTLCullMode(CullMode mode)
Definition: formats_mtl.h:191
std
Definition: comparable.h:95
pipeline_descriptor.h
pipeline_library.h
pipeline_mtl.h
render_pass_mtl.h
context_mtl.h
sampler_mtl.h
shader_types.h
impeller::Attachment
Definition: formats.h:660
impeller::Attachment::resolve_texture
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:662
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:663
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:661
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:664
impeller::BufferView
Definition: buffer_view.h:19
impeller::BufferView::GetRange
Range GetRange() const
Definition: buffer_view.h:27
impeller::BufferView::GetBuffer
const DeviceBuffer * GetBuffer() const
Definition: buffer_view.cc:17
impeller::ColorAttachment
Definition: formats.h:669
impeller::ColorAttachment::clear_color
Color clear_color
Definition: formats.h:670
impeller::DepthAttachment
Definition: formats.h:673
impeller::DepthAttachment::clear_depth
double clear_depth
Definition: formats.h:674
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::PassBindingsCacheMTL::SetSampler
bool SetSampler(ShaderStage stage, uint64_t index, id< MTLSamplerState > sampler)
Set the sampler for the given stage and binding.
Definition: pass_bindings_cache_mtl.mm:99
impeller::PassBindingsCacheMTL::SetBuffer
bool SetBuffer(ShaderStage stage, uint64_t index, uint64_t offset, id< MTLBuffer > buffer)
Set the buffer for the given shader stage, binding, and offset.
Definition: pass_bindings_cache_mtl.mm:31
impeller::PassBindingsCacheMTL::SetTexture
bool SetTexture(ShaderStage stage, uint64_t index, id< MTLTexture > texture)
Set the texture for the given stage and binding.
Definition: pass_bindings_cache_mtl.mm:75
impeller::Range::offset
size_t offset
Definition: range.h:14
impeller::StencilAttachment
Definition: formats.h:677
impeller::StencilAttachment::clear_stencil
uint32_t clear_stencil
Definition: formats.h:678
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
texture_mtl.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
vertex_descriptor.h