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  if (!IsValid()) {
164  return nullptr;
165  }
166 
167  auto mtl_texture_desc = ToMTLTextureDescriptor(desc);
168 
169  if (!mtl_texture_desc) {
170  VALIDATION_LOG << "Texture descriptor was invalid.";
171  return nullptr;
172  }
173 
174  mtl_texture_desc.storageMode = ToMTLStorageMode(
175  desc.storage_mode, supports_memoryless_targets_, supports_uma_);
176 
177  if (@available(macOS 12.5, ios 15.0, *)) {
178  if (desc.compression_type == CompressionType::kLossy &&
180  mtl_texture_desc.compressionType = MTLTextureCompressionTypeLossy;
181  }
182  }
183 
184 #ifdef IMPELLER_DEBUG
185  if (desc.storage_mode != StorageMode::kDeviceTransient) {
186  debug_allocater_->Increment(desc.GetByteSizeOfAllMipLevels());
187  }
188 #endif // IMPELLER_DEBUG
189 
190  auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc];
191  if (!texture) {
192  return nullptr;
193  }
194  std::shared_ptr<TextureMTL> result_texture =
195  TextureMTL::Create(desc, texture);
196 #ifdef IMPELLER_DEBUG
197  result_texture->SetDebugAllocator(debug_allocater_);
198 #endif // IMPELLER_DEBUG
199 
200  return result_texture;
201 }
202 
203 uint16_t AllocatorMTL::MinimumBytesPerRow(PixelFormat format) const {
204  return static_cast<uint16_t>([device_
205  minimumLinearTextureAlignmentForPixelFormat:ToMTLPixelFormat(format)]);
206 }
207 
208 ISize AllocatorMTL::GetMaxTextureSizeSupported() const {
209  return max_texture_supported_;
210 }
211 
213  supports_uma_ = value;
214 }
215 
217 #ifdef IMPELLER_DEBUG
218  return debug_allocater_->GetAllocationSize();
219 #else
220  return {};
221 #endif // IMPELLER_DEBUG
222 }
223 
224 void AllocatorMTL::DebugTraceMemoryStatistics() const {
225 #ifdef IMPELLER_DEBUG
226  FML_TRACE_COUNTER("flutter", "AllocatorMTL",
227  reinterpret_cast<int64_t>(this), // Trace Counter ID
228  "MemoryBudgetUsageMB",
229  DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
230 #endif // IMPELLER_DEBUG
231 }
232 
233 } // 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)
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