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"
27 MTLRenderPassAttachmentDescriptor* attachment) {
33 VALIDATION_LOG <<
"Resolve store action specified on attachment but no "
34 "resolve texture was specified.";
39 VALIDATION_LOG <<
"A resolve texture was specified even though the store "
40 "action doesn't require it.";
48 attachment.resolveTexture =
55 MTLRenderPassAttachmentDescriptor* attachment) {
73 MTLRenderPassColorAttachmentDescriptor* attachment) {
83 MTLRenderPassDepthAttachmentDescriptor* attachment) {
93 MTLRenderPassStencilAttachmentDescriptor* attachment) {
104 auto result = [MTLRenderPassDescriptor renderPassDescriptor];
108 for (
const auto& color : colors) {
110 result.colorAttachments[color.first])) {
111 VALIDATION_LOG <<
"Could not configure color attachment at index "
119 if (depth.has_value() &&
127 if (stencil.has_value() &&
136 RenderPassMTL::RenderPassMTL(std::weak_ptr<const Context> context,
137 const RenderTarget& target,
138 id<MTLCommandBuffer> buffer)
139 : RenderPass(
std::move(context), target),
142 if (!buffer_ || !desc_ || !render_target_.IsValid()) {
148 RenderPassMTL::~RenderPassMTL() =
default;
150 bool RenderPassMTL::IsValid()
const {
154 void RenderPassMTL::OnSetLabel(std::string label) {
158 label_ = std::move(label);
161 bool RenderPassMTL::OnEncodeCommands(
const Context& context)
const {
162 TRACE_EVENT0(
"impeller",
"RenderPassMTL::EncodeCommands");
166 auto render_command_encoder =
167 [buffer_ renderCommandEncoderWithDescriptor:desc_];
169 if (!render_command_encoder) {
173 if (!label_.empty()) {
174 [render_command_encoder setLabel:@(label_.c_str())];
179 fml::ScopedCleanupClosure auto_end(
180 [render_command_encoder]() { [render_command_encoder endEncoding]; });
182 return EncodeCommands(context.GetResourceAllocator(), render_command_encoder);
196 : encoder_(encoder) {}
203 if (pipeline == pipeline_) {
206 pipeline_ = pipeline;
207 [encoder_ setRenderPipelineState:pipeline_];
211 if (depth_stencil_ == depth_stencil) {
214 depth_stencil_ = depth_stencil;
215 [encoder_ setDepthStencilState:depth_stencil_];
221 id<MTLBuffer> buffer) {
222 auto& buffers_map = buffers_[stage];
223 auto found = buffers_map.find(index);
224 if (found != buffers_map.end() && found->second.buffer == buffer) {
226 if (found->second.offset == offset) {
232 found->second.offset = offset;
235 case ShaderStage::kVertex:
236 [encoder_ setVertexBufferOffset:offset atIndex:index];
238 case ShaderStage::kFragment:
239 [encoder_ setFragmentBufferOffset:offset atIndex:index];
242 VALIDATION_LOG <<
"Cannot update buffer offset of an unknown stage.";
247 buffers_map[index] = {buffer,
static_cast<size_t>(offset)};
249 case ShaderStage::kVertex:
250 [encoder_ setVertexBuffer:buffer offset:offset atIndex:index];
252 case ShaderStage::kFragment:
253 [encoder_ setFragmentBuffer:buffer offset:offset atIndex:index];
263 auto& texture_map = textures_[stage];
264 auto found = texture_map.find(index);
265 if (found != texture_map.end() && found->second == texture) {
269 texture_map[index] = texture;
271 case ShaderStage::kVertex:
272 [encoder_ setVertexTexture:texture atIndex:index];
274 case ShaderStage::kFragment:
275 [encoder_ setFragmentTexture:texture atIndex:index];
286 id<MTLSamplerState> sampler) {
287 auto& sampler_map = samplers_[stage];
288 auto found = sampler_map.find(index);
289 if (found != sampler_map.end() && found->second == sampler) {
293 sampler_map[index] = sampler;
295 case ShaderStage::kVertex:
296 [encoder_ setVertexSamplerState:sampler atIndex:index];
298 case ShaderStage::kFragment:
299 [encoder_ setFragmentSamplerState:sampler atIndex:index];
309 if (viewport_.has_value() && viewport_.value() == viewport) {
312 [encoder_ setViewport:MTLViewport{
320 viewport_ = viewport;
324 if (scissor_.has_value() && scissor_.value() == scissor) {
328 setScissorRect:MTLScissorRect{
329 .x =
static_cast<NSUInteger
>(scissor.
origin.
x),
330 .y =
static_cast<NSUInteger
>(scissor.
origin.
y),
331 .width =
static_cast<NSUInteger
>(scissor.
size.
width),
339 struct BufferOffsetPair {
340 id<MTLBuffer> buffer =
nullptr;
343 using BufferMap = std::map<uint64_t, BufferOffsetPair>;
344 using TextureMap = std::map<uint64_t, id<MTLTexture>>;
345 using SamplerMap = std::map<uint64_t, id<MTLSamplerState>>;
347 const id<MTLRenderCommandEncoder> encoder_;
348 id<MTLRenderPipelineState> pipeline_ =
nullptr;
349 id<MTLDepthStencilState> depth_stencil_ =
nullptr;
350 std::map<ShaderStage, BufferMap> buffers_;
351 std::map<ShaderStage, TextureMap> textures_;
352 std::map<ShaderStage, SamplerMap> samplers_;
353 std::optional<Viewport> viewport_;
354 std::optional<IRect> scissor_;
366 auto device_buffer = view.
buffer->GetDeviceBuffer(allocator);
367 if (!device_buffer) {
371 auto buffer = DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer();
393 <<
"Texture at binding index " << bind_index
394 <<
" has a mip count > 1, but the mipmap has not been generated.";
396 #endif // !FML_OS_IOS
400 TextureMTL::Cast(texture).GetMTLTexture()) &&
402 SamplerMTL::Cast(sampler).GetMTLSamplerState());
405 bool RenderPassMTL::EncodeCommands(
const std::shared_ptr<Allocator>& allocator,
406 id<MTLRenderCommandEncoder> encoder)
const {
407 PassBindingsCache pass_bindings(encoder);
408 auto bind_stage_resources = [&allocator, &pass_bindings](
409 const Bindings& bindings,
411 if (stage == ShaderStage::kVertex) {
412 if (!
Bind(pass_bindings, *allocator, stage,
413 VertexDescriptor::kReservedVertexBufferIndex,
414 bindings.vertex_buffer.view.resource)) {
418 for (
const auto& buffer : bindings.buffers) {
419 if (!
Bind(pass_bindings, *allocator, stage, buffer.first,
420 buffer.second.view.resource)) {
424 for (
const auto& data : bindings.sampled_images) {
425 if (!
Bind(pass_bindings, stage, data.first, *data.second.sampler.resource,
426 *data.second.texture.resource)) {
433 const auto target_sample_count = render_target_.GetSampleCount();
435 fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; };
436 for (
const auto& command : commands_) {
437 if (command.vertex_count == 0u) {
440 if (command.instance_count == 0u) {
444 #ifdef IMPELLER_DEBUG
445 fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker);
446 if (!command.label.empty()) {
447 [encoder pushDebugGroup:@(command.label.c_str())];
449 auto_pop_debug_marker.Release();
451 #endif // IMPELLER_DEBUG
453 const auto& pipeline_desc = command.pipeline->GetDescriptor();
454 if (target_sample_count != pipeline_desc.GetSampleCount()) {
455 VALIDATION_LOG <<
"Pipeline for command and the render target disagree "
456 "on sample counts (target was "
457 <<
static_cast<uint64_t
>(target_sample_count)
458 <<
" but pipeline wanted "
459 <<
static_cast<uint64_t
>(pipeline_desc.GetSampleCount())
464 pass_bindings.SetRenderPipelineState(
465 PipelineMTL::Cast(*command.pipeline).GetMTLRenderPipelineState());
466 pass_bindings.SetDepthStencilState(
467 PipelineMTL::Cast(*command.pipeline).GetMTLDepthStencilState());
468 pass_bindings.SetViewport(command.viewport.value_or<Viewport>(
469 {.rect = Rect::MakeSize(GetRenderTargetSize())}));
470 pass_bindings.SetScissor(
471 command.scissor.value_or(IRect::MakeSize(GetRenderTargetSize())));
473 [encoder setFrontFacingWinding:pipeline_desc.GetWindingOrder() ==
474 WindingOrder::kClockwise
475 ? MTLWindingClockwise
476 : MTLWindingCounterClockwise];
477 [encoder setCullMode:
ToMTLCullMode(pipeline_desc.GetCullMode())];
479 pipeline_desc.GetPolygonMode())];
480 [encoder setStencilReferenceValue:command.stencil_reference];
482 if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) {
485 if (!bind_stage_resources(command.fragment_bindings,
486 ShaderStage::kFragment)) {
490 const PrimitiveType primitive_type = pipeline_desc.GetPrimitiveType();
491 if (command.index_type == IndexType::kNone) {
492 if (command.instance_count != 1u) {
493 #if TARGET_OS_SIMULATOR
494 VALIDATION_LOG <<
"iOS Simulator does not support instanced rendering.";
496 #else // TARGET_OS_SIMULATOR
498 vertexStart:command.base_vertex
499 vertexCount:command.vertex_count
500 instanceCount:command.instance_count
502 #endif // TARGET_OS_SIMULATOR
505 vertexStart:command.base_vertex
506 vertexCount:command.vertex_count];
511 if (command.index_type == IndexType::kUnknown) {
514 auto index_buffer = command.index_buffer.buffer;
518 auto device_buffer = index_buffer->GetDeviceBuffer(*allocator);
519 if (!device_buffer) {
522 auto mtl_index_buffer =
523 DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer();
524 if (!mtl_index_buffer) {
528 FML_DCHECK(command.vertex_count *
529 (command.index_type == IndexType::k16bit ? 2 : 4) ==
530 command.index_buffer.range.length);
532 if (command.instance_count != 1u) {
533 #if TARGET_OS_SIMULATOR
534 VALIDATION_LOG <<
"iOS Simulator does not support instanced rendering.";
536 #else // TARGET_OS_SIMULATOR
538 indexCount:command.vertex_count
540 indexBuffer:mtl_index_buffer
541 indexBufferOffset:command.index_buffer.range.offset
542 instanceCount:command.instance_count
543 baseVertex:command.base_vertex
545 #endif // TARGET_OS_SIMULATOR
548 indexCount:command.vertex_count
550 indexBuffer:mtl_index_buffer
551 indexBufferOffset:command.index_buffer.range.offset];