Flutter Impeller
shader_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 "flutter/fml/closure.h"
10 
11 namespace impeller {
12 
13 ShaderLibraryMTL::ShaderLibraryMTL(NSArray<id<MTLLibrary>>* libraries)
14  : libraries_([libraries mutableCopy]) {
15  if (libraries_ == nil || libraries_.count == 0) {
16  return;
17  }
18 
19  is_valid_ = true;
20 }
21 
22 ShaderLibraryMTL::~ShaderLibraryMTL() = default;
23 
24 bool ShaderLibraryMTL::IsValid() const {
25  return is_valid_;
26 }
27 
28 static MTLFunctionType ToMTLFunctionType(ShaderStage stage) {
29  switch (stage) {
30  case ShaderStage::kVertex:
31  return MTLFunctionTypeVertex;
32  case ShaderStage::kFragment:
33  return MTLFunctionTypeFragment;
34  case ShaderStage::kUnknown:
35  case ShaderStage::kCompute:
36  return MTLFunctionTypeKernel;
37  }
38  FML_UNREACHABLE();
39 }
40 
41 std::shared_ptr<const ShaderFunction> ShaderLibraryMTL::GetFunction(
42  std::string_view name,
43  ShaderStage stage) {
44  if (!IsValid()) {
45  return nullptr;
46  }
47 
48  if (name.empty()) {
49  VALIDATION_LOG << "Library function name was empty.";
50  return nullptr;
51  }
52 
53  ShaderKey key(name, stage);
54 
55  id<MTLFunction> function = nil;
56  id<MTLLibrary> library = nil;
57 
58  {
59  ReaderLock lock(libraries_mutex_);
60 
61  if (auto found = functions_.find(key); found != functions_.end()) {
62  return found->second;
63  }
64 
65  for (size_t i = 0, count = [libraries_ count]; i < count; i++) {
66  library = libraries_[i];
67  function = [library newFunctionWithName:@(name.data())];
68  if (function) {
69  break;
70  }
71  }
72 
73  if (function == nil) {
74  return nullptr;
75  }
76 
77  if (function.functionType != ToMTLFunctionType(stage)) {
78  VALIDATION_LOG << "Library function named " << name
79  << " was for an unexpected shader stage.";
80  return nullptr;
81  }
82 
83  auto func = std::shared_ptr<ShaderFunctionMTL>(new ShaderFunctionMTL(
84  library_id_, function, library, {name.data(), name.size()}, stage));
85  functions_[key] = func;
86 
87  return func;
88  }
89 }
90 
91 id<MTLDevice> ShaderLibraryMTL::GetDevice() const {
92  ReaderLock lock(libraries_mutex_);
93  if (libraries_.count > 0u) {
94  return libraries_[0].device;
95  }
96  return nil;
97 }
98 
99 // |ShaderLibrary|
100 void ShaderLibraryMTL::RegisterFunction(std::string name,
101  ShaderStage stage,
102  std::shared_ptr<fml::Mapping> code,
103  RegistrationCallback callback) {
104  if (!callback) {
105  callback = [](auto) {};
106  }
107  auto failure_callback = std::make_shared<fml::ScopedCleanupClosure>(
108  [callback]() { callback(false); });
109  if (!IsValid()) {
110  return;
111  }
112  if (code == nullptr || code->GetMapping() == nullptr) {
113  return;
114  }
115  auto device = GetDevice();
116  if (device == nil) {
117  return;
118  }
119 
120  auto source = [[NSString alloc] initWithBytes:code->GetMapping()
121  length:code->GetSize()
122  encoding:NSUTF8StringEncoding];
123 
124  auto weak_this = weak_from_this();
125  [device newLibraryWithSource:source
126  options:NULL
127  completionHandler:^(id<MTLLibrary> library, NSError* error) {
128  auto strong_this = weak_this.lock();
129  if (!strong_this) {
130  VALIDATION_LOG << "Shader library was collected before "
131  "dynamic shader stage could be registered.";
132  return;
133  }
134  if (!library) {
135  VALIDATION_LOG << "Could not register dynamic stage library: "
136  << error.localizedDescription.UTF8String;
137  return;
138  }
139  reinterpret_cast<ShaderLibraryMTL*>(strong_this.get())
140  ->RegisterLibraryAndCacheFunction(library, name, stage);
141  failure_callback->Release();
142  callback(true);
143  }];
144 }
145 
146 // |ShaderLibrary|
147 void ShaderLibraryMTL::UnregisterFunction(std::string name, ShaderStage stage) {
148  WriterLock lock(libraries_mutex_);
149 
150  ShaderKey key(name, stage);
151 
152  // Cache-first path: the cache, populated at RegisterFunction time, holds
153  // the mapping from registration name to MTLLibrary. Scoped registration
154  // names (e.g. `re:<library_id>:<entry>`) don't match any MSL function name,
155  // so the library-iteration fallback below cannot find them.
156  if (auto found = functions_.find(key); found != functions_.end()) {
157  id<MTLLibrary> target_library =
158  ShaderFunctionMTL::Cast(*found->second).library_;
159  if (target_library) {
160  [libraries_ removeObject:target_library];
161  }
162  functions_.erase(found);
163  return;
164  }
165 
166  // Fallback: look the function up in the libraries by its MSL name. Used
167  // when the cache was not populated (e.g. for engine-bundled libraries that
168  // were registered via the multi-library constructor rather than through
169  // RegisterFunction).
170  bool found_library = false;
171  for (size_t i = [libraries_ count] - 1; i >= 0; i--) {
172  id<MTLFunction> function =
173  [libraries_[i] newFunctionWithName:@(name.data())];
174  if (function) {
175  [libraries_ removeObjectAtIndex:i];
176  found_library = true;
177  break;
178  }
179  }
180  if (!found_library) {
181  VALIDATION_LOG << "Library containing function " << name
182  << " was not found, so it couldn't be unregistered.";
183  }
184 }
185 
186 void ShaderLibraryMTL::RegisterLibraryAndCacheFunction(id<MTLLibrary> library,
187  const std::string& name,
188  ShaderStage stage) {
189  WriterLock lock(libraries_mutex_);
190  [libraries_ addObject:library];
191 
192  // Find the function in the newly compiled library matching the requested
193  // stage and cache it under the registration name. Subsequent
194  // `GetFunction(name, stage)` calls then resolve via the cache, which lets
195  // namespaced registration names (e.g. `re:<library_id>:<entry>`) that don't
196  // match any MSL function name still resolve to the right function.
197  const MTLFunctionType expected = ToMTLFunctionType(stage);
198  for (NSString* function_name in [library functionNames]) {
199  id<MTLFunction> mtl_function = [library newFunctionWithName:function_name];
200  if (mtl_function && mtl_function.functionType == expected) {
201  ShaderKey key(name, stage);
202  functions_[key] =
203  std::shared_ptr<ShaderFunctionMTL>(new ShaderFunctionMTL(
204  library_id_, mtl_function, library, name, stage));
205  break;
206  }
207  }
208 }
209 
210 } // namespace impeller
static MTLFunctionType ToMTLFunctionType(ShaderStage stage)
#define VALIDATION_LOG
Definition: validation.h:91