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