blob: 836bb22a12eaa9a2859dd76947c68f1a5140a456 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/mtl/MtlCaps.h"
#include "include/core/SkTextureCompressionType.h"
#include "include/gpu/graphite/TextureInfo.h"
#include "include/gpu/graphite/mtl/MtlGraphiteTypes.h"
#include "src/gpu/SwizzlePriv.h"
#include "src/gpu/graphite/CommandBuffer.h"
#include "src/gpu/graphite/ComputePipelineDesc.h"
#include "src/gpu/graphite/GraphicsPipelineDesc.h"
#include "src/gpu/graphite/GraphiteResourceKey.h"
#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/TextureProxy.h"
#include "src/gpu/graphite/mtl/MtlGraphiteUtilsPriv.h"
#include "src/gpu/mtl/MtlUtilsPriv.h"
#include "src/sksl/SkSLUtil.h"
namespace skgpu::graphite {
MtlCaps::MtlCaps(const id<MTLDevice> device, const ContextOptions& options)
: Caps() {
this->initGPUFamily(device);
this->initCaps(device);
this->initShaderCaps();
this->initFormatTable(device);
// Metal-specific MtlCaps
this->finishInitialization(options);
}
bool MtlCaps::GetGPUFamily(id<MTLDevice> device, GPUFamily* gpuFamily, int* group) {
#if SKGPU_GRAPHITE_METAL_SDK_VERSION >= 220
if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
// Apple Silicon
#if SKGPU_GRAPHITE_METAL_SDK_VERSION >= 230
if ([device supportsFamily:MTLGPUFamilyApple7]) {
*gpuFamily = GPUFamily::kApple;
*group = 7;
return true;
}
#endif
#ifdef SK_BUILD_FOR_IOS
if ([device supportsFamily:MTLGPUFamilyApple6]) {
*gpuFamily = GPUFamily::kApple;
*group = 6;
return true;
}
if ([device supportsFamily:MTLGPUFamilyApple5]) {
*gpuFamily = GPUFamily::kApple;
*group = 5;
return true;
}
if ([device supportsFamily:MTLGPUFamilyApple4]) {
*gpuFamily = GPUFamily::kApple;
*group = 4;
return true;
}
if ([device supportsFamily:MTLGPUFamilyApple3]) {
*gpuFamily = GPUFamily::kApple;
*group = 3;
return true;
}
if ([device supportsFamily:MTLGPUFamilyApple2]) {
*gpuFamily = GPUFamily::kApple;
*group = 2;
return true;
}
if ([device supportsFamily:MTLGPUFamilyApple1]) {
*gpuFamily = GPUFamily::kApple;
*group = 1;
return true;
}
#endif
// Older Macs
#if SKGPU_GRAPHITE_METAL_SDK_VERSION >= 300
// TODO: replace with Metal 3 definitions
SkASSERT([device supportsFamily:MTLGPUFamilyMac2]);
*gpuFamily = GPUFamily::kMac;
*group = 2;
return true;
#else
// At the moment MacCatalyst families have the same features as Mac,
// so we treat them the same
if ([device supportsFamily:MTLGPUFamilyMac2] ||
[device supportsFamily:MTLGPUFamilyMacCatalyst2]) {
*gpuFamily = GPUFamily::kMac;
*group = 2;
return true;
}
if ([device supportsFamily:MTLGPUFamilyMac1] ||
[device supportsFamily:MTLGPUFamilyMacCatalyst1]) {
*gpuFamily = GPUFamily::kMac;
*group = 1;
return true;
}
#endif
}
#endif
// No supported GPU families were found
return false;
}
void MtlCaps::initGPUFamily(id<MTLDevice> device) {
if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
if (GetGPUFamily(device, &fGPUFamily, &fFamilyGroup)) {
return;
}
}
// We don't know what this is, fall back to minimum defaults
#ifdef SK_BUILD_FOR_MAC
fGPUFamily = GPUFamily::kMac;
fFamilyGroup = 1;
#else
fGPUFamily = GPUFamily::kApple;
fFamilyGroup = 1;
#endif
}
void MtlCaps::initCaps(const id<MTLDevice> device) {
#if defined(GRAPHITE_TEST_UTILS)
this->setDeviceName([[device name] UTF8String]);
#endif
if (this->isMac() || fFamilyGroup >= 3) {
fMaxTextureSize = 16384;
} else {
fMaxTextureSize = 8192;
}
// We use constant address space for our uniform buffers which has various alignment
// requirements for the offset when binding the buffer. On MacOS Intel the offset must align
// to 256. On iOS or Apple Silicon we must align to the max of the data type consumed by the
// vertex function or 4 bytes, or we can ignore the data type and just use 16 bytes.
//
// On Mac, all copies must be aligned to at least 4 bytes; on iOS there is no alignment.
if (this->isMac()) {
fRequiredUniformBufferAlignment = 256;
fRequiredTransferBufferAlignment = 4;
} else {
fRequiredUniformBufferAlignment = 16;
fRequiredTransferBufferAlignment = 1;
}
fResourceBindingReqs.fUniformBufferLayout = Layout::kMetal;
fResourceBindingReqs.fStorageBufferLayout = Layout::kMetal;
fResourceBindingReqs.fDistinctIndexRanges = true;
// Metal does not distinguish between uniform and storage buffers.
fRequiredStorageBufferAlignment = fRequiredUniformBufferAlignment;
fStorageBufferSupport = true;
fStorageBufferPreferred = true;
fComputeSupport = true;
if (@available(macOS 10.12, iOS 14.0, tvOS 14.0, *)) {
fClampToBorderSupport = (this->isMac() || fFamilyGroup >= 7);
} else {
fClampToBorderSupport = false;
}
// Init sample counts. All devices support 1 (i.e. 0 in skia).
fColorSampleCounts.push_back(1);
if (![device.name containsString:@"Intel"]) {
if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
for (auto sampleCnt : {2, 4, 8}) {
if ([device supportsTextureSampleCount:sampleCnt]) {
fColorSampleCounts.push_back(sampleCnt);
}
}
}
}
}
void MtlCaps::initShaderCaps() {
SkSL::ShaderCaps* shaderCaps = fShaderCaps.get();
// Dual source blending requires Metal 1.2.
if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) {
shaderCaps->fDualSourceBlendingSupport = true;
}
// Setting this true with the assumption that this cap will eventually mean we support varying
// precisions and not just via modifiers.
shaderCaps->fUsesPrecisionModifiers = true;
shaderCaps->fFlatInterpolationSupport = true;
shaderCaps->fShaderDerivativeSupport = true;
shaderCaps->fInfinitySupport = true;
if (@available(macOS 11.0, *)) {
if (this->isApple()) {
shaderCaps->fFBFetchSupport = true;
shaderCaps->fFBFetchColorName = "sk_LastFragColor";
}
}
shaderCaps->fIntegerSupport = true;
shaderCaps->fNonsquareMatrixSupport = true;
shaderCaps->fInverseHyperbolicSupport = true;
// Metal uses IEEE floats so assuming those values here.
shaderCaps->fFloatIs32Bits = true;
}
// Define this so we can use it to initialize arrays and work around
// the fact that these pixel formats are not always available.
#define kMTLPixelFormatB5G6R5Unorm MTLPixelFormat(40)
#define kMTLPixelFormatABGR4Unorm MTLPixelFormat(42)
#define kMTLPixelFormatETC2_RGB8 MTLPixelFormat(180)
// These are all the valid MTLPixelFormats that we currently support in Skia. They are roughly
// ordered from most frequently used to least to improve look up times in arrays.
static constexpr MTLPixelFormat kMtlFormats[] = {
MTLPixelFormatRGBA8Unorm,
MTLPixelFormatR8Unorm,
MTLPixelFormatA8Unorm,
MTLPixelFormatBGRA8Unorm,
kMTLPixelFormatB5G6R5Unorm,
MTLPixelFormatRGBA16Float,
MTLPixelFormatR16Float,
MTLPixelFormatRG8Unorm,
MTLPixelFormatRGB10A2Unorm,
// MTLPixelFormatBGR10A2Unorm
kMTLPixelFormatABGR4Unorm,
MTLPixelFormatRGBA8Unorm_sRGB,
MTLPixelFormatR16Unorm,
MTLPixelFormatRG16Unorm,
kMTLPixelFormatETC2_RGB8,
#ifdef SK_BUILD_FOR_MAC
MTLPixelFormatBC1_RGBA,
#endif
MTLPixelFormatRGBA16Unorm,
MTLPixelFormatRG16Float,
MTLPixelFormatStencil8,
MTLPixelFormatDepth16Unorm,
MTLPixelFormatDepth32Float,
#ifdef SK_BUILD_FOR_MAC
MTLPixelFormatDepth24Unorm_Stencil8,
#endif
MTLPixelFormatDepth32Float_Stencil8,
MTLPixelFormatInvalid,
};
void MtlCaps::setColorType(SkColorType colorType, std::initializer_list<MTLPixelFormat> formats) {
int idx = static_cast<int>(colorType);
for (auto it = formats.begin(); it != formats.end(); ++it) {
const auto& info = this->getFormatInfo(*it);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
if (info.fColorTypeInfos[i].fColorType == colorType) {
fColorTypeToFormatTable[idx] = *it;
return;
}
}
}
}
size_t MtlCaps::GetFormatIndex(MTLPixelFormat pixelFormat) {
static_assert(std::size(kMtlFormats) == MtlCaps::kNumMtlFormats,
"Size of kMtlFormats array must match static value in header");
for (size_t i = 0; i < MtlCaps::kNumMtlFormats; ++i) {
if (kMtlFormats[i] == pixelFormat) {
return i;
}
}
return GetFormatIndex(MTLPixelFormatInvalid);
}
void MtlCaps::initFormatTable(const id<MTLDevice> device) {
FormatInfo* info;
if (@available(macOS 11.0, iOS 8.0, tvOS 9.0, *)) {
if (this->isApple()) {
SkASSERT(kMTLPixelFormatB5G6R5Unorm == MTLPixelFormatB5G6R5Unorm);
SkASSERT(kMTLPixelFormatABGR4Unorm == MTLPixelFormatABGR4Unorm);
}
}
// Format: RGBA8Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGBA8Unorm, Surface: kRGBA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: RGBA8Unorm, Surface: kRGB_888x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_888x_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
}
}
// Format: R8Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatR8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 3;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: R8Unorm, Surface: kR8_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR8_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: R8Unorm, Surface: kAlpha_8
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kAlpha_8_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
}
// Format: R8Unorm, Surface: kGray_8
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kGray_8_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1");
}
}
// Format: A8Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatA8Unorm)];
info->fFlags = FormatInfo::kTexturable_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: A8Unorm, Surface: kAlpha_8
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kAlpha_8_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: BGRA8Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatBGRA8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: BGRA8Unorm, Surface: kBGRA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kBGRA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
if (@available(macOS 11.0, iOS 8.0, tvOS 9.0, *)) {
if (this->isApple()) {
// Format: B5G6R5Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatB5G6R5Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos =
std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: B5G6R5Unorm, Surface: kBGR_565
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_565_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag |
ColorTypeInfo::kRenderable_Flag;
}
}
// Format: ABGR4Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatABGR4Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos =
std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: ABGR4Unorm, Surface: kABGR_4444
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kARGB_4444_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag |
ColorTypeInfo::kRenderable_Flag;
}
}
}
}
// Format: RGBA8Unorm_sRGB
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA8Unorm_sRGB)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGBA8Unorm_sRGB, Surface: kSRGBA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kSRGBA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: RGB10A2Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGB10A2Unorm)];
if (this->isMac() || fFamilyGroup >= 3) {
info->fFlags = FormatInfo::kAllFlags;
} else {
info->fFlags = FormatInfo::kTexturable_Flag;
}
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGB10A2Unorm, Surface: kRGBA_1010102
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_1010102_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: RGBA16Float
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA16Float)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGBA16Float, Surface: RGBA_F16
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_F16_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: RGBA16Float, Surface: RGBA_F16Norm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_F16Norm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: R16Float
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatR16Float)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: R16Float, Surface: kA16_float
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kA16_float_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
}
}
// Format: RG8Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRG8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RG8Unorm, Surface: kR8G8_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR8G8_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: RGBA16Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA16Unorm)];
if (this->isMac()) {
info->fFlags = FormatInfo::kAllFlags;
} else {
info->fFlags = FormatInfo::kTexturable_Flag | FormatInfo::kRenderable_Flag;
}
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGBA16Unorm, Surface: kR16G16B16A16_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR16G16B16A16_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: RG16Float
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRG16Float)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RG16Float, Surface: kR16G16_float
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR16G16_float_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: R16Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatR16Unorm)];
if (this->isMac()) {
info->fFlags = FormatInfo::kAllFlags;
} else {
info->fFlags = FormatInfo::kTexturable_Flag | FormatInfo::kRenderable_Flag;
}
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: R16Unorm, Surface: kA16_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kA16_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
}
}
// Format: RG16Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatRG16Unorm)];
if (this->isMac()) {
info->fFlags = FormatInfo::kAllFlags;
} else {
info->fFlags = FormatInfo::kTexturable_Flag | FormatInfo::kRenderable_Flag;
}
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RG16Unorm, Surface: kR16G16_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR16G16_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: ETC2_RGB8
{
if (@available(macOS 11.0, iOS 8.0, tvOS 9.0, *)) {
if (this->isApple()) {
info = &fFormatTable[GetFormatIndex(MTLPixelFormatETC2_RGB8)];
info->fFlags = FormatInfo::kTexturable_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: ETC2_RGB8, Surface: kRGB_888x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_888x_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
}
}
}
}
// Format: BC1_RGBA
{
#ifdef SK_BUILD_FOR_MAC
if (this->isMac()) {
info = &fFormatTable[GetFormatIndex(MTLPixelFormatBC1_RGBA)];
info->fFlags = FormatInfo::kTexturable_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: BC1_RGBA, Surface: kRGBA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
}
}
#endif
}
/*
* Non-color formats
*/
// Format: Stencil8
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatStencil8)];
info->fFlags = FormatInfo::kMSAA_Flag;
info->fColorTypeInfoCount = 0;
}
// Format: Depth16Unorm
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatDepth16Unorm)];
info->fFlags = FormatInfo::kMSAA_Flag;
if (this->isMac() || fFamilyGroup >= 3) {
info->fFlags |= FormatInfo::kResolve_Flag;
}
info->fColorTypeInfoCount = 0;
}
// Format: Depth32Float
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatDepth32Float)];
info->fFlags = FormatInfo::kMSAA_Flag;
if (this->isMac() || fFamilyGroup >= 3) {
info->fFlags |= FormatInfo::kResolve_Flag;
}
info->fColorTypeInfoCount = 0;
}
// Format: Depth24Unorm_Stencil8
{
#ifdef SK_BUILD_FOR_MAC
if (this->isMac() && [device isDepth24Stencil8PixelFormatSupported]) {
info = &fFormatTable[GetFormatIndex(MTLPixelFormatDepth24Unorm_Stencil8)];
info->fFlags = FormatInfo::kMSAA_Flag | FormatInfo::kResolve_Flag;
info->fColorTypeInfoCount = 0;
}
#endif
}
// Format: Depth32Float_Stencil8
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatDepth32Float_Stencil8)];
info->fFlags = FormatInfo::kMSAA_Flag;
if (this->isMac() || fFamilyGroup >= 3) {
info->fFlags |= FormatInfo::kResolve_Flag;
}
info->fColorTypeInfoCount = 0;
}
////////////////////////////////////////////////////////////////////////////
// Map SkColorTypes (used for creating SkSurfaces) to MTLPixelFormats. The order in which the
// formats are passed into the setColorType function indicates the priority in selecting which
// format we use for a given SkColorType.
std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, MTLPixelFormatInvalid);
this->setColorType(kAlpha_8_SkColorType, { MTLPixelFormatR8Unorm,
MTLPixelFormatA8Unorm });
if (@available(macOS 11.0, iOS 8.0, tvOS 9.0, *)) {
if (this->isApple()) {
this->setColorType(kRGB_565_SkColorType, {MTLPixelFormatB5G6R5Unorm});
this->setColorType(kARGB_4444_SkColorType, { MTLPixelFormatABGR4Unorm });
}
}
this->setColorType(kRGBA_8888_SkColorType, { MTLPixelFormatRGBA8Unorm });
this->setColorType(kRGB_888x_SkColorType, { MTLPixelFormatRGBA8Unorm });
this->setColorType(kBGRA_8888_SkColorType, { MTLPixelFormatBGRA8Unorm });
this->setColorType(kRGBA_1010102_SkColorType, { MTLPixelFormatRGB10A2Unorm });
// kBGRA_1010102_SkColorType
// kRGB_101010x_SkColorType
// kBGR_101010x_SkColorType
// kBGR_101010x_XR_SkColorType
this->setColorType(kGray_8_SkColorType, { MTLPixelFormatR8Unorm });
this->setColorType(kRGBA_F16Norm_SkColorType, { MTLPixelFormatRGBA16Float });
this->setColorType(kRGBA_F16_SkColorType, { MTLPixelFormatRGBA16Float });
// kRGBA_F32_SkColorType
this->setColorType(kR8G8_unorm_SkColorType, { MTLPixelFormatRG8Unorm });
this->setColorType(kA16_float_SkColorType, { MTLPixelFormatR16Float });
this->setColorType(kR16G16_float_SkColorType, { MTLPixelFormatRG16Float });
this->setColorType(kA16_unorm_SkColorType, { MTLPixelFormatR16Unorm });
this->setColorType(kR16G16_unorm_SkColorType, { MTLPixelFormatRG16Unorm });
this->setColorType(kR16G16B16A16_unorm_SkColorType,{ MTLPixelFormatRGBA16Unorm });
this->setColorType(kSRGBA_8888_SkColorType, { MTLPixelFormatRGBA8Unorm_sRGB });
this->setColorType(kR8_unorm_SkColorType, { MTLPixelFormatR8Unorm });
}
TextureInfo MtlCaps::getDefaultSampledTextureInfo(SkColorType colorType,
Mipmapped mipmapped,
Protected,
Renderable renderable) const {
MTLTextureUsage usage = MTLTextureUsageShaderRead;
if (renderable == Renderable::kYes) {
usage |= MTLTextureUsageRenderTarget;
}
MtlPixelFormat format = this->getFormatFromColorType(colorType);
if (format == MTLPixelFormatInvalid) {
return {};
}
MtlTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = mipmapped;
info.fFormat = format;
info.fUsage = usage;
info.fStorageMode = MTLStorageModePrivate;
info.fFramebufferOnly = false;
return info;
}
TextureInfo MtlCaps::getTextureInfoForSampledCopy(const TextureInfo& textureInfo,
Mipmapped mipmapped) const {
MtlTextureInfo info;
if (!textureInfo.getMtlTextureInfo(&info)) {
return {};
}
info.fSampleCount = 1;
info.fMipmapped = mipmapped;
info.fUsage = MTLTextureUsageShaderRead;
info.fStorageMode = MTLStorageModePrivate;
info.fFramebufferOnly = false;
return info;
}
namespace {
MTLPixelFormat format_from_compression(SkTextureCompressionType compression) {
switch (compression) {
case SkTextureCompressionType::kETC2_RGB8_UNORM:
return kMTLPixelFormatETC2_RGB8;
case SkTextureCompressionType::kBC1_RGBA8_UNORM:
#ifdef SK_BUILD_FOR_MAC
return MTLPixelFormatBC1_RGBA;
#endif
default:
return MTLPixelFormatInvalid;
}
}
}
TextureInfo MtlCaps::getDefaultCompressedTextureInfo(SkTextureCompressionType compression,
Mipmapped mipmapped,
Protected) const {
MTLTextureUsage usage = MTLTextureUsageShaderRead;
MtlPixelFormat format = format_from_compression(compression);
if (format == MTLPixelFormatInvalid) {
return {};
}
MtlTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = mipmapped;
info.fFormat = format;
info.fUsage = usage;
info.fStorageMode = MTLStorageModePrivate;
info.fFramebufferOnly = false;
return info;
}
MTLStorageMode MtlCaps::getDefaultMSAAStorageMode(Discardable discardable) const {
// Try to use memoryless if it's available (only on new Apple silicon)
if (discardable == Discardable::kYes && this->isApple()) {
if (@available(macOS 11.0, iOS 10.0, tvOS 10.0, *)) {
return MTLStorageModeMemoryless;
}
}
// If it's not discardable or not available, private is the best option
return MTLStorageModePrivate;
}
TextureInfo MtlCaps::getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo,
Discardable discardable) const {
if (fDefaultMSAASamples <= 1) {
return {};
}
const MtlTextureSpec& singleSpec = singleSampledInfo.mtlTextureSpec();
if (!this->isRenderable((MTLPixelFormat)singleSpec.fFormat, fDefaultMSAASamples)) {
return {};
}
MTLTextureUsage usage = MTLTextureUsageRenderTarget;
MtlTextureInfo info;
info.fSampleCount = fDefaultMSAASamples;
info.fMipmapped = Mipmapped::kNo;
info.fFormat = singleSpec.fFormat;
info.fUsage = usage;
info.fStorageMode = this->getDefaultMSAAStorageMode(discardable);
info.fFramebufferOnly = false;
return info;
}
MTLPixelFormat MtlCaps::getFormatFromDepthStencilFlags(
SkEnumBitMask<DepthStencilFlags> mask) const {
// TODO: Decide if we want to change this to always return a combined depth and stencil format
// to allow more sharing of depth stencil allocations.
if (mask == DepthStencilFlags::kDepth) {
// Graphite only needs 16-bits for depth values, so save some memory. If needed for
// workarounds, MTLPixelFormatDepth32Float is also available.
return MTLPixelFormatDepth16Unorm;
} else if (mask == DepthStencilFlags::kStencil) {
return MTLPixelFormatStencil8;
} else if (mask == DepthStencilFlags::kDepthStencil) {
#if defined(SK_BUILD_FOR_MAC)
if (SkToBool(this->getFormatInfo(MTLPixelFormatDepth24Unorm_Stencil8).fFlags)) {
return MTLPixelFormatDepth24Unorm_Stencil8;
}
#endif
return MTLPixelFormatDepth32Float_Stencil8;
}
SkASSERT(false);
return MTLPixelFormatInvalid;
}
TextureInfo MtlCaps::getDefaultDepthStencilTextureInfo(
SkEnumBitMask<DepthStencilFlags> depthStencilType,
uint32_t sampleCount,
Protected) const {
MtlTextureInfo info;
info.fSampleCount = sampleCount;
info.fMipmapped = Mipmapped::kNo;
info.fFormat = this->getFormatFromDepthStencilFlags(depthStencilType);
info.fUsage = MTLTextureUsageRenderTarget;
info.fStorageMode = this->getDefaultMSAAStorageMode(Discardable::kYes);
info.fFramebufferOnly = false;
return info;
}
TextureInfo MtlCaps::getDefaultStorageTextureInfo(SkColorType colorType) const {
// Storage textures are currently always sampleable from a shader.
MTLPixelFormat format = static_cast<MTLPixelFormat>(this->getFormatFromColorType(colorType));
if (format == MTLPixelFormatInvalid) {
return {};
}
const FormatInfo& formatInfo = this->getFormatInfo(format);
if (!SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags)) {
return {};
}
MTLTextureUsage usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
MtlTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = Mipmapped::kNo;
info.fFormat = format;
info.fUsage = usage;
info.fStorageMode = MTLStorageModePrivate;
info.fFramebufferOnly = false;
return info;
}
const Caps::ColorTypeInfo* MtlCaps::getColorTypeInfo(
SkColorType ct, const TextureInfo& textureInfo) const {
MTLPixelFormat mtlFormat = static_cast<MTLPixelFormat>(textureInfo.mtlTextureSpec().fFormat);
if (mtlFormat == MTLPixelFormatInvalid) {
return nullptr;
}
const FormatInfo& info = this->getFormatInfo(mtlFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
const ColorTypeInfo& ctInfo = info.fColorTypeInfos[i];
if (ctInfo.fColorType == ct) {
return &ctInfo;
}
}
return nullptr;
}
static const skgpu::UniqueKey::Domain kGraphicsPipelineDomain = UniqueKey::GenerateDomain();
static const int kGraphicsPipelineKeyData32Count = 5;
UniqueKey MtlCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc) const {
UniqueKey pipelineKey;
{
// 5 uint32_t's (render step id, paint id, uint64 renderpass desc, uint16 write swizzle key)
UniqueKey::Builder builder(&pipelineKey, kGraphicsPipelineDomain,
kGraphicsPipelineKeyData32Count, "GraphicsPipeline");
// add GraphicsPipelineDesc key
builder[0] = pipelineDesc.renderStepID();
builder[1] = pipelineDesc.paintParamsID().asUInt();
// add RenderPassDesc key
uint64_t renderPassKey = this->getRenderPassDescKey(renderPassDesc);
builder[2] = renderPassKey & 0xFFFFFFFF;
builder[3] = (renderPassKey >> 32) & 0xFFFFFFFF;
builder[4] = renderPassDesc.fWriteSwizzle.asKey();
builder.finish();
}
return pipelineKey;
}
bool MtlCaps::extractGraphicsDescs(const UniqueKey& key,
GraphicsPipelineDesc* pipelineDesc,
RenderPassDesc* renderPassDesc,
const RendererProvider* rendererProvider) const {
struct UnpackedKeyData {
// From the GraphicsPipelineDesc
uint32_t fRenderStepID = 0;
UniquePaintParamsID fPaintParamsID;
// From the RenderPassDesc
MtlPixelFormat fColorFormat = 0;
uint32_t fColorSampleCount = 1;
MtlPixelFormat fDSFormat = 0;
uint32_t fDSSampleCount = 1;
Swizzle fWriteSwizzle;
} keyData;
SkASSERT(key.domain() == kGraphicsPipelineDomain);
SkASSERT(key.dataSize() == 4 * kGraphicsPipelineKeyData32Count);
const uint32_t* rawKeyData = key.data();
keyData.fRenderStepID = rawKeyData[0];
keyData.fPaintParamsID = rawKeyData[1] ? UniquePaintParamsID(rawKeyData[1])
: UniquePaintParamsID::InvalidID();
keyData.fDSFormat = static_cast<MtlPixelFormat>((rawKeyData[2] >> 16) & 0xFFFF);
keyData.fDSSampleCount = rawKeyData[2] & 0xFFFF;
keyData.fColorFormat = static_cast<MtlPixelFormat>((rawKeyData[3] >> 16) & 0xFFFF);
keyData.fColorSampleCount = rawKeyData[3] & 0xFFFF;
keyData.fWriteSwizzle = SwizzleCtorAccessor::Make(rawKeyData[4]);
// Recreate the RenderPassDesc
SkASSERT(keyData.fColorSampleCount == keyData.fDSSampleCount);
MTLPixelFormat dsFormat = (MTLPixelFormat) keyData.fDSFormat;
SkEnumBitMask<DepthStencilFlags> dsFlags = DepthStencilFlags::kNone;
if (MtlFormatIsDepth(dsFormat)) {
dsFlags |= DepthStencilFlags::kDepth;
}
if (MtlFormatIsStencil(dsFormat)) {
dsFlags |= DepthStencilFlags::kStencil;
}
MtlTextureInfo mtlInfo(keyData.fColorSampleCount,
skgpu::Mipmapped::kNo,
keyData.fColorFormat,
MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget,
MTLStorageModePrivate,
/* framebufferOnly= */ false);
TextureInfo info(mtlInfo);
*renderPassDesc = RenderPassDesc::Make(this,
info,
LoadOp::kClear,
StoreOp::kStore,
dsFlags,
/* clearColor= */ { .0f, .0f, .0f, .0f },
/* requiresMSAA= */ keyData.fColorSampleCount > 1,
keyData.fWriteSwizzle);
// Recreate the GraphicsPipelineDesc
const RenderStep* renderStep = rendererProvider->lookup(keyData.fRenderStepID);
UniquePaintParamsID paintID = renderStep->performsShading() ? keyData.fPaintParamsID
: UniquePaintParamsID::InvalidID();
*pipelineDesc = GraphicsPipelineDesc(renderStep, paintID);
return true;
}
uint64_t MtlCaps::getRenderPassDescKey(const RenderPassDesc& renderPassDesc) const {
MtlTextureInfo colorInfo, depthStencilInfo;
renderPassDesc.fColorAttachment.fTextureInfo.getMtlTextureInfo(&colorInfo);
renderPassDesc.fDepthStencilAttachment.fTextureInfo.getMtlTextureInfo(&depthStencilInfo);
SkASSERT(colorInfo.fFormat < 65535 && depthStencilInfo.fFormat < 65535);
uint32_t colorAttachmentKey = colorInfo.fFormat << 16 | colorInfo.fSampleCount;
uint32_t dsAttachmentKey = depthStencilInfo.fFormat << 16 | depthStencilInfo.fSampleCount;
return (((uint64_t) colorAttachmentKey) << 32) | dsAttachmentKey;
}
UniqueKey MtlCaps::makeComputePipelineKey(const ComputePipelineDesc& pipelineDesc) const {
UniqueKey pipelineKey;
{
static const skgpu::UniqueKey::Domain kComputePipelineDomain = UniqueKey::GenerateDomain();
// The key is made up of a single uint32_t corresponding to the compute step ID.
UniqueKey::Builder builder(&pipelineKey, kComputePipelineDomain, 1, "ComputePipeline");
builder[0] = pipelineDesc.computeStep()->uniqueID();
// TODO(b/240615224): The local work group size should factor into the key on platforms
// that don't support specialization constants and require the workgroup/threadgroup size to
// be specified in the shader text (D3D12, Vulkan 1.0, and OpenGL).
builder.finish();
}
return pipelineKey;
}
uint32_t MtlCaps::channelMask(const TextureInfo& info) const {
return skgpu::MtlFormatChannels((MTLPixelFormat)info.mtlTextureSpec().fFormat);
}
bool MtlCaps::onIsTexturable(const TextureInfo& info) const {
if (!info.isValid()) {
return false;
}
if (!(info.mtlTextureSpec().fUsage & MTLTextureUsageShaderRead)) {
return false;
}
if (info.mtlTextureSpec().fFramebufferOnly) {
return false;
}
return this->isTexturable((MTLPixelFormat)info.mtlTextureSpec().fFormat);
}
bool MtlCaps::isTexturable(MTLPixelFormat format) const {
const FormatInfo& formatInfo = this->getFormatInfo(format);
return SkToBool(FormatInfo::kTexturable_Flag & formatInfo.fFlags);
}
bool MtlCaps::isRenderable(const TextureInfo& info) const {
return info.isValid() &&
(info.mtlTextureSpec().fUsage & MTLTextureUsageRenderTarget) &&
this->isRenderable((MTLPixelFormat)info.mtlTextureSpec().fFormat, info.numSamples());
}
bool MtlCaps::isRenderable(MTLPixelFormat format, uint32_t sampleCount) const {
return sampleCount <= this->maxRenderTargetSampleCount(format);
}
bool MtlCaps::isStorage(const TextureInfo& info) const {
if (!info.isValid()) {
return false;
}
if (!(info.mtlTextureSpec().fUsage & MTLTextureUsageShaderWrite)) {
return false;
}
if (info.mtlTextureSpec().fFramebufferOnly) {
return false;
}
const FormatInfo& formatInfo =
this->getFormatInfo((MTLPixelFormat)info.mtlTextureSpec().fFormat);
return info.numSamples() == 1 && SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags);
}
uint32_t MtlCaps::maxRenderTargetSampleCount(MTLPixelFormat format) const {
const FormatInfo& formatInfo = this->getFormatInfo(format);
if (!SkToBool(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) {
return 0;
}
if (SkToBool(formatInfo.fFlags & FormatInfo::kMSAA_Flag)) {
return fColorSampleCounts[fColorSampleCounts.size() - 1];
} else {
return 1;
}
}
bool MtlCaps::supportsWritePixels(const TextureInfo& texInfo) const {
MtlTextureInfo mtlInfo;
texInfo.getMtlTextureInfo(&mtlInfo);
if (mtlInfo.fFramebufferOnly) {
return false;
}
if (texInfo.numSamples() > 1) {
return false;
}
return true;
}
bool MtlCaps::supportsReadPixels(const TextureInfo& texInfo) const {
MtlTextureInfo mtlInfo;
texInfo.getMtlTextureInfo(&mtlInfo);
if (mtlInfo.fFramebufferOnly) {
return false;
}
// We disallow reading back directly from compressed textures.
if (MtlFormatIsCompressed((MTLPixelFormat)mtlInfo.fFormat)) {
return false;
}
if (texInfo.numSamples() > 1) {
return false;
}
return true;
}
std::pair<SkColorType, bool /*isRGBFormat*/> MtlCaps::supportedWritePixelsColorType(
SkColorType dstColorType,
const TextureInfo& dstTextureInfo,
SkColorType srcColorType) const {
MtlTextureInfo mtlInfo;
dstTextureInfo.getMtlTextureInfo(&mtlInfo);
const FormatInfo& info = this->getFormatInfo((MTLPixelFormat)mtlInfo.fFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
const auto& ctInfo = info.fColorTypeInfos[i];
if (ctInfo.fColorType == dstColorType) {
return {dstColorType, false};
}
}
return {kUnknown_SkColorType, false};
}
std::pair<SkColorType, bool /*isRGBFormat*/> MtlCaps::supportedReadPixelsColorType(
SkColorType srcColorType,
const TextureInfo& srcTextureInfo,
SkColorType dstColorType) const {
MtlTextureInfo mtlInfo;
srcTextureInfo.getMtlTextureInfo(&mtlInfo);
// TODO: handle compressed formats
if (MtlFormatIsCompressed((MTLPixelFormat)mtlInfo.fFormat)) {
SkASSERT(this->isTexturable((MTLPixelFormat)mtlInfo.fFormat));
return {kUnknown_SkColorType, false};
}
const FormatInfo& info = this->getFormatInfo((MTLPixelFormat)mtlInfo.fFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
const auto& ctInfo = info.fColorTypeInfos[i];
if (ctInfo.fColorType == srcColorType) {
return {srcColorType, false};
}
}
return {kUnknown_SkColorType, false};
}
void MtlCaps::buildKeyForTexture(SkISize dimensions,
const TextureInfo& info,
ResourceType type,
Shareable shareable,
GraphiteResourceKey* key) const {
const MtlTextureSpec& mtlSpec = info.mtlTextureSpec();
SkASSERT(!dimensions.isEmpty());
// A MTLPixelFormat is an NSUInteger type which is documented to be 32 bits in 32 bit
// applications and 64 bits in 64 bit applications. So it should fit in an uint64_t, but adding
// the assert heere to make sure.
static_assert(sizeof(MTLPixelFormat) <= sizeof(uint64_t));
SkASSERT(mtlSpec.fFormat != MTLPixelFormatInvalid);
uint64_t formatKey = static_cast<uint64_t>(mtlSpec.fFormat);
uint32_t samplesKey = SamplesToKey(info.numSamples());
// We don't have to key the number of mip levels because it is inherit in the combination of
// isMipped and dimensions.
bool isMipped = info.mipmapped() == Mipmapped::kYes;
Protected isProtected = info.isProtected();
bool isFBOnly = mtlSpec.fFramebufferOnly;
// Confirm all the below parts of the key can fit in a single uint32_t. The sum of the shift
// amounts in the asserts must be less than or equal to 32.
SkASSERT(samplesKey < (1u << 3));
SkASSERT(static_cast<uint32_t>(isMipped) < (1u << 1));
SkASSERT(static_cast<uint32_t>(isProtected) < (1u << 1));
SkASSERT(mtlSpec.fUsage < (1u << 5));
SkASSERT(mtlSpec.fStorageMode < (1u << 2));
SkASSERT(static_cast<uint32_t>(isFBOnly) < (1u << 1));
// We need two uint32_ts for dimensions, 2 for format, and 1 for the rest of the key;
static int kNum32DataCnt = 2 + 2 + 1;
GraphiteResourceKey::Builder builder(key, type, kNum32DataCnt, shareable);
builder[0] = dimensions.width();
builder[1] = dimensions.height();
builder[2] = formatKey & 0xFFFFFFFF;
builder[3] = (formatKey >> 32) & 0xFFFFFFFF;
builder[4] = (samplesKey << 0) |
(static_cast<uint32_t>(isMipped) << 3) |
(static_cast<uint32_t>(isProtected) << 4) |
(static_cast<uint32_t>(mtlSpec.fUsage) << 5) |
(static_cast<uint32_t>(mtlSpec.fStorageMode) << 10)|
(static_cast<uint32_t>(isFBOnly) << 12);
}
} // namespace skgpu::graphite