Flutter Engine
 
Loading...
Searching...
No Matches
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
10#include "fml/status.h"
11
24
25namespace 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
56static 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
103static 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
137RenderPassMTL::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(IRect32::MakeSize(GetRenderTargetSize()));
159 is_valid_ = true;
160}
161
162RenderPassMTL::~RenderPassMTL() {
163 if (!did_finish_encoding_) {
164 [encoder_ endEncoding];
165 did_finish_encoding_ = true;
166 }
167}
168
169bool RenderPassMTL::IsValid() const {
170 return is_valid_;
171}
172
173void 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
182bool RenderPassMTL::OnEncodeCommands(const Context& context) const {
183 did_finish_encoding_ = true;
184 [encoder_ endEncoding];
185 return true;
186}
187
188static 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
210static bool Bind(PassBindingsCacheMTL& pass,
211 ShaderStage stage,
212 size_t bind_index,
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|
236void 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|
255void 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|
266void RenderPassMTL::SetStencilReference(uint32_t value) {
267 pass_bindings_.SetStencilRef(value);
268}
269
270// |RenderPass|
271void RenderPassMTL::SetBaseVertex(uint64_t value) {
272 base_vertex_ = value;
273}
274
275// |RenderPass|
276void RenderPassMTL::SetViewport(Viewport viewport) {
277 pass_bindings_.SetViewport(viewport);
278}
279
280// |RenderPass|
281void RenderPassMTL::SetScissor(IRect32 scissor) {
282 pass_bindings_.SetScissor(scissor);
283}
284
285// |RenderPass|
286void RenderPassMTL::SetElementCount(size_t count) {
287 vertex_count_ = count;
288}
289
290// |RenderPass|
291void RenderPassMTL::SetInstanceCount(size_t count) {
292 instance_count_ = count;
293}
294
295// |RenderPass|
296bool 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|
314bool 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|
329fml::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|
384bool RenderPassMTL::BindResource(ShaderStage stage,
385 DescriptorType type,
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|
393bool RenderPassMTL::BindDynamicResource(
394 ShaderStage stage,
395 DescriptorType type,
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|
403bool RenderPassMTL::BindResource(ShaderStage stage,
404 DescriptorType type,
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
415bool RenderPassMTL::BindDynamicResource(
416 ShaderStage stage,
417 DescriptorType type,
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)
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
id< MTLTexture > GetMTLTexture() const
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
FlView * view
uint32_t * target
FlTexture * texture
static bool ConfigureColorAttachment(const ColorAttachment &desc, MTLRenderPassColorAttachmentDescriptor *attachment)
static bool ConfigureDepthAttachment(const DepthAttachment &desc, MTLRenderPassDepthAttachmentDescriptor *attachment)
constexpr MTLLoadAction ToMTLLoadAction(LoadAction action)
static MTLRenderPassDescriptor * ToMTLRenderPassDescriptor(const RenderTarget &desc)
static bool ConfigureResolveTextureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type)
static bool ConfigureStencilAttachment(const StencilAttachment &desc, MTLRenderPassStencilAttachmentDescriptor *attachment)
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
constexpr MTLIndexType ToMTLIndexType(IndexType type)
constexpr MTLStoreAction ToMTLStoreAction(StoreAction action)
MTLClearColor ToMTLClearColor(const Color &color)
static bool ConfigureAttachment(const Attachment &desc, MTLRenderPassAttachmentDescriptor *attachment)
Definition ref_ptr.h:261
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
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.
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
#define VALIDATION_LOG
Definition validation.h:91