| 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
5 #include "impeller/renderer/backend/metal/allocator_mtl.h"
6
7 #include "flutter/fml/build_config.h"
8 #include "flutter/fml/logging.h"
9 #include "fml/trace_event.h"
10 #include "impeller/base/allocation_size.h"
11 #include "impeller/base/validation.h"
12 #include "impeller/core/formats.h"
13 #include "impeller/renderer/backend/metal/device_buffer_mtl.h"
14 #include "impeller/renderer/backend/metal/formats_mtl.h"
15 #include "impeller/renderer/backend/metal/texture_mtl.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
102 Bytes DebugAllocatorStats::GetAllocationSize() {
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) {
129 case StorageMode::kHostVisible:
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
139 case StorageMode::kDevicePrivate:
140 return MTLResourceStorageModePrivate;
141 case StorageMode::kDeviceTransient:
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) {
161 case StorageMode::kHostVisible:
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
171 case StorageMode::kDevicePrivate:
172 return MTLStorageModePrivate;
173 case StorageMode::kDeviceTransient:
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 &&
225 SupportsLossyTextureCompression(device_)) {
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
258 Bytes AllocatorMTL::DebugGetHeapUsage() const {
259 #ifdef IMPELLER_DEBUG
260 return debug_allocater_->GetAllocationSize();
261 #else
262 return {};
263 #endif // IMPELLER_DEBUG
264 }
265
266 void AllocatorMTL::DebugTraceMemoryStatistics() const {
267 #ifdef IMPELLER_DEBUG
268 FML_TRACE_COUNTER("flutter", "AllocatorMTL",
269 reinterpret_cast<int64_t>(this), // Trace Counter ID
270 "MemoryBudgetUsageMB",
271 DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
272 #endif // IMPELLER_DEBUG
273 }
274
275 } // namespace impeller
impeller::ISize
ISize64 ISize
Definition: size.h:174
impeller::DeviceHasUnifiedMemoryArchitecture
static bool DeviceHasUnifiedMemoryArchitecture(id< MTLDevice > device)
Definition: allocator_mtl.mm:40
allocator_mtl.h
formats_mtl.h
formats.h
impeller::SupportsLossyTextureCompression
static bool SupportsLossyTextureCompression(id< MTLDevice > device)
Definition: allocator_mtl.mm:83
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:157
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:98
impeller::TSize
Definition: size.h:25
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:125
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::DeviceSupportsDeviceTransientTargets
static bool DeviceSupportsDeviceTransientTargets(id< MTLDevice > device)
Definition: allocator_mtl.mm:19
type
GLenum type
Definition: blit_command_gles.cc:151
impeller::TextureMTL::Create
static std::shared_ptr< TextureMTL > Create(TextureDescriptor desc, id< MTLTexture > texture)
Definition: texture_mtl.mm:59
impeller::AllocationSize< 1u >
impeller::AllocatorMTL::DebugGetHeapUsage
Bytes DebugGetHeapUsage() const override
Definition: allocator_mtl.mm:258
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:91
impeller::DeviceMaxTextureSizeSupported
ISize DeviceMaxTextureSizeSupported(id< MTLDevice > device)
Definition: allocator_mtl.mm:55
impeller::DebugAllocatorStats::Increment
void Increment(size_t size)
Increment the tracked allocation size in bytes.
Definition: allocator_mtl.mm:94
std
Definition: comparable.h:95
texture_mtl.h
impeller::DebugAllocatorStats::GetAllocationSize
Bytes GetAllocationSize()
Get the current tracked allocation size.
Definition: allocator_mtl.mm:102
allocation_size.h
impeller
Definition: allocation.cc:12
device_buffer_mtl.h