Flutter Impeller
allocator_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/build_config.h"
8 #include "flutter/fml/logging.h"
13 
14 namespace impeller {
15 
16 static bool DeviceSupportsDeviceTransientTargets(id<MTLDevice> device) {
17  // Refer to the "Memoryless render targets" feature in the table below:
18  // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
19  if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
20  return [device supportsFamily:MTLGPUFamilyApple2];
21  } else {
22 #if FML_OS_IOS
23  // This is perhaps redundant. But, just in case we somehow get into a case
24  // where Impeller runs on iOS versions less than 8.0 and/or without A8
25  // GPUs, we explicitly check feature set support.
26  return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
27 #else
28  // MacOS devices with Apple GPUs are only available with macos 10.15 and
29  // above. So, if we are here, it is safe to assume that memory-less targets
30  // are not supported.
31  return false;
32 #endif
33  }
34  FML_UNREACHABLE();
35 }
36 
37 static bool DeviceHasUnifiedMemoryArchitecture(id<MTLDevice> device) {
38  if (@available(ios 13.0, tvos 13.0, macOS 10.15, *)) {
39  return [device hasUnifiedMemory];
40  } else {
41 #if FML_OS_IOS
42  // iOS devices where the availability check can fail always have had UMA.
43  return true;
44 #else
45  // Mac devices where the availability check can fail have never had UMA.
46  return false;
47 #endif
48  }
49  FML_UNREACHABLE();
50 }
51 
52 static ISize DeviceMaxTextureSizeSupported(id<MTLDevice> device) {
53  // Since Apple didn't expose API for us to get the max texture size, we have
54  // to use hardcoded data from
55  // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
56  // According to the feature set table, there are two supported max sizes :
57  // 16384 and 8192 for devices flutter support. The former is used on macs and
58  // latest ios devices. The latter is used on old ios devices.
59  if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
60  if ([device supportsFamily:MTLGPUFamilyApple3] ||
61  [device supportsFamily:MTLGPUFamilyMacCatalyst1] ||
62  [device supportsFamily:MTLGPUFamilyMac1]) {
63  return {16384, 16384};
64  }
65  return {8192, 8192};
66  } else {
67 #if FML_OS_IOS
68  if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1] ||
69  [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
70  return {16384, 16384};
71  }
72 #endif
73 #if FML_OS_MACOSX
74  return {16384, 16384};
75 #endif
76  return {8192, 8192};
77  }
78 }
79 
80 static bool SupportsLossyTextureCompression(id<MTLDevice> device) {
81 #ifdef FML_OS_IOS_SIMULATOR
82  return false;
83 #else
84  if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
85  return [device supportsFamily:MTLGPUFamilyApple8];
86  }
87  return false;
88 #endif
89 }
90 
91 AllocatorMTL::AllocatorMTL(id<MTLDevice> device, std::string label)
92  : device_(device), allocator_label_(std::move(label)) {
93  if (!device_) {
94  return;
95  }
96 
97  supports_memoryless_targets_ = DeviceSupportsDeviceTransientTargets(device_);
98  supports_uma_ = DeviceHasUnifiedMemoryArchitecture(device_);
99  max_texture_supported_ = DeviceMaxTextureSizeSupported(device_);
100 
101  is_valid_ = true;
102 }
103 
104 AllocatorMTL::~AllocatorMTL() = default;
105 
106 bool AllocatorMTL::IsValid() const {
107  return is_valid_;
108 }
109 
110 static MTLResourceOptions ToMTLResourceOptions(StorageMode type,
111  bool supports_memoryless_targets,
112  bool supports_uma) {
113  switch (type) {
114  case StorageMode::kHostVisible:
115 #if FML_OS_IOS
116  return MTLResourceStorageModeShared;
117 #else
118  if (supports_uma) {
119  return MTLResourceStorageModeShared;
120  } else {
121  return MTLResourceStorageModeManaged;
122  }
123 #endif
124  case StorageMode::kDevicePrivate:
125  return MTLResourceStorageModePrivate;
126  case StorageMode::kDeviceTransient:
127  if (supports_memoryless_targets) {
128  // Device may support but the OS has not been updated.
129  if (@available(macOS 11.0, *)) {
130  return MTLResourceStorageModeMemoryless;
131  } else {
132  return MTLResourceStorageModePrivate;
133  }
134  } else {
135  return MTLResourceStorageModePrivate;
136  }
137  FML_UNREACHABLE();
138  }
139  FML_UNREACHABLE();
140 }
141 
142 static MTLStorageMode ToMTLStorageMode(StorageMode mode,
143  bool supports_memoryless_targets,
144  bool supports_uma) {
145  switch (mode) {
146  case StorageMode::kHostVisible:
147 #if FML_OS_IOS
148  return MTLStorageModeShared;
149 #else
150  if (supports_uma) {
151  return MTLStorageModeShared;
152  } else {
153  return MTLStorageModeManaged;
154  }
155 #endif
156  case StorageMode::kDevicePrivate:
157  return MTLStorageModePrivate;
158  case StorageMode::kDeviceTransient:
159  if (supports_memoryless_targets) {
160  // Device may support but the OS has not been updated.
161  if (@available(macOS 11.0, *)) {
162  return MTLStorageModeMemoryless;
163  } else {
164  return MTLStorageModePrivate;
165  }
166  } else {
167  return MTLStorageModePrivate;
168  }
169  FML_UNREACHABLE();
170  }
171  FML_UNREACHABLE();
172 }
173 
174 std::shared_ptr<DeviceBuffer> AllocatorMTL::OnCreateBuffer(
175  const DeviceBufferDescriptor& desc) {
176  const auto resource_options = ToMTLResourceOptions(
177  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
178  const auto storage_mode = ToMTLStorageMode(
179  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
180 
181  auto buffer = [device_ newBufferWithLength:desc.size
182  options:resource_options];
183  if (!buffer) {
184  return nullptr;
185  }
186  return std::shared_ptr<DeviceBufferMTL>(new DeviceBufferMTL(desc, //
187  buffer, //
188  storage_mode //
189  ));
190 }
191 
192 std::shared_ptr<Texture> AllocatorMTL::OnCreateTexture(
193  const TextureDescriptor& desc) {
194  if (!IsValid()) {
195  return nullptr;
196  }
197 
198  auto mtl_texture_desc = ToMTLTextureDescriptor(desc);
199 
200  if (!mtl_texture_desc) {
201  VALIDATION_LOG << "Texture descriptor was invalid.";
202  return nullptr;
203  }
204 
205  mtl_texture_desc.storageMode = ToMTLStorageMode(
206  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
207 
208  if (@available(macOS 12.5, ios 15.0, *)) {
209  if (desc.compression_type == CompressionType::kLossy &&
211  mtl_texture_desc.compressionType = MTLTextureCompressionTypeLossy;
212  }
213  }
214 
215  auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc];
216  if (!texture) {
217  return nullptr;
218  }
219  return TextureMTL::Create(desc, texture);
220 }
221 
222 uint16_t AllocatorMTL::MinimumBytesPerRow(PixelFormat format) const {
223  return static_cast<uint16_t>([device_
224  minimumLinearTextureAlignmentForPixelFormat:ToMTLPixelFormat(format)]);
225 }
226 
227 ISize AllocatorMTL::GetMaxTextureSizeSupported() const {
228  return max_texture_supported_;
229 }
230 
231 } // namespace impeller
impeller::DeviceHasUnifiedMemoryArchitecture
static bool DeviceHasUnifiedMemoryArchitecture(id< MTLDevice > device)
Definition: allocator_mtl.mm:37
allocator_mtl.h
formats_mtl.h
impeller::SupportsLossyTextureCompression
static bool SupportsLossyTextureCompression(id< MTLDevice > device)
Definition: allocator_mtl.mm:80
impeller::AllocatorMTL::AllocatorMTL
AllocatorMTL()
validation.h
impeller::ToMTLStorageMode
static MTLStorageMode ToMTLStorageMode(StorageMode mode, bool supports_memoryless_targets, bool supports_uma)
Definition: allocator_mtl.mm:142
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:100
impeller::ToMTLTextureDescriptor
MTLTextureDescriptor * ToMTLTextureDescriptor(const TextureDescriptor &desc)
Definition: formats_mtl.mm:86
impeller::TSize< int64_t >
impeller::StorageMode
StorageMode
Specified where the allocation resides and how it is used.
Definition: formats.h:33
impeller::ToMTLResourceOptions
static MTLResourceOptions ToMTLResourceOptions(StorageMode type, bool supports_memoryless_targets, bool supports_uma)
Definition: allocator_mtl.mm:110
impeller::DeviceSupportsDeviceTransientTargets
static bool DeviceSupportsDeviceTransientTargets(id< MTLDevice > device)
Definition: allocator_mtl.mm:16
impeller::DeviceMaxTextureSizeSupported
static ISize DeviceMaxTextureSizeSupported(id< MTLDevice > device)
Definition: allocator_mtl.mm:52
impeller::ToMTLPixelFormat
constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format)
Definition: formats_mtl.h:77
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
std
Definition: comparable.h:95
texture_mtl.h
impeller
Definition: aiks_blur_unittests.cc:20
device_buffer_mtl.h