7 #include "flutter/fml/closure.h"
8 #include "flutter/fml/logging.h"
9 #include "flutter/fml/make_copyable.h"
10 #include "flutter/fml/trace_event.h"
28 MTLRenderPassAttachmentDescriptor* attachment) {
34 VALIDATION_LOG <<
"Resolve store action specified on attachment but no "
35 "resolve texture was specified.";
40 VALIDATION_LOG <<
"A resolve texture was specified even though the store "
41 "action doesn't require it.";
49 attachment.resolveTexture =
56 MTLRenderPassAttachmentDescriptor* attachment) {
74 MTLRenderPassColorAttachmentDescriptor* attachment) {
84 MTLRenderPassDepthAttachmentDescriptor* attachment) {
94 MTLRenderPassStencilAttachmentDescriptor* attachment) {
105 auto result = [MTLRenderPassDescriptor renderPassDescriptor];
109 for (
const auto& color : colors) {
111 result.colorAttachments[color.first])) {
112 VALIDATION_LOG <<
"Could not configure color attachment at index "
120 if (depth.has_value() &&
128 if (stencil.has_value() &&
137 RenderPassMTL::RenderPassMTL(std::weak_ptr<const Context> context,
138 const RenderTarget& target,
139 id<MTLCommandBuffer> buffer)
140 : RenderPass(
std::move(context), target),
143 if (!buffer_ || !desc_ || !render_target_.IsValid()) {
149 RenderPassMTL::~RenderPassMTL() =
default;
151 bool RenderPassMTL::IsValid()
const {
155 void RenderPassMTL::OnSetLabel(std::string label) {
159 label_ = std::move(label);
162 bool RenderPassMTL::OnEncodeCommands(
const Context& context)
const {
163 TRACE_EVENT0(
"impeller",
"RenderPassMTL::EncodeCommands");
167 auto render_command_encoder =
168 [buffer_ renderCommandEncoderWithDescriptor:desc_];
170 if (!render_command_encoder) {
174 if (!label_.empty()) {
175 [render_command_encoder setLabel:@(label_.c_str())];
180 fml::ScopedCleanupClosure auto_end(
181 [render_command_encoder]() { [render_command_encoder endEncoding]; });
183 return EncodeCommands(context.GetResourceAllocator(), render_command_encoder);
197 : encoder_(encoder) {}
204 if (pipeline == pipeline_) {
207 pipeline_ = pipeline;
208 [encoder_ setRenderPipelineState:pipeline_];
212 if (depth_stencil_ == depth_stencil) {
215 depth_stencil_ = depth_stencil;
216 [encoder_ setDepthStencilState:depth_stencil_];
222 id<MTLBuffer> buffer) {
223 auto& buffers_map = buffers_[stage];
224 auto found = buffers_map.find(index);
225 if (found != buffers_map.end() && found->second.buffer == buffer) {
227 if (found->second.offset == offset) {
233 found->second.offset = offset;
236 case ShaderStage::kVertex:
237 [encoder_ setVertexBufferOffset:offset atIndex:index];
239 case ShaderStage::kFragment:
240 [encoder_ setFragmentBufferOffset:offset atIndex:index];
243 VALIDATION_LOG <<
"Cannot update buffer offset of an unknown stage.";
248 buffers_map[index] = {buffer,
static_cast<size_t>(offset)};
250 case ShaderStage::kVertex:
251 [encoder_ setVertexBuffer:buffer offset:offset atIndex:index];
253 case ShaderStage::kFragment:
254 [encoder_ setFragmentBuffer:buffer offset:offset atIndex:index];
264 auto& texture_map = textures_[stage];
265 auto found = texture_map.find(index);
266 if (found != texture_map.end() && found->second == texture) {
270 texture_map[index] = texture;
272 case ShaderStage::kVertex:
273 [encoder_ setVertexTexture:texture atIndex:index];
275 case ShaderStage::kFragment:
276 [encoder_ setFragmentTexture:texture atIndex:index];
287 id<MTLSamplerState> sampler) {
288 auto& sampler_map = samplers_[stage];
289 auto found = sampler_map.find(index);
290 if (found != sampler_map.end() && found->second == sampler) {
294 sampler_map[index] = sampler;
296 case ShaderStage::kVertex:
297 [encoder_ setVertexSamplerState:sampler atIndex:index];
299 case ShaderStage::kFragment:
300 [encoder_ setFragmentSamplerState:sampler atIndex:index];
310 if (viewport_.has_value() && viewport_.value() == viewport) {
313 [encoder_ setViewport:MTLViewport{
321 viewport_ = viewport;
325 if (scissor_.has_value() && scissor_.value() == scissor) {
329 setScissorRect:MTLScissorRect{
330 .x =
static_cast<NSUInteger
>(scissor.
GetX()),
331 .y =
static_cast<NSUInteger
>(scissor.
GetY()),
332 .width =
static_cast<NSUInteger
>(scissor.
GetWidth()),
334 static_cast<NSUInteger
>(scissor.
GetHeight()),
340 struct BufferOffsetPair {
341 id<MTLBuffer> buffer =
nullptr;
344 using BufferMap = std::map<uint64_t, BufferOffsetPair>;
345 using TextureMap = std::map<uint64_t, id<MTLTexture>>;
346 using SamplerMap = std::map<uint64_t, id<MTLSamplerState>>;
348 const id<MTLRenderCommandEncoder> encoder_;
349 id<MTLRenderPipelineState> pipeline_ =
nullptr;
350 id<MTLDepthStencilState> depth_stencil_ =
nullptr;
351 std::map<ShaderStage, BufferMap> buffers_;
352 std::map<ShaderStage, TextureMap> textures_;
353 std::map<ShaderStage, SamplerMap> samplers_;
354 std::optional<Viewport> viewport_;
355 std::optional<IRect> scissor_;
367 auto device_buffer = view.
buffer->GetDeviceBuffer(allocator);
368 if (!device_buffer) {
372 auto buffer = DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer();
394 <<
"Texture at binding index " << bind_index
395 <<
" has a mip count > 1, but the mipmap has not been generated.";
397 #endif // !FML_OS_IOS
401 TextureMTL::Cast(texture).GetMTLTexture()) &&
403 SamplerMTL::Cast(sampler).GetMTLSamplerState());
406 bool RenderPassMTL::EncodeCommands(
const std::shared_ptr<Allocator>& allocator,
407 id<MTLRenderCommandEncoder> encoder)
const {
408 PassBindingsCache pass_bindings(encoder);
409 auto bind_stage_resources = [&allocator, &pass_bindings](
410 const Bindings& bindings,
412 for (
const BufferAndUniformSlot& buffer : bindings.buffers) {
413 if (!
Bind(pass_bindings, *allocator, stage, buffer.slot.ext_res_0,
414 buffer.view.resource)) {
418 for (
const TextureAndSampler& data : bindings.sampled_images) {
419 if (!
Bind(pass_bindings, stage, data.slot.texture_index, *data.sampler,
420 *data.texture.resource)) {
427 const auto target_sample_count = render_target_.GetSampleCount();
429 fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; };
430 for (
const auto& command : commands_) {
431 #ifdef IMPELLER_DEBUG
432 fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker);
433 if (!command.label.empty()) {
434 [encoder pushDebugGroup:@(command.label.c_str())];
436 auto_pop_debug_marker.Release();
438 #endif // IMPELLER_DEBUG
440 const auto& pipeline_desc = command.pipeline->GetDescriptor();
441 if (target_sample_count != pipeline_desc.GetSampleCount()) {
442 VALIDATION_LOG <<
"Pipeline for command and the render target disagree "
443 "on sample counts (target was "
444 <<
static_cast<uint64_t
>(target_sample_count)
445 <<
" but pipeline wanted "
446 <<
static_cast<uint64_t
>(pipeline_desc.GetSampleCount())
451 pass_bindings.SetRenderPipelineState(
452 PipelineMTL::Cast(*command.pipeline).GetMTLRenderPipelineState());
453 pass_bindings.SetDepthStencilState(
454 PipelineMTL::Cast(*command.pipeline).GetMTLDepthStencilState());
455 pass_bindings.SetViewport(command.viewport.value_or<Viewport>(
456 {.rect = Rect::MakeSize(GetRenderTargetSize())}));
457 pass_bindings.SetScissor(
458 command.scissor.value_or(IRect::MakeSize(GetRenderTargetSize())));
460 [encoder setFrontFacingWinding:pipeline_desc.GetWindingOrder() ==
461 WindingOrder::kClockwise
462 ? MTLWindingClockwise
463 : MTLWindingCounterClockwise];
464 [encoder setCullMode:
ToMTLCullMode(pipeline_desc.GetCullMode())];
466 pipeline_desc.GetPolygonMode())];
467 [encoder setStencilReferenceValue:command.stencil_reference];
469 if (!
Bind(pass_bindings, *allocator, ShaderStage::kVertex,
470 VertexDescriptor::kReservedVertexBufferIndex,
471 command.vertex_buffer.vertex_buffer)) {
475 if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) {
478 if (!bind_stage_resources(command.fragment_bindings,
479 ShaderStage::kFragment)) {
483 const PrimitiveType primitive_type = pipeline_desc.GetPrimitiveType();
484 if (command.vertex_buffer.index_type == IndexType::kNone) {
485 if (command.instance_count != 1u) {
486 #if TARGET_OS_SIMULATOR
487 VALIDATION_LOG <<
"iOS Simulator does not support instanced rendering.";
489 #else // TARGET_OS_SIMULATOR
491 vertexStart:command.base_vertex
492 vertexCount:command.vertex_buffer.vertex_count
493 instanceCount:command.instance_count
495 #endif // TARGET_OS_SIMULATOR
498 vertexStart:command.base_vertex
499 vertexCount:command.vertex_buffer.vertex_count];
504 if (command.vertex_buffer.index_type == IndexType::kUnknown) {
507 auto index_buffer = command.vertex_buffer.index_buffer.buffer;
511 auto device_buffer = index_buffer->GetDeviceBuffer(*allocator);
512 if (!device_buffer) {
515 auto mtl_index_buffer =
516 DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer();
517 if (!mtl_index_buffer) {
522 command.vertex_buffer.vertex_count *
523 (command.vertex_buffer.index_type == IndexType::k16bit ? 2 : 4) ==
524 command.vertex_buffer.index_buffer.range.length);
526 if (command.instance_count != 1u) {
527 #if TARGET_OS_SIMULATOR
528 VALIDATION_LOG <<
"iOS Simulator does not support instanced rendering.";
530 #else // TARGET_OS_SIMULATOR
533 indexCount:command.vertex_buffer.vertex_count
535 indexBuffer:mtl_index_buffer
536 indexBufferOffset:command.vertex_buffer.index_buffer.range.offset
537 instanceCount:command.instance_count
538 baseVertex:command.base_vertex
540 #endif // TARGET_OS_SIMULATOR
544 indexCount:command.vertex_buffer.vertex_count
546 indexBuffer:mtl_index_buffer
547 indexBufferOffset:command.vertex_buffer.index_buffer.range