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