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  return [device supportsFamily:MTLGPUFamilyApple2];
23 }
24 
25 ISize DeviceMaxTextureSizeSupported(id<MTLDevice> device) {
26  // Since Apple didn't expose API for us to get the max texture size, we have
27  // to use hardcoded data from
28  // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
29  // According to the feature set table, there are two supported max sizes :
30  // 16384 and 8192 for devices flutter support. The former is used on macs and
31  // latest ios devices. The latter is used on old ios devices.
32  if ([device supportsFamily:MTLGPUFamilyApple3] ||
33  [device supportsFamily:MTLGPUFamilyMacCatalyst1] ||
34  [device supportsFamily:MTLGPUFamilyMac1]) {
35  return {16384, 16384};
36  }
37  return {8192, 8192};
38 }
39 
40 static bool SupportsLossyTextureCompression(id<MTLDevice> device) {
41 #ifdef FML_OS_IOS_SIMULATOR
42  return false;
43 #else
44  return [device supportsFamily:MTLGPUFamilyApple8];
45 #endif
46 }
47 
48 void DebugAllocatorStats::Increment(size_t size) {
49  size_.fetch_add(size, std::memory_order_relaxed);
50 }
51 
52 void DebugAllocatorStats::Decrement(size_t size) {
53  size_.fetch_sub(size, std::memory_order_relaxed);
54 }
55 
57  return Bytes{size_.load()};
58 }
59 
60 AllocatorMTL::AllocatorMTL(id<MTLDevice> device, std::string label)
61  : device_(device), allocator_label_(std::move(label)) {
62  if (!device_) {
63  return;
64  }
65 
66  supports_memoryless_targets_ = DeviceSupportsDeviceTransientTargets(device_);
67  supports_uma_ = device_.hasUnifiedMemory;
68  max_texture_supported_ = DeviceMaxTextureSizeSupported(device_);
69 
70  is_valid_ = true;
71 }
72 
73 AllocatorMTL::~AllocatorMTL() = default;
74 
75 bool AllocatorMTL::IsValid() const {
76  return is_valid_;
77 }
78 
79 static MTLResourceOptions ToMTLResourceOptions(StorageMode type,
80  bool supports_memoryless_targets,
81  bool supports_uma) {
82  switch (type) {
84 #if FML_OS_IOS
85  return MTLResourceStorageModeShared;
86 #else
87  if (supports_uma) {
88  return MTLResourceStorageModeShared;
89  } else {
90  return MTLResourceStorageModeManaged;
91  }
92 #endif
94  return MTLResourceStorageModePrivate;
96  if (supports_memoryless_targets) {
97  // Device may support but the OS has not been updated.
98  if (@available(macOS 11.0, *)) {
99  return MTLResourceStorageModeMemoryless;
100  } else {
101  return MTLResourceStorageModePrivate;
102  }
103  } else {
104  return MTLResourceStorageModePrivate;
105  }
106  FML_UNREACHABLE();
107  }
108  FML_UNREACHABLE();
109 }
110 
111 static MTLStorageMode ToMTLStorageMode(StorageMode mode,
112  bool supports_memoryless_targets,
113  bool supports_uma) {
114  switch (mode) {
116 #if FML_OS_IOS
117  return MTLStorageModeShared;
118 #else
119  if (supports_uma) {
120  return MTLStorageModeShared;
121  } else {
122  return MTLStorageModeManaged;
123  }
124 #endif
126  return MTLStorageModePrivate;
128  if (supports_memoryless_targets) {
129  // Device may support but the OS has not been updated.
130  if (@available(macOS 11.0, *)) {
131  return MTLStorageModeMemoryless;
132  } else {
133  return MTLStorageModePrivate;
134  }
135  } else {
136  return MTLStorageModePrivate;
137  }
138  FML_UNREACHABLE();
139  }
140  FML_UNREACHABLE();
141 }
142 
143 std::shared_ptr<DeviceBuffer> AllocatorMTL::OnCreateBuffer(
144  const DeviceBufferDescriptor& desc) {
145  const auto resource_options = ToMTLResourceOptions(
146  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
147  const auto storage_mode = ToMTLStorageMode(
148  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
149 
150  auto buffer = [device_ newBufferWithLength:desc.size
151  options:resource_options];
152  if (!buffer) {
153  return nullptr;
154  }
155  return std::shared_ptr<DeviceBufferMTL>(new DeviceBufferMTL(desc, //
156  buffer, //
157  storage_mode //
158  ));
159 }
160 
161 std::shared_ptr<Texture> AllocatorMTL::OnCreateTexture(
162  const TextureDescriptor& desc,
163  bool threadsafe) {
164  if (!IsValid()) {
165  return nullptr;
166  }
167 
168  auto mtl_texture_desc = ToMTLTextureDescriptor(desc);
169 
170  if (!mtl_texture_desc) {
171  VALIDATION_LOG << "Texture descriptor was invalid.";
172  return nullptr;
173  }
174 
175  mtl_texture_desc.storageMode = ToMTLStorageMode(
176  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
177 
178  if (@available(macOS 12.5, ios 15.0, *)) {
179  if (desc.compression_type == CompressionType::kLossy &&
181  mtl_texture_desc.compressionType = MTLTextureCompressionTypeLossy;
182  }
183  }
184 
185 #ifdef IMPELLER_DEBUG
186  if (desc.storage_mode != StorageMode::kDeviceTransient) {
187  debug_allocater_->Increment(desc.GetByteSizeOfAllMipLevels());
188  }
189 #endif // IMPELLER_DEBUG
190 
191  auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc];
192  if (!texture) {
193  return nullptr;
194  }
195  std::shared_ptr<TextureMTL> result_texture =
196  TextureMTL::Create(desc, texture);
197 #ifdef IMPELLER_DEBUG
198  result_texture->SetDebugAllocator(debug_allocater_);
199 #endif // IMPELLER_DEBUG
200 
201  return result_texture;
202 }
203 
204 uint16_t AllocatorMTL::MinimumBytesPerRow(PixelFormat format) const {
205  return static_cast<uint16_t>([device_
206  minimumLinearTextureAlignmentForPixelFormat:ToMTLPixelFormat(format)]);
207 }
208 
209 ISize AllocatorMTL::GetMaxTextureSizeSupported() const {
210  return max_texture_supported_;
211 }
212 
214  supports_uma_ = value;
215 }
216 
218 #ifdef IMPELLER_DEBUG
219  return debug_allocater_->GetAllocationSize();
220 #else
221  return {};
222 #endif // IMPELLER_DEBUG
223 }
224 
225 void AllocatorMTL::DebugTraceMemoryStatistics() const {
226 #ifdef IMPELLER_DEBUG
227  FML_TRACE_COUNTER("flutter", "AllocatorMTL",
228  reinterpret_cast<int64_t>(this), // Trace Counter ID
229  "MemoryBudgetUsageMB",
230  DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
231 #endif // IMPELLER_DEBUG
232 }
233 
234 } // 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:61
int32_t value
static MTLStorageMode ToMTLStorageMode(StorageMode mode, bool supports_memoryless_targets, bool supports_uma)
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