| 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
5 #include "impeller/renderer/backend/metal/pipeline_library_mtl.h"
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"
13 #include "impeller/renderer/backend/metal/compute_pipeline_mtl.h"
14 #include "impeller/renderer/backend/metal/formats_mtl.h"
15 #include "impeller/renderer/backend/metal/pipeline_mtl.h"
16 #include "impeller/renderer/backend/metal/shader_function_mtl.h"
17 #include "impeller/renderer/backend/metal/vertex_descriptor_mtl.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
32 static void GetMTLRenderPipelineDescriptor(const PipelineDescriptor& desc,
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] =
51 ToMTLRenderPipelineColorAttachmentDescriptor(item.second);
52 }
53
54 descriptor.depthAttachmentPixelFormat =
55 ToMTLPixelFormat(desc.GetDepthPixelFormat());
56 descriptor.stencilAttachmentPixelFormat =
57 ToMTLPixelFormat(desc.GetStencilPixelFormat());
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(
101 desc.GetDepthStencilAttachmentDescriptor(), //
102 desc.GetFrontStencilAttachmentDescriptor(), //
103 desc.GetBackStencilAttachmentDescriptor() //
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) {
137 GetMTLRenderPipelineDescriptor(
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
impeller::PipelineDescriptor
Definition: pipeline_descriptor.h:24
impeller::ComputePipelineDescriptor
Definition: compute_pipeline_descriptor.h:20
impeller::PipelineDescriptor::GetSpecializationConstants
const std::vector< Scalar > & GetSpecializationConstants() const
Definition: pipeline_descriptor.cc:289
impeller::ComputePipelineDescriptor::GetStageEntrypoint
std::shared_ptr< const ShaderFunction > GetStageEntrypoint() const
Definition: compute_pipeline_descriptor.cc:58
impeller::PipelineDescriptor::GetBackStencilAttachmentDescriptor
std::optional< StencilAttachmentDescriptor > GetBackStencilAttachmentDescriptor() const
Definition: pipeline_descriptor.cc:243
impeller::PipelineDescriptor::GetLabel
std::string_view GetLabel() const
Definition: pipeline_descriptor.cc:234
impeller::GetMTLComputePipelineDescriptor
static MTLComputePipelineDescriptor * GetMTLComputePipelineDescriptor(const ComputePipelineDescriptor &desc)
Definition: pipeline_library_mtl.mm:88
compute_pipeline_mtl.h
formats_mtl.h
impeller::PipelineDescriptor::GetSampleCount
SampleCount GetSampleCount() const
Definition: pipeline_descriptor.h:36
impeller::PipelineDescriptor::GetVertexDescriptor
const std::shared_ptr< VertexDescriptor > & GetVertexDescriptor() const
Definition: pipeline_descriptor.cc:217
impeller::GetMTLRenderPipelineDescriptor
static void GetMTLRenderPipelineDescriptor(const PipelineDescriptor &desc, const Callback &callback)
Definition: pipeline_library_mtl.mm:32
pipeline_mtl.h
impeller::VertexDescriptorMTL::SetStageInputsAndLayout
bool SetStageInputsAndLayout(const std::vector< ShaderStageIOSlot > &inputs, const std::vector< ShaderStageBufferLayout > &layouts)
Definition: vertex_descriptor_mtl.mm:172
impeller::ToMTLDepthStencilDescriptor
MTLDepthStencilDescriptor * ToMTLDepthStencilDescriptor(std::optional< DepthAttachmentDescriptor > depth, std::optional< StencilAttachmentDescriptor > front, std::optional< StencilAttachmentDescriptor > back)
Definition: formats_mtl.mm:54
impeller::ShaderFunctionMTL::GetMTLFunctionSpecialized
void GetMTLFunctionSpecialized(const std::vector< Scalar > &constants, const CompileCallback &callback) const
Definition: shader_function_mtl.mm:20
shader_function_mtl.h
impeller::ToMTLRenderPipelineColorAttachmentDescriptor
MTLRenderPipelineColorAttachmentDescriptor * ToMTLRenderPipelineColorAttachmentDescriptor(ColorAttachmentDescriptor descriptor)
Definition: formats_mtl.mm:15
impeller::ShaderFunctionMTL::GetMTLFunction
id< MTLFunction > GetMTLFunction() const
Definition: shader_function_mtl.mm:42
impeller::ShaderStage::kFragment
@ kFragment
impeller::PipelineDescriptor::GetStencilPixelFormat
PixelFormat GetStencilPixelFormat() const
Definition: pipeline_descriptor.cc:197
impeller::Callback
std::function< void(MTLRenderPipelineDescriptor *)> Callback
Definition: pipeline_library_mtl.mm:30
impeller::ToMTLPixelFormat
constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format)
Definition: formats_mtl.h:76
impeller::ComputePipelineDescriptor::GetLabel
const std::string & GetLabel() const
Definition: compute_pipeline_descriptor.cc:62
impeller::VertexDescriptorMTL::GetMTLVertexDescriptor
MTLVertexDescriptor * GetMTLVertexDescriptor() const
Definition: vertex_descriptor_mtl.mm:206
pipeline_library_mtl.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
impeller::PipelineDescriptor::GetFrontStencilAttachmentDescriptor
std::optional< StencilAttachmentDescriptor > GetFrontStencilAttachmentDescriptor() const
Definition: pipeline_descriptor.cc:202
impeller::PipelineLibraryMTL::PipelineLibraryMTL
PipelineLibraryMTL()
impeller::PipelineDescriptor::GetDepthPixelFormat
PixelFormat GetDepthPixelFormat() const
Definition: pipeline_descriptor.cc:238
impeller::CreateDepthStencilDescriptor
static id< MTLDepthStencilState > CreateDepthStencilDescriptor(const PipelineDescriptor &desc, id< MTLDevice > device)
Definition: pipeline_library_mtl.mm:97
promise.h
impeller::ShaderStage::kVertex
@ kVertex
vertex_descriptor_mtl.h
impeller::PipelineDescriptor::GetColorAttachmentDescriptors
const std::map< size_t, ColorAttachmentDescriptor > & GetColorAttachmentDescriptors() const
Definition: pipeline_descriptor.cc:212
impeller::BackendCast< ShaderFunctionMTL, ShaderFunction >::Cast
static ShaderFunctionMTL & Cast(ShaderFunction &base)
Definition: backend_cast.h:13
impeller::PipelineDescriptor::GetDepthStencilAttachmentDescriptor
std::optional< DepthAttachmentDescriptor > GetDepthStencilAttachmentDescriptor() const
Definition: pipeline_descriptor.cc:207
impeller::VertexDescriptorMTL
Definition: vertex_descriptor_mtl.h:17
impeller::PipelineDescriptor::GetStageEntrypoints
const std::map< ShaderStage, std::shared_ptr< const ShaderFunction > > & GetStageEntrypoints() const
Definition: pipeline_descriptor.cc:222
impeller::PipelineLibraryMTL::~PipelineLibraryMTL
~PipelineLibraryMTL() override
impeller
Definition: allocation.cc:12