Flutter Engine
 
Loading...
Searching...
No Matches
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
18
19#if !__has_feature(objc_arc)
20#error ARC must be enabled !
21#endif
22
23namespace impeller {
24
26 : device_(device) {}
27
28PipelineLibraryMTL::~PipelineLibraryMTL() = default;
29
30using 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
88static 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
97static 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|
109bool PipelineLibraryMTL::IsValid() const {
110 return device_ != nullptr;
111}
112
113// |PipelineLibrary|
114PipelineFuture<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
205PipelineFuture<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|
262bool PipelineLibraryMTL::HasPipeline(const PipelineDescriptor& descriptor) {
263 return pipelines_.find(descriptor) != pipelines_.end();
264}
265
266// |PipelineLibrary|
267void 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
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_
VkDevice device
Definition main.cc:69
const gchar FlBinaryMessengerMessageHandler handler
const uint8_t uint32_t uint32_t GError ** error
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
Dart_NativeFunction function
Definition fuchsia.cc:50
const char * name
Definition fuchsia.cc:49
void erase_if(Collection &container, const std::function< bool(typename Collection::iterator)> &predicate)
Definition container.h:16
static void GetMTLRenderPipelineDescriptor(const PipelineDescriptor &desc, const Callback &callback)
std::function< void(MTLRenderPipelineDescriptor *)> Callback
static id< MTLDepthStencilState > CreateDepthStencilDescriptor(const PipelineDescriptor &desc, id< MTLDevice > device)
MTLDepthStencilDescriptor * ToMTLDepthStencilDescriptor(std::optional< DepthAttachmentDescriptor > depth, std::optional< StencilAttachmentDescriptor > front, std::optional< StencilAttachmentDescriptor > back)
constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format)
Definition formats_mtl.h:76
static MTLComputePipelineDescriptor * GetMTLComputePipelineDescriptor(const ComputePipelineDescriptor &desc)
MTLRenderPipelineColorAttachmentDescriptor * ToMTLRenderPipelineColorAttachmentDescriptor(ColorAttachmentDescriptor descriptor)
#define VALIDATION_LOG
Definition validation.h:91