blob: 76e3083b7fbcd7d6f2731b69ce7592a260d33a6b [file] [log] [blame]
// 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 std::make_shared<TextureMTL>(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