Flutter Impeller
context_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 <memory>
8 
9 #include "flutter/fml/concurrent_message_loop.h"
10 #include "flutter/fml/file.h"
11 #include "flutter/fml/logging.h"
12 #include "flutter/fml/paths.h"
13 #include "flutter/fml/synchronization/sync_switch.h"
14 #include "impeller/core/formats.h"
19 
20 namespace impeller {
21 
22 static bool DeviceSupportsFramebufferFetch(id<MTLDevice> device) {
23  // The iOS simulator lies about supporting framebuffer fetch.
24 #if FML_OS_IOS_SIMULATOR
25  return false;
26 #else // FML_OS_IOS_SIMULATOR
27 
28  if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
29  return [device supportsFamily:MTLGPUFamilyApple2];
30  }
31  // According to
32  // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf , Apple2
33  // corresponds to iOS GPU family 2, which supports A8 devices.
34 #if FML_OS_IOS
35  return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
36 #else
37  return false;
38 #endif // FML_OS_IOS
39 #endif // FML_OS_IOS_SIMULATOR
40 }
41 
42 static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
43  bool supports_subgroups = false;
44  // Refer to the "SIMD-scoped reduction operations" feature in the table
45  // below: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
46  if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
47  supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
48  [device supportsFamily:MTLGPUFamilyMac2];
49  }
50  return supports_subgroups;
51 }
52 
53 static std::unique_ptr<Capabilities> InferMetalCapabilities(
54  id<MTLDevice> device,
55  PixelFormat color_format) {
56  return CapabilitiesBuilder()
58  .SetSupportsSSBO(true)
63  .SetDefaultColorFormat(color_format)
66  .SetSupportsCompute(true)
71  .Build();
72 }
73 
74 ContextMTL::ContextMTL(
75  id<MTLDevice> device,
76  id<MTLCommandQueue> command_queue,
77  NSArray<id<MTLLibrary>>* shader_libraries,
78  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch)
79  : device_(device),
80  command_queue_(command_queue),
81  is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) {
82  // Validate device.
83  if (!device_) {
84  VALIDATION_LOG << "Could not set up valid Metal device.";
85  return;
86  }
87 
88  sync_switch_observer_.reset(new SyncSwitchObserver(*this));
89  is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
90 
91  // Setup the shader library.
92  {
93  if (shader_libraries == nil) {
94  VALIDATION_LOG << "Shader libraries were null.";
95  return;
96  }
97 
98  // std::make_shared disallowed because of private friend ctor.
99  auto library = std::shared_ptr<ShaderLibraryMTL>(
100  new ShaderLibraryMTL(shader_libraries));
101  if (!library->IsValid()) {
102  VALIDATION_LOG << "Could not create valid Metal shader library.";
103  return;
104  }
105  shader_library_ = std::move(library);
106  }
107 
108  // Setup the pipeline library.
109  {
110  pipeline_library_ =
111  std::shared_ptr<PipelineLibraryMTL>(new PipelineLibraryMTL(device_));
112  }
113 
114  // Setup the sampler library.
115  {
116  sampler_library_ =
117  std::shared_ptr<SamplerLibraryMTL>(new SamplerLibraryMTL(device_));
118  }
119 
120  // Setup the resource allocator.
121  {
122  resource_allocator_ = std::shared_ptr<AllocatorMTL>(
123  new AllocatorMTL(device_, "Impeller Permanents Allocator"));
124  if (!resource_allocator_) {
125  VALIDATION_LOG << "Could not set up the resource allocator.";
126  return;
127  }
128  }
129 
130  device_capabilities_ =
132  command_queue_ip_ = std::make_shared<CommandQueue>();
133 #ifdef IMPELLER_DEBUG
134  gpu_tracer_ = std::make_shared<GPUTracerMTL>();
135 #endif // IMPELLER_DEBUG
136  is_valid_ = true;
137 }
138 
139 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
140  id<MTLDevice> device,
141  const std::vector<std::string>& libraries_paths) {
142  NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
143  for (const auto& library_path : libraries_paths) {
144  if (!fml::IsFile(library_path)) {
145  VALIDATION_LOG << "Shader library does not exist at path '"
146  << library_path << "'";
147  return nil;
148  }
149  NSError* shader_library_error = nil;
150  auto library = [device newLibraryWithFile:@(library_path.c_str())
151  error:&shader_library_error];
152  if (!library) {
153  FML_LOG(ERROR) << "Could not create shader library: "
154  << shader_library_error.localizedDescription.UTF8String;
155  return nil;
156  }
157  [found_libraries addObject:library];
158  }
159  return found_libraries;
160 }
161 
162 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
163  id<MTLDevice> device,
164  const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
165  const std::string& label) {
166  NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
167  for (const auto& library_data : libraries_data) {
168  if (library_data == nullptr) {
169  FML_LOG(ERROR) << "Shader library data was null.";
170  return nil;
171  }
172 
173  __block auto data = library_data;
174 
175  auto dispatch_data =
176  ::dispatch_data_create(library_data->GetMapping(), // buffer
177  library_data->GetSize(), // size
178  dispatch_get_main_queue(), // queue
179  ^() {
180  // We just need a reference.
181  data.reset();
182  } // destructor
183  );
184  if (!dispatch_data) {
185  FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
186  return nil;
187  }
188 
189  NSError* shader_library_error = nil;
190  auto library = [device newLibraryWithData:dispatch_data
191  error:&shader_library_error];
192  if (!library) {
193  FML_LOG(ERROR) << "Could not create shader library: "
194  << shader_library_error.localizedDescription.UTF8String;
195  return nil;
196  }
197  if (!label.empty()) {
198  library.label = @(label.c_str());
199  }
200  [found_libraries addObject:library];
201  }
202  return found_libraries;
203 }
204 
205 static id<MTLDevice> CreateMetalDevice() {
206  return ::MTLCreateSystemDefaultDevice();
207 }
208 
209 static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
210  auto command_queue = device.newCommandQueue;
211  if (!command_queue) {
212  VALIDATION_LOG << "Could not set up the command queue.";
213  return nullptr;
214  }
215  command_queue.label = @"Impeller Command Queue";
216  return command_queue;
217 }
218 
219 std::shared_ptr<ContextMTL> ContextMTL::Create(
220  const std::vector<std::string>& shader_library_paths,
221  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
222  auto device = CreateMetalDevice();
223  auto command_queue = CreateMetalCommandQueue(device);
224  if (!command_queue) {
225  return nullptr;
226  }
227  auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
228  device, command_queue,
229  MTLShaderLibraryFromFilePaths(device, shader_library_paths),
230  std::move(is_gpu_disabled_sync_switch)));
231  if (!context->IsValid()) {
232  FML_LOG(ERROR) << "Could not create Metal context.";
233  return nullptr;
234  }
235  return context;
236 }
237 
238 std::shared_ptr<ContextMTL> ContextMTL::Create(
239  const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
240  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
241  const std::string& library_label) {
242  auto device = CreateMetalDevice();
243  auto command_queue = CreateMetalCommandQueue(device);
244  if (!command_queue) {
245  return nullptr;
246  }
247  auto context = std::shared_ptr<ContextMTL>(
248  new ContextMTL(device, command_queue,
249  MTLShaderLibraryFromFileData(device, shader_libraries_data,
250  library_label),
251  std::move(is_gpu_disabled_sync_switch)));
252  if (!context->IsValid()) {
253  FML_LOG(ERROR) << "Could not create Metal context.";
254  return nullptr;
255  }
256  return context;
257 }
258 
259 std::shared_ptr<ContextMTL> ContextMTL::Create(
260  id<MTLDevice> device,
261  id<MTLCommandQueue> command_queue,
262  const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
263  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
264  const std::string& library_label) {
265  auto context = std::shared_ptr<ContextMTL>(
266  new ContextMTL(device, command_queue,
267  MTLShaderLibraryFromFileData(device, shader_libraries_data,
268  library_label),
269  std::move(is_gpu_disabled_sync_switch)));
270  if (!context->IsValid()) {
271  FML_LOG(ERROR) << "Could not create Metal context.";
272  return nullptr;
273  }
274  return context;
275 }
276 
277 ContextMTL::~ContextMTL() {
278  is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
279 }
280 
281 Context::BackendType ContextMTL::GetBackendType() const {
282  return Context::BackendType::kMetal;
283 }
284 
285 // |Context|
286 std::string ContextMTL::DescribeGpuModel() const {
287  return std::string([[device_ name] UTF8String]);
288 }
289 
290 // |Context|
291 bool ContextMTL::IsValid() const {
292  return is_valid_;
293 }
294 
295 // |Context|
296 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
297  return shader_library_;
298 }
299 
300 // |Context|
301 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
302  return pipeline_library_;
303 }
304 
305 // |Context|
306 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
307  return sampler_library_;
308 }
309 
310 // |Context|
311 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
312  return CreateCommandBufferInQueue(command_queue_);
313 }
314 
315 // |Context|
316 void ContextMTL::Shutdown() {}
317 
318 #ifdef IMPELLER_DEBUG
319 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer() const {
320  return gpu_tracer_;
321 }
322 #endif // IMPELLER_DEBUG
323 
324 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
325  const {
326  return is_gpu_disabled_sync_switch_;
327 }
328 
329 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
330  id<MTLCommandQueue> queue) const {
331  if (!IsValid()) {
332  return nullptr;
333  }
334 
335  auto buffer = std::shared_ptr<CommandBufferMTL>(
336  new CommandBufferMTL(weak_from_this(), queue));
337  if (!buffer->IsValid()) {
338  return nullptr;
339  }
340  return buffer;
341 }
342 
343 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
344  return resource_allocator_;
345 }
346 
347 id<MTLDevice> ContextMTL::GetMTLDevice() const {
348  return device_;
349 }
350 
351 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
352  return device_capabilities_;
353 }
354 
355 void ContextMTL::SetCapabilities(
356  const std::shared_ptr<const Capabilities>& capabilities) {
357  device_capabilities_ = capabilities;
358 }
359 
360 // |Context|
361 bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
362  device_capabilities_ = InferMetalCapabilities(device_, format);
363  return true;
364 }
365 
366 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
367  const std::string& label) const {
368  auto buffer = [command_queue_ commandBuffer];
369  if (!label.empty()) {
370  [buffer setLabel:@(label.data())];
371  }
372  return buffer;
373 }
374 
375 void ContextMTL::StoreTaskForGPU(const std::function<void()>& task) {
376  tasks_awaiting_gpu_.emplace_back(task);
377  while (tasks_awaiting_gpu_.size() > kMaxTasksAwaitingGPU) {
378  tasks_awaiting_gpu_.front()();
379  tasks_awaiting_gpu_.pop_front();
380  }
381 }
382 
383 void ContextMTL::FlushTasksAwaitingGPU() {
384  for (const auto& task : tasks_awaiting_gpu_) {
385  task();
386  }
387  tasks_awaiting_gpu_.clear();
388 }
389 
390 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
391  : parent_(parent) {}
392 
393 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) {
394  if (!new_is_disabled) {
395  parent_.FlushTasksAwaitingGPU();
396  }
397 }
398 
399 // |Context|
400 std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
401  return command_queue_ip_;
402 }
403 
404 } // namespace impeller
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::CapabilitiesBuilder::Build
std::unique_ptr< Capabilities > Build()
Definition: capabilities.cc:226
impeller::Context::BackendType
BackendType
Definition: context.h:48
impeller::DeviceSupportsComputeSubgroups
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
Definition: context_mtl.mm:42
impeller::PixelFormat::kA8UNormInt
@ kA8UNormInt
context_mtl.h
impeller::CreateCommandBuffer
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
Definition: command_buffer_mtl.mm:117
formats.h
impeller::CapabilitiesBuilder
Definition: capabilities.h:122
impeller::MTLShaderLibraryFromFileData
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFileData(id< MTLDevice > device, const std::vector< std::shared_ptr< fml::Mapping >> &libraries_data, const std::string &label)
Definition: context_mtl.mm:162
sampler_library_mtl.h
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:100
impeller::CreateMetalCommandQueue
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
Definition: context_mtl.mm:209
impeller::CapabilitiesBuilder::SetSupportsSSBO
CapabilitiesBuilder & SetSupportsSSBO(bool value)
Definition: capabilities.cc:150
impeller::CapabilitiesBuilder::SetSupportsCompute
CapabilitiesBuilder & SetSupportsCompute(bool value)
Definition: capabilities.cc:173
impeller::CapabilitiesBuilder::SetDefaultDepthStencilFormat
CapabilitiesBuilder & SetDefaultDepthStencilFormat(PixelFormat value)
Definition: capabilities.cc:196
impeller::CapabilitiesBuilder::SetDefaultGlyphAtlasFormat
CapabilitiesBuilder & SetDefaultGlyphAtlasFormat(PixelFormat value)
Definition: capabilities.cc:220
impeller::DeviceSupportsFramebufferFetch
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
Definition: context_mtl.mm:22
impeller::CapabilitiesBuilder::SetSupportsFramebufferFetch
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
Definition: capabilities.cc:167
impeller::CapabilitiesBuilder::SetSupportsComputeSubgroups
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
Definition: capabilities.cc:178
capabilities.h
impeller::CapabilitiesBuilder::SetSupportsTextureToTextureBlits
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
Definition: capabilities.cc:161
impeller::CreateMetalDevice
static id< MTLDevice > CreateMetalDevice()
Definition: context_mtl.mm:205
impeller::InferMetalCapabilities
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
Definition: context_mtl.mm:53
impeller::CapabilitiesBuilder::SetDefaultColorFormat
CapabilitiesBuilder & SetDefaultColorFormat(PixelFormat value)
Definition: capabilities.cc:184
sampler_descriptor.h
impeller::CapabilitiesBuilder::SetDefaultStencilFormat
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
Definition: capabilities.cc:190
impeller::CapabilitiesBuilder::SetSupportsBufferToTextureBlits
CapabilitiesBuilder & SetSupportsBufferToTextureBlits(bool value)
Definition: capabilities.cc:155
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::CapabilitiesBuilder::SetSupportsDecalSamplerAddressMode
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
Definition: capabilities.cc:208
std
Definition: comparable.h:95
impeller::MTLShaderLibraryFromFilePaths
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
Definition: context_mtl.mm:139
impeller::ContextMTL::GetCommandQueue
std::shared_ptr< CommandQueue > GetCommandQueue() const override
Return the graphics queue for submitting command buffers.
Definition: context_mtl.mm:400
gpu_tracer_mtl.h
impeller::CapabilitiesBuilder::SetSupportsDeviceTransientTextures
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
Definition: capabilities.cc:214
impeller::ContextMTL
Definition: context_mtl.h:35
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller
Definition: aiks_blur_unittests.cc:20
impeller::CapabilitiesBuilder::SetSupportsReadFromResolve
CapabilitiesBuilder & SetSupportsReadFromResolve(bool value)
Definition: capabilities.cc:202
impeller::CapabilitiesBuilder::SetSupportsOffscreenMSAA
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
Definition: capabilities.cc:145