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