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  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
118  return found->second;
119  }
120 
121  if (!IsValid()) {
122  return {
123  descriptor,
124  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
125  }
126 
127  auto promise = std::make_shared<
128  std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
129  auto pipeline_future =
130  PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
131  pipelines_[descriptor] = pipeline_future;
132  auto weak_this = weak_from_this();
133 
134  auto get_pipeline_descriptor =
135  [descriptor,
136  device = device_](MTLNewRenderPipelineStateCompletionHandler handler) {
138  descriptor,
139  [device, handler](MTLRenderPipelineDescriptor* descriptor) {
140  [device newRenderPipelineStateWithDescriptor:descriptor
141  completionHandler:handler];
142  });
143  };
144 
145  // Extra info for https://github.com/flutter/flutter/issues/148320.
146  std::optional<std::string> thread_name =
147 #if FLUTTER_RELEASE
148  std::nullopt;
149 #else
150  [NSThread isMainThread] ? "main"
151  : [[[NSThread currentThread] name] UTF8String];
152 #endif
153  auto completion_handler = ^(
154  id<MTLRenderPipelineState> _Nullable render_pipeline_state,
155  NSError* _Nullable error) {
156  if (error != nil) {
157  VALIDATION_LOG << "Could not create render pipeline for "
158  << descriptor.GetLabel() << " :"
159  << error.localizedDescription.UTF8String << " (thread: "
160  << (thread_name.has_value() ? *thread_name : "unknown")
161  << ")";
162  promise->set_value(nullptr);
163  return;
164  }
165 
166  auto strong_this = weak_this.lock();
167  if (!strong_this) {
168  promise->set_value(nullptr);
169  return;
170  }
171 
172  auto new_pipeline = std::shared_ptr<PipelineMTL>(new PipelineMTL(
173  weak_this,
174  descriptor, //
175  render_pipeline_state, //
176  CreateDepthStencilDescriptor(descriptor, device_) //
177  ));
178  promise->set_value(new_pipeline);
179  };
180  auto retry_handler =
181  ^(id<MTLRenderPipelineState> _Nullable render_pipeline_state,
182  NSError* _Nullable error) {
183  if (error) {
184  FML_LOG(INFO) << "pipeline creation retry";
185  // The dispatch here is just to minimize the number of threads calling
186  // this. Executing on the platform thread matches the ContentContext
187  // path. It also serializes the retries. It may not be necessary.
188  dispatch_async(dispatch_get_main_queue(), ^{
189  get_pipeline_descriptor(completion_handler);
190  });
191  } else {
192  completion_handler(render_pipeline_state, error);
193  }
194  };
195 #if defined(FML_ARCH_CPU_X86_64)
196  get_pipeline_descriptor(retry_handler);
197 #else
198  get_pipeline_descriptor(completion_handler);
199  (void)retry_handler;
200 #endif
201  return pipeline_future;
202 }
203 
204 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryMTL::GetPipeline(
205  ComputePipelineDescriptor descriptor,
206  bool async) {
207  if (auto found = compute_pipelines_.find(descriptor);
208  found != compute_pipelines_.end()) {
209  return found->second;
210  }
211 
212  if (!IsValid()) {
213  return {
214  descriptor,
215  RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
216  nullptr)};
217  }
218 
219  auto promise = std::make_shared<
220  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
221  auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
222  descriptor, promise->get_future()};
223  compute_pipelines_[descriptor] = pipeline_future;
224  auto weak_this = weak_from_this();
225 
226  auto completion_handler =
227  ^(id<MTLComputePipelineState> _Nullable compute_pipeline_state,
228  MTLComputePipelineReflection* _Nullable reflection,
229  NSError* _Nullable error) {
230  if (error != nil) {
231  VALIDATION_LOG << "Could not create compute pipeline: "
232  << error.localizedDescription.UTF8String;
233  promise->set_value(nullptr);
234  return;
235  }
236 
237  auto strong_this = weak_this.lock();
238  if (!strong_this) {
239  VALIDATION_LOG << "Library was collected before a pending pipeline "
240  "creation could finish.";
241  promise->set_value(nullptr);
242  return;
243  }
244 
245  auto new_pipeline = std::shared_ptr<ComputePipelineMTL>(
246  new ComputePipelineMTL(weak_this,
247  descriptor, //
248  compute_pipeline_state //
249  ));
250  promise->set_value(new_pipeline);
251  };
252  [device_
253  newComputePipelineStateWithDescriptor:GetMTLComputePipelineDescriptor(
254  descriptor)
255  options:MTLPipelineOptionNone
256  completionHandler:completion_handler];
257  return pipeline_future;
258 }
259 
260 // |PipelineLibrary|
261 bool PipelineLibraryMTL::HasPipeline(const PipelineDescriptor& descriptor) {
262  return pipelines_.find(descriptor) != pipelines_.end();
263 }
264 
265 // |PipelineLibrary|
266 void PipelineLibraryMTL::RemovePipelinesWithEntryPoint(
267  std::shared_ptr<const ShaderFunction> function) {
268  fml::erase_if(pipelines_, [&](auto item) {
269  return item->first.GetEntrypointForStage(function->GetStage())
270  ->IsEqual(*function);
271  });
272 }
273 
274 } // 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