Flutter Impeller
pipeline_library_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 <Foundation/Foundation.h>
8 #include <Metal/Metal.h>
9 
10 #include "flutter/fml/build_config.h"
11 #include "flutter/fml/container.h"
12 #include "impeller/base/promise.h"
18 
19 #if !__has_feature(objc_arc)
20 #error ARC must be enabled !
21 #endif
22 
23 namespace impeller {
24 
25 PipelineLibraryMTL::PipelineLibraryMTL(id<MTLDevice> device)
26  : device_(device) {}
27 
28 PipelineLibraryMTL::~PipelineLibraryMTL() = default;
29 
30 using Callback = std::function<void(MTLRenderPipelineDescriptor*)>;
31 
33  const Callback& callback) {
34  auto descriptor = [[MTLRenderPipelineDescriptor alloc] init];
35  descriptor.label = @(desc.GetLabel().data());
36  descriptor.rasterSampleCount = static_cast<NSUInteger>(desc.GetSampleCount());
37  bool created_specialized_function = false;
38 
39  if (const auto& vertex_descriptor = desc.GetVertexDescriptor()) {
40  VertexDescriptorMTL vertex_descriptor_mtl;
41  if (vertex_descriptor_mtl.SetStageInputsAndLayout(
42  vertex_descriptor->GetStageInputs(),
43  vertex_descriptor->GetStageLayouts())) {
44  descriptor.vertexDescriptor =
45  vertex_descriptor_mtl.GetMTLVertexDescriptor();
46  }
47  }
48 
49  for (const auto& item : desc.GetColorAttachmentDescriptors()) {
50  descriptor.colorAttachments[item.first] =
52  }
53 
54  descriptor.depthAttachmentPixelFormat =
56  descriptor.stencilAttachmentPixelFormat =
58 
59  const auto& constants = desc.GetSpecializationConstants();
60  for (const auto& entry : desc.GetStageEntrypoints()) {
61  if (entry.first == ShaderStage::kVertex) {
62  descriptor.vertexFunction =
63  ShaderFunctionMTL::Cast(*entry.second).GetMTLFunction();
64  }
65  if (entry.first == ShaderStage::kFragment) {
66  if (constants.empty()) {
67  descriptor.fragmentFunction =
68  ShaderFunctionMTL::Cast(*entry.second).GetMTLFunction();
69  } else {
70  // This code only expects a single specialized function per pipeline.
71  FML_CHECK(!created_specialized_function);
72  created_specialized_function = true;
73  ShaderFunctionMTL::Cast(*entry.second)
74  .GetMTLFunctionSpecialized(
75  constants, [callback, descriptor](id<MTLFunction> function) {
76  descriptor.fragmentFunction = function;
77  callback(descriptor);
78  });
79  }
80  }
81  }
82 
83  if (!created_specialized_function) {
84  callback(descriptor);
85  }
86 }
87 
88 static MTLComputePipelineDescriptor* GetMTLComputePipelineDescriptor(
89  const ComputePipelineDescriptor& desc) {
90  auto descriptor = [[MTLComputePipelineDescriptor alloc] init];
91  descriptor.label = @(desc.GetLabel().c_str());
92  descriptor.computeFunction =
93  ShaderFunctionMTL::Cast(*desc.GetStageEntrypoint()).GetMTLFunction();
94  return descriptor;
95 }
96 
97 static id<MTLDepthStencilState> CreateDepthStencilDescriptor(
98  const PipelineDescriptor& desc,
99  id<MTLDevice> device) {
100  auto descriptor = ToMTLDepthStencilDescriptor(
104  );
105  return [device newDepthStencilStateWithDescriptor:descriptor];
106 }
107 
108 // |PipelineLibrary|
109 bool PipelineLibraryMTL::IsValid() const {
110  return device_ != nullptr;
111 }
112 
113 // |PipelineLibrary|
114 PipelineFuture<PipelineDescriptor> PipelineLibraryMTL::GetPipeline(
115  PipelineDescriptor descriptor,
116  bool async,
117  bool threadsafe) {
118  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
119  return found->second;
120  }
121 
122  if (!IsValid()) {
123  return {
124  descriptor,
125  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
126  }
127 
128  auto promise = std::make_shared<
129  std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
130  auto pipeline_future =
131  PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
132  pipelines_[descriptor] = pipeline_future;
133  auto weak_this = weak_from_this();
134 
135  auto get_pipeline_descriptor =
136  [descriptor,
137  device = device_](MTLNewRenderPipelineStateCompletionHandler handler) {
139  descriptor,
140  [device, handler](MTLRenderPipelineDescriptor* descriptor) {
141  [device newRenderPipelineStateWithDescriptor:descriptor
142  completionHandler:handler];
143  });
144  };
145 
146  // Extra info for https://github.com/flutter/flutter/issues/148320.
147  std::optional<std::string> thread_name =
148 #if FLUTTER_RELEASE
149  std::nullopt;
150 #else
151  [NSThread isMainThread] ? "main"
152  : [[[NSThread currentThread] name] UTF8String];
153 #endif
154  auto completion_handler = ^(
155  id<MTLRenderPipelineState> _Nullable render_pipeline_state,
156  NSError* _Nullable error) {
157  if (error != nil) {
158  VALIDATION_LOG << "Could not create render pipeline for "
159  << descriptor.GetLabel() << " :"
160  << error.localizedDescription.UTF8String << " (thread: "
161  << (thread_name.has_value() ? *thread_name : "unknown")
162  << ")";
163  promise->set_value(nullptr);
164  return;
165  }
166 
167  auto strong_this = weak_this.lock();
168  if (!strong_this) {
169  promise->set_value(nullptr);
170  return;
171  }
172 
173  auto new_pipeline = std::shared_ptr<PipelineMTL>(new PipelineMTL(
174  weak_this,
175  descriptor, //
176  render_pipeline_state, //
177  CreateDepthStencilDescriptor(descriptor, device_) //
178  ));
179  promise->set_value(new_pipeline);
180  };
181  auto retry_handler =
182  ^(id<MTLRenderPipelineState> _Nullable render_pipeline_state,
183  NSError* _Nullable error) {
184  if (error) {
185  FML_LOG(INFO) << "pipeline creation retry";
186  // The dispatch here is just to minimize the number of threads calling
187  // this. Executing on the platform thread matches the ContentContext
188  // path. It also serializes the retries. It may not be necessary.
189  dispatch_async(dispatch_get_main_queue(), ^{
190  get_pipeline_descriptor(completion_handler);
191  });
192  } else {
193  completion_handler(render_pipeline_state, error);
194  }
195  };
196 #if defined(FML_ARCH_CPU_X86_64)
197  get_pipeline_descriptor(retry_handler);
198 #else
199  get_pipeline_descriptor(completion_handler);
200  (void)retry_handler;
201 #endif
202  return pipeline_future;
203 }
204 
205 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryMTL::GetPipeline(
206  ComputePipelineDescriptor descriptor,
207  bool async) {
208  if (auto found = compute_pipelines_.find(descriptor);
209  found != compute_pipelines_.end()) {
210  return found->second;
211  }
212 
213  if (!IsValid()) {
214  return {
215  descriptor,
216  RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
217  nullptr)};
218  }
219 
220  auto promise = std::make_shared<
221  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
222  auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
223  descriptor, promise->get_future()};
224  compute_pipelines_[descriptor] = pipeline_future;
225  auto weak_this = weak_from_this();
226 
227  auto completion_handler =
228  ^(id<MTLComputePipelineState> _Nullable compute_pipeline_state,
229  MTLComputePipelineReflection* _Nullable reflection,
230  NSError* _Nullable error) {
231  if (error != nil) {
232  VALIDATION_LOG << "Could not create compute pipeline: "
233  << error.localizedDescription.UTF8String;
234  promise->set_value(nullptr);
235  return;
236  }
237 
238  auto strong_this = weak_this.lock();
239  if (!strong_this) {
240  VALIDATION_LOG << "Library was collected before a pending pipeline "
241  "creation could finish.";
242  promise->set_value(nullptr);
243  return;
244  }
245 
246  auto new_pipeline = std::shared_ptr<ComputePipelineMTL>(
247  new ComputePipelineMTL(weak_this,
248  descriptor, //
249  compute_pipeline_state //
250  ));
251  promise->set_value(new_pipeline);
252  };
253  [device_
254  newComputePipelineStateWithDescriptor:GetMTLComputePipelineDescriptor(
255  descriptor)
256  options:MTLPipelineOptionNone
257  completionHandler:completion_handler];
258  return pipeline_future;
259 }
260 
261 // |PipelineLibrary|
262 bool PipelineLibraryMTL::HasPipeline(const PipelineDescriptor& descriptor) {
263  return pipelines_.find(descriptor) != pipelines_.end();
264 }
265 
266 // |PipelineLibrary|
267 void PipelineLibraryMTL::RemovePipelinesWithEntryPoint(
268  std::shared_ptr<const ShaderFunction> function) {
269  fml::erase_if(pipelines_, [&](auto item) {
270  return item->first.GetEntrypointForStage(function->GetStage())
271  ->IsEqual(*function);
272  });
273 }
274 
275 } // namespace impeller
std::shared_ptr< const ShaderFunction > GetStageEntrypoint() const
std::string_view GetLabel() const
PixelFormat GetDepthPixelFormat() const
std::optional< DepthAttachmentDescriptor > GetDepthStencilAttachmentDescriptor() const
const std::map< ShaderStage, std::shared_ptr< const ShaderFunction > > & GetStageEntrypoints() const
PixelFormat GetStencilPixelFormat() const
const std::vector< Scalar > & GetSpecializationConstants() const
const std::map< size_t, ColorAttachmentDescriptor > & GetColorAttachmentDescriptors() const
std::optional< StencilAttachmentDescriptor > GetBackStencilAttachmentDescriptor() const
const std::shared_ptr< VertexDescriptor > & GetVertexDescriptor() const
std::optional< StencilAttachmentDescriptor > GetFrontStencilAttachmentDescriptor() const
SampleCount GetSampleCount() const
bool SetStageInputsAndLayout(const std::vector< ShaderStageIOSlot > &inputs, const std::vector< ShaderStageBufferLayout > &layouts)
MTLVertexDescriptor * GetMTLVertexDescriptor() const
std::vector< std::pair< uint64_t, std::unique_ptr< GenericRenderPipelineHandle > > > pipelines_
static MTLComputePipelineDescriptor * GetMTLComputePipelineDescriptor(const ComputePipelineDescriptor &desc)
static void GetMTLRenderPipelineDescriptor(const PipelineDescriptor &desc, const Callback &callback)
std::function< void(MTLRenderPipelineDescriptor *)> Callback
MTLDepthStencilDescriptor * ToMTLDepthStencilDescriptor(std::optional< DepthAttachmentDescriptor > depth, std::optional< StencilAttachmentDescriptor > front, std::optional< StencilAttachmentDescriptor > back)
Definition: formats_mtl.mm:54
constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format)
Definition: formats_mtl.h:76
MTLRenderPipelineColorAttachmentDescriptor * ToMTLRenderPipelineColorAttachmentDescriptor(ColorAttachmentDescriptor descriptor)
Definition: formats_mtl.mm:15
static id< MTLDepthStencilState > CreateDepthStencilDescriptor(const PipelineDescriptor &desc, id< MTLDevice > device)
#define VALIDATION_LOG
Definition: validation.h:91