| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "impeller/renderer/backend/metal/allocator_mtl.h" |
| |
| #include "flutter/fml/build_config.h" |
| #include "flutter/fml/logging.h" |
| #include "impeller/base/validation.h" |
| #include "impeller/core/buffer.h" |
| #include "impeller/renderer/backend/metal/device_buffer_mtl.h" |
| #include "impeller/renderer/backend/metal/formats_mtl.h" |
| #include "impeller/renderer/backend/metal/texture_mtl.h" |
| |
| namespace impeller { |
| |
| static bool DeviceSupportsDeviceTransientTargets(id<MTLDevice> device) { |
| // Refer to the "Memoryless render targets" feature in the table below: |
| // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf |
| if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) { |
| return [device supportsFamily:MTLGPUFamilyApple2]; |
| } else { |
| #if FML_OS_IOS |
| // This is perhaps redundant. But, just in case we somehow get into a case |
| // where Impeller runs on iOS versions less than 8.0 and/or without A8 |
| // GPUs, we explicitly check feature set support. |
| return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]; |
| #else |
| // MacOS devices with Apple GPUs are only available with macos 10.15 and |
| // above. So, if we are here, it is safe to assume that memory-less targets |
| // are not supported. |
| return false; |
| #endif |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| static bool DeviceHasUnifiedMemoryArchitecture(id<MTLDevice> device) { |
| if (@available(ios 13.0, tvos 13.0, macOS 10.15, *)) { |
| return [device hasUnifiedMemory]; |
| } else { |
| #if FML_OS_IOS |
| // iOS devices where the availability check can fail always have had UMA. |
| return true; |
| #else |
| // Mac devices where the availability check can fail have never had UMA. |
| return false; |
| #endif |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| static ISize DeviceMaxTextureSizeSupported(id<MTLDevice> device) { |
| // Since Apple didn't expose API for us to get the max texture size, we have |
| // to use hardcoded data from |
| // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf |
| // According to the feature set table, there are two supported max sizes : |
| // 16384 and 8192 for devices flutter support. The former is used on macs and |
| // latest ios devices. The latter is used on old ios devices. |
| if (@available(macOS 10.15, iOS 13, tvOS 13, *)) { |
| if ([device supportsFamily:MTLGPUFamilyApple3] || |
| [device supportsFamily:MTLGPUFamilyMacCatalyst1] || |
| [device supportsFamily:MTLGPUFamilyMac1]) { |
| return {16384, 16384}; |
| } |
| return {8192, 8192}; |
| } else { |
| #if FML_OS_IOS |
| if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1] || |
| [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) { |
| return {16384, 16384}; |
| } |
| #endif |
| #if FML_OS_MACOSX |
| return {16384, 16384}; |
| #endif |
| return {8192, 8192}; |
| } |
| } |
| |
| static bool SupportsLossyTextureCompression(id<MTLDevice> device) { |
| #ifdef FML_OS_IOS_SIMULATOR |
| return false; |
| #else |
| if (@available(macOS 10.15, iOS 13, tvOS 13, *)) { |
| return [device supportsFamily:MTLGPUFamilyApple8]; |
| } |
| return false; |
| #endif |
| } |
| |
| AllocatorMTL::AllocatorMTL(id<MTLDevice> device, std::string label) |
| : device_(device), allocator_label_(std::move(label)) { |
| if (!device_) { |
| return; |
| } |
| |
| supports_memoryless_targets_ = DeviceSupportsDeviceTransientTargets(device_); |
| supports_uma_ = DeviceHasUnifiedMemoryArchitecture(device_); |
| max_texture_supported_ = DeviceMaxTextureSizeSupported(device_); |
| |
| is_valid_ = true; |
| } |
| |
| AllocatorMTL::~AllocatorMTL() = default; |
| |
| bool AllocatorMTL::IsValid() const { |
| return is_valid_; |
| } |
| |
| static MTLResourceOptions ToMTLResourceOptions(StorageMode type, |
| bool supports_memoryless_targets, |
| bool supports_uma) { |
| switch (type) { |
| case StorageMode::kHostVisible: |
| #if FML_OS_IOS |
| return MTLResourceStorageModeShared; |
| #else |
| if (supports_uma) { |
| return MTLResourceStorageModeShared; |
| } else { |
| return MTLResourceStorageModeManaged; |
| } |
| #endif |
| case StorageMode::kDevicePrivate: |
| return MTLResourceStorageModePrivate; |
| case StorageMode::kDeviceTransient: |
| if (supports_memoryless_targets) { |
| // Device may support but the OS has not been updated. |
| if (@available(macOS 11.0, *)) { |
| return MTLResourceStorageModeMemoryless; |
| } else { |
| return MTLResourceStorageModePrivate; |
| } |
| } else { |
| return MTLResourceStorageModePrivate; |
| } |
| FML_UNREACHABLE(); |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| static MTLStorageMode ToMTLStorageMode(StorageMode mode, |
| bool supports_memoryless_targets, |
| bool supports_uma) { |
| switch (mode) { |
| case StorageMode::kHostVisible: |
| #if FML_OS_IOS |
| return MTLStorageModeShared; |
| #else |
| if (supports_uma) { |
| return MTLStorageModeShared; |
| } else { |
| return MTLStorageModeManaged; |
| } |
| #endif |
| case StorageMode::kDevicePrivate: |
| return MTLStorageModePrivate; |
| case StorageMode::kDeviceTransient: |
| if (supports_memoryless_targets) { |
| // Device may support but the OS has not been updated. |
| if (@available(macOS 11.0, *)) { |
| return MTLStorageModeMemoryless; |
| } else { |
| return MTLStorageModePrivate; |
| } |
| } else { |
| return MTLStorageModePrivate; |
| } |
| FML_UNREACHABLE(); |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| std::shared_ptr<DeviceBuffer> AllocatorMTL::OnCreateBuffer( |
| const DeviceBufferDescriptor& desc) { |
| const auto resource_options = ToMTLResourceOptions( |
| desc.storage_mode, supports_memoryless_targets_, supports_uma_); |
| const auto storage_mode = ToMTLStorageMode( |
| desc.storage_mode, supports_memoryless_targets_, supports_uma_); |
| |
| auto buffer = [device_ newBufferWithLength:desc.size |
| options:resource_options]; |
| if (!buffer) { |
| return nullptr; |
| } |
| return std::shared_ptr<DeviceBufferMTL>(new DeviceBufferMTL(desc, // |
| buffer, // |
| storage_mode // |
| )); |
| } |
| |
| std::shared_ptr<Texture> AllocatorMTL::OnCreateTexture( |
| const TextureDescriptor& desc) { |
| if (!IsValid()) { |
| return nullptr; |
| } |
| |
| auto mtl_texture_desc = ToMTLTextureDescriptor(desc); |
| |
| if (!mtl_texture_desc) { |
| VALIDATION_LOG << "Texture descriptor was invalid."; |
| return nullptr; |
| } |
| |
| mtl_texture_desc.storageMode = ToMTLStorageMode( |
| desc.storage_mode, supports_memoryless_targets_, supports_uma_); |
| |
| if (@available(macOS 12.5, ios 15.0, *)) { |
| if (desc.compression_type == CompressionType::kLossy && |
| SupportsLossyTextureCompression(device_)) { |
| mtl_texture_desc.compressionType = MTLTextureCompressionTypeLossy; |
| } |
| } |
| |
| auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc]; |
| if (!texture) { |
| return nullptr; |
| } |
| return TextureMTL::Create(desc, texture); |
| } |
| |
| uint16_t AllocatorMTL::MinimumBytesPerRow(PixelFormat format) const { |
| return static_cast<uint16_t>([device_ |
| minimumLinearTextureAlignmentForPixelFormat:ToMTLPixelFormat(format)]); |
| } |
| |
| ISize AllocatorMTL::GetMaxTextureSizeSupported() const { |
| return max_texture_supported_; |
| } |
| |
| } // namespace impeller |