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)
62  .SetDefaultColorFormat(color_format)
65  .SetSupportsCompute(true)
70  .Build();
71 }
72 
73 ContextMTL::ContextMTL(
74  id<MTLDevice> device,
75  id<MTLCommandQueue> command_queue,
76  NSArray<id<MTLLibrary>>* shader_libraries,
77  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
78  std::optional<PixelFormat> pixel_format_override)
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_ =
131  InferMetalCapabilities(device_, pixel_format_override.has_value()
132  ? pixel_format_override.value()
134  command_queue_ip_ = std::make_shared<CommandQueue>();
135 #ifdef IMPELLER_DEBUG
136  gpu_tracer_ = std::make_shared<GPUTracerMTL>();
137 #endif // IMPELLER_DEBUG
138  is_valid_ = true;
139 }
140 
141 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
142  id<MTLDevice> device,
143  const std::vector<std::string>& libraries_paths) {
144  NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
145  for (const auto& library_path : libraries_paths) {
146  if (!fml::IsFile(library_path)) {
147  VALIDATION_LOG << "Shader library does not exist at path '"
148  << library_path << "'";
149  return nil;
150  }
151  NSError* shader_library_error = nil;
152  auto library = [device newLibraryWithFile:@(library_path.c_str())
153  error:&shader_library_error];
154  if (!library) {
155  FML_LOG(ERROR) << "Could not create shader library: "
156  << shader_library_error.localizedDescription.UTF8String;
157  return nil;
158  }
159  [found_libraries addObject:library];
160  }
161  return found_libraries;
162 }
163 
164 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
165  id<MTLDevice> device,
166  const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
167  const std::string& label) {
168  NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
169  for (const auto& library_data : libraries_data) {
170  if (library_data == nullptr) {
171  FML_LOG(ERROR) << "Shader library data was null.";
172  return nil;
173  }
174 
175  __block auto data = library_data;
176 
177  auto dispatch_data =
178  ::dispatch_data_create(library_data->GetMapping(), // buffer
179  library_data->GetSize(), // size
180  dispatch_get_main_queue(), // queue
181  ^() {
182  // We just need a reference.
183  data.reset();
184  } // destructor
185  );
186  if (!dispatch_data) {
187  FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
188  return nil;
189  }
190 
191  NSError* shader_library_error = nil;
192  auto library = [device newLibraryWithData:dispatch_data
193  error:&shader_library_error];
194  if (!library) {
195  FML_LOG(ERROR) << "Could not create shader library: "
196  << shader_library_error.localizedDescription.UTF8String;
197  return nil;
198  }
199  if (!label.empty()) {
200  library.label = @(label.c_str());
201  }
202  [found_libraries addObject:library];
203  }
204  return found_libraries;
205 }
206 
207 static id<MTLDevice> CreateMetalDevice() {
208  return ::MTLCreateSystemDefaultDevice();
209 }
210 
211 static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
212  auto command_queue = device.newCommandQueue;
213  if (!command_queue) {
214  VALIDATION_LOG << "Could not set up the command queue.";
215  return nullptr;
216  }
217  command_queue.label = @"Impeller Command Queue";
218  return command_queue;
219 }
220 
221 std::shared_ptr<ContextMTL> ContextMTL::Create(
222  const std::vector<std::string>& shader_library_paths,
223  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
224  auto device = CreateMetalDevice();
225  auto command_queue = CreateMetalCommandQueue(device);
226  if (!command_queue) {
227  return nullptr;
228  }
229  auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
230  device, command_queue,
231  MTLShaderLibraryFromFilePaths(device, shader_library_paths),
232  std::move(is_gpu_disabled_sync_switch)));
233  if (!context->IsValid()) {
234  FML_LOG(ERROR) << "Could not create Metal context.";
235  return nullptr;
236  }
237  return context;
238 }
239 
240 std::shared_ptr<ContextMTL> ContextMTL::Create(
241  const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
242  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
243  const std::string& library_label,
244  std::optional<PixelFormat> pixel_format_override) {
245  auto device = CreateMetalDevice();
246  auto command_queue = CreateMetalCommandQueue(device);
247  if (!command_queue) {
248  return nullptr;
249  }
250  auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
251  device, command_queue,
252  MTLShaderLibraryFromFileData(device, shader_libraries_data,
253  library_label),
254  std::move(is_gpu_disabled_sync_switch), pixel_format_override));
255  if (!context->IsValid()) {
256  FML_LOG(ERROR) << "Could not create Metal context.";
257  return nullptr;
258  }
259  return context;
260 }
261 
262 std::shared_ptr<ContextMTL> ContextMTL::Create(
263  id<MTLDevice> device,
264  id<MTLCommandQueue> command_queue,
265  const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
266  std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
267  const std::string& library_label) {
268  auto context = std::shared_ptr<ContextMTL>(
269  new ContextMTL(device, command_queue,
270  MTLShaderLibraryFromFileData(device, shader_libraries_data,
271  library_label),
272  std::move(is_gpu_disabled_sync_switch)));
273  if (!context->IsValid()) {
274  FML_LOG(ERROR) << "Could not create Metal context.";
275  return nullptr;
276  }
277  return context;
278 }
279 
280 ContextMTL::~ContextMTL() {
281  is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
282 }
283 
284 Context::BackendType ContextMTL::GetBackendType() const {
285  return Context::BackendType::kMetal;
286 }
287 
288 // |Context|
289 std::string ContextMTL::DescribeGpuModel() const {
290  return std::string([[device_ name] UTF8String]);
291 }
292 
293 // |Context|
294 bool ContextMTL::IsValid() const {
295  return is_valid_;
296 }
297 
298 // |Context|
299 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
300  return shader_library_;
301 }
302 
303 // |Context|
304 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
305  return pipeline_library_;
306 }
307 
308 // |Context|
309 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
310  return sampler_library_;
311 }
312 
313 // |Context|
314 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
315  return CreateCommandBufferInQueue(command_queue_);
316 }
317 
318 // |Context|
319 void ContextMTL::Shutdown() {}
320 
321 #ifdef IMPELLER_DEBUG
322 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer() const {
323  return gpu_tracer_;
324 }
325 #endif // IMPELLER_DEBUG
326 
327 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
328  const {
329  return is_gpu_disabled_sync_switch_;
330 }
331 
332 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
333  id<MTLCommandQueue> queue) const {
334  if (!IsValid()) {
335  return nullptr;
336  }
337 
338  auto buffer = std::shared_ptr<CommandBufferMTL>(
339  new CommandBufferMTL(weak_from_this(), queue));
340  if (!buffer->IsValid()) {
341  return nullptr;
342  }
343  return buffer;
344 }
345 
346 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
347  return resource_allocator_;
348 }
349 
350 id<MTLDevice> ContextMTL::GetMTLDevice() const {
351  return device_;
352 }
353 
354 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
355  return device_capabilities_;
356 }
357 
358 void ContextMTL::SetCapabilities(
359  const std::shared_ptr<const Capabilities>& capabilities) {
360  device_capabilities_ = capabilities;
361 }
362 
363 // |Context|
364 bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
365  device_capabilities_ = InferMetalCapabilities(device_, format);
366  return true;
367 }
368 
369 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
370  const std::string& label) const {
371  auto buffer = [command_queue_ commandBuffer];
372  if (!label.empty()) {
373  [buffer setLabel:@(label.data())];
374  }
375  return buffer;
376 }
377 
378 void ContextMTL::StoreTaskForGPU(const std::function<void()>& task) {
379  tasks_awaiting_gpu_.emplace_back(task);
380  while (tasks_awaiting_gpu_.size() > kMaxTasksAwaitingGPU) {
381  tasks_awaiting_gpu_.front()();
382  tasks_awaiting_gpu_.pop_front();
383  }
384 }
385 
386 void ContextMTL::FlushTasksAwaitingGPU() {
387  for (const auto& task : tasks_awaiting_gpu_) {
388  task();
389  }
390  tasks_awaiting_gpu_.clear();
391 }
392 
393 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
394  : parent_(parent) {}
395 
396 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) {
397  if (!new_is_disabled) {
398  parent_.FlushTasksAwaitingGPU();
399  }
400 }
401 
402 // |Context|
403 std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
404  return command_queue_ip_;
405 }
406 
407 } // namespace impeller
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::CapabilitiesBuilder::Build
std::unique_ptr< Capabilities > Build()
Definition: capabilities.cc:212
impeller::Context::BackendType
BackendType
Definition: context.h:47
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
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
formats.h
impeller::CapabilitiesBuilder
Definition: capabilities.h:116
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:164
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:99
impeller::CreateMetalCommandQueue
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
Definition: context_mtl.mm:211
impeller::CapabilitiesBuilder::SetSupportsSSBO
CapabilitiesBuilder & SetSupportsSSBO(bool value)
Definition: capabilities.cc:142
impeller::CapabilitiesBuilder::SetSupportsCompute
CapabilitiesBuilder & SetSupportsCompute(bool value)
Definition: capabilities.cc:159
impeller::CapabilitiesBuilder::SetDefaultDepthStencilFormat
CapabilitiesBuilder & SetDefaultDepthStencilFormat(PixelFormat value)
Definition: capabilities.cc:182
impeller::CapabilitiesBuilder::SetDefaultGlyphAtlasFormat
CapabilitiesBuilder & SetDefaultGlyphAtlasFormat(PixelFormat value)
Definition: capabilities.cc:206
impeller::DeviceSupportsFramebufferFetch
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
Definition: context_mtl.mm:22
impeller::CapabilitiesBuilder::SetSupportsFramebufferFetch
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
Definition: capabilities.cc:153
impeller::CapabilitiesBuilder::SetSupportsComputeSubgroups
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
Definition: capabilities.cc:164
capabilities.h
impeller::CapabilitiesBuilder::SetSupportsTextureToTextureBlits
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
Definition: capabilities.cc:147
impeller::CreateMetalDevice
static id< MTLDevice > CreateMetalDevice()
Definition: context_mtl.mm:207
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:170
sampler_descriptor.h
impeller::CapabilitiesBuilder::SetDefaultStencilFormat
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
Definition: capabilities.cc:176
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::CapabilitiesBuilder::SetSupportsDecalSamplerAddressMode
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
Definition: capabilities.cc:194
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:141
impeller::ContextMTL::GetCommandQueue
std::shared_ptr< CommandQueue > GetCommandQueue() const override
Return the graphics queue for submitting command buffers.
Definition: context_mtl.mm:403
gpu_tracer_mtl.h
impeller::CapabilitiesBuilder::SetSupportsDeviceTransientTextures
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
Definition: capabilities.cc:200
impeller::ContextMTL
Definition: context_mtl.h:34
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller
Definition: aiks_blend_unittests.cc:18
impeller::CapabilitiesBuilder::SetSupportsReadFromResolve
CapabilitiesBuilder & SetSupportsReadFromResolve(bool value)
Definition: capabilities.cc:188
impeller::CapabilitiesBuilder::SetSupportsOffscreenMSAA
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
Definition: capabilities.cc:137