/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/ganesh/mtl/GrMtlCaps.h" #include "include/core/SkRect.h" #include "include/core/SkTextureCompressionType.h" #include "include/gpu/ganesh/GrBackendSurface.h" #include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h" #include "src/core/SkCompressedDataUtils.h" #include "src/core/SkReadBuffer.h" #include "src/gpu/KeyBuilder.h" #include "src/gpu/ganesh/GrBackendUtils.h" #include "src/gpu/ganesh/GrProcessor.h" #include "src/gpu/ganesh/GrProgramDesc.h" #include "src/gpu/ganesh/GrProgramInfo.h" #include "src/gpu/ganesh/GrRenderTarget.h" #include "src/gpu/ganesh/GrRenderTargetProxy.h" #include "src/gpu/ganesh/GrShaderCaps.h" #include "src/gpu/ganesh/GrSurfaceProxy.h" #include "src/gpu/ganesh/mtl/GrMtlRenderTarget.h" #include "src/gpu/ganesh/mtl/GrMtlTexture.h" #include "src/gpu/ganesh/mtl/GrMtlUtil.h" #include "src/gpu/mtl/MtlUtilsPriv.h" #if defined(GPU_TEST_UTILS) #include "src/gpu/ganesh/TestFormatColorTypeCombination.h" #endif #if !__has_feature(objc_arc) #error This file must be compiled with Arc. Use -fobjc-arc flag #endif GR_NORETAIN_BEGIN GrMtlCaps::GrMtlCaps(const GrContextOptions& contextOptions, const id device) : INHERITED(contextOptions) { fShaderCaps = std::make_unique(); this->initGPUFamily(device); this->initGrCaps(device); this->initShaderCaps(); if (!contextOptions.fDisableDriverCorrectnessWorkarounds) { this->applyDriverCorrectnessWorkarounds(contextOptions, device); } this->initFormatTable(); this->initStencilFormat(device); // TODO: appears to be slow with Mac msaa8, disabled for now fStoreAndMultisampleResolveSupport = (fGPUFamily == GPUFamily::kApple && fFamilyGroup >= 3); // Also slow with non-Apple silicon fPreferDiscardableMSAAAttachment = (fGPUFamily == GPUFamily::kApple); this->finishInitialization(contextOptions); } // translates from older MTLFeatureSet interface to MTLGPUFamily interface bool GrMtlCaps::getGPUFamilyFromFeatureSet(id device, GPUFamily* gpuFamily, int* group) { // MTLFeatureSet is deprecated for newer versions of the SDK #if GR_METAL_SDK_VERSION < 300 #if defined(SK_BUILD_FOR_MAC) // Apple Silicon is only available in later OSes *gpuFamily = GPUFamily::kMac; // Mac OSX 14 if (@available(macOS 10.14, *)) { if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]) { *group = 2; return true; } if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v4]) { *group = 1; return true; } } // Mac OSX 13 if (@available(macOS 10.13, *)) { if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v3]) { *group = 1; return true; } } // Mac OSX 12 if (@available(macOS 10.12, *)) { if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2]) { *group = 1; return true; } } // Mac OSX 11 if (@available(macOS 10.11, *)) { if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) { *group = 1; return true; } } #elif defined(SK_BUILD_FOR_IOS) // TODO: support tvOS *gpuFamily = GPUFamily::kApple; // iOS 12 if (@available(iOS 12.0, tvOS 12.0, *)) { if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily5_v1]) { *group = 5; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v2]) { *group = 4; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v4]) { *group = 3; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v5]) { *group = 2; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v5]) { *group = 1; return true; } } // iOS 11 if (@available(iOS 11.0, tvOS 11.0, *)) { if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) { *group = 4; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v3]) { *group = 3; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v4]) { *group = 2; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v4]) { *group = 1; return true; } } // iOS 10 if (@available(iOS 10.0, tvOS 10.0, *)) { if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) { *group = 3; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) { *group = 2; return true; } if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) { *group = 1; return true; } } // We don't support earlier OSes #endif #endif // GR_METAL_SDK_VERSION < 300 // No supported GPU families were found return false; } bool GrMtlCaps::getGPUFamily(id device, GPUFamily* gpuFamily, int* group) { #if GR_METAL_SDK_VERSION >= 220 if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { // Apple Silicon #if GR_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 // MTLGPUFamilyMac1, MTLGPUFamilyMacCatalyst1, and MTLGPUFamilyMacCatalyst2 are deprecated. // However, some MTLGPUFamilyMac1 only hardware is still supported. // MacCatalyst families have the same features as Mac, so treat them the same if ([device supportsFamily:MTLGPUFamilyMac2] || [device supportsFamily:(MTLGPUFamily)4002/*MTLGPUFamilyMacCatalyst2*/]) { *gpuFamily = GPUFamily::kMac; *group = 2; return true; } if ([device supportsFamily:(MTLGPUFamily)2001/*MTLGPUFamilyMac1*/] || [device supportsFamily:(MTLGPUFamily)4001/*MTLGPUFamilyMacCatalyst1*/]) { *gpuFamily = GPUFamily::kMac; *group = 1; return true; } } #endif // No supported GPU families were found return false; } void GrMtlCaps::initGPUFamily(id device) { if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { if (this->getGPUFamily(device, &fGPUFamily, &fFamilyGroup)) { return; } } else { if (this->getGPUFamilyFromFeatureSet(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 } bool GrMtlCaps::canCopyAsBlit(MTLPixelFormat dstFormat, int dstSampleCount, MTLPixelFormat srcFormat, int srcSampleCount, const SkIRect& srcRect, const SkIPoint& dstPoint, bool areDstSrcSameObj) const { if (!dstFormat || dstFormat != srcFormat) { return false; } if ((dstSampleCount > 1 || srcSampleCount > 1) && (dstSampleCount != srcSampleCount)) { return false; } if (areDstSrcSameObj) { SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(), srcRect.width(), srcRect.height()); if (dstRect.intersect(srcRect)) { return false; } } return true; } bool GrMtlCaps::canCopyAsResolve(MTLPixelFormat dstFormat, int dstSampleCount, MTLPixelFormat srcFormat, int srcSampleCount, bool srcIsRenderTarget, const SkISize srcDimensions, const SkIRect& srcRect, const SkIPoint& dstPoint, bool areDstSrcSameObj) const { if (areDstSrcSameObj) { return false; } if (dstFormat != srcFormat) { return false; } if (dstSampleCount > 1 || srcSampleCount == 1 || !srcIsRenderTarget) { return false; } // TODO: Support copying subrectangles if (dstPoint != SkIPoint::Make(0, 0)) { return false; } if (srcRect != SkIRect::MakeSize(srcDimensions)) { return false; } return true; } bool GrMtlCaps::onCanCopySurface(const GrSurfaceProxy* dst, const SkIRect& dstRect, const GrSurfaceProxy* src, const SkIRect& srcRect) const { // Metal does not support scaling copies if (srcRect.size() != dstRect.size()) { return false; } int dstSampleCnt = 1; int srcSampleCnt = 1; if (const GrRenderTargetProxy* rtProxy = dst->asRenderTargetProxy()) { dstSampleCnt = rtProxy->numSamples(); } if (const GrRenderTargetProxy* rtProxy = src->asRenderTargetProxy()) { srcSampleCnt = rtProxy->numSamples(); } // TODO: need some way to detect whether the proxy is framebufferOnly const SkIPoint dstPoint = dstRect.topLeft(); if (this->canCopyAsBlit(GrBackendFormatAsMTLPixelFormat(dst->backendFormat()), dstSampleCnt, GrBackendFormatAsMTLPixelFormat(src->backendFormat()), srcSampleCnt, srcRect, dstPoint, dst == src)) { return true; } bool srcIsRenderTarget = src->asRenderTargetProxy(); MTLPixelFormat dstFormat = GrBackendFormatAsMTLPixelFormat(dst->backendFormat()); MTLPixelFormat srcFormat = GrBackendFormatAsMTLPixelFormat(src->backendFormat()); return this->canCopyAsResolve(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt, srcIsRenderTarget, src->backingStoreDimensions(), srcRect, dstPoint, dst == src); } void GrMtlCaps::initGrCaps(id device) { #if defined(GPU_TEST_UTILS) this->setDeviceName([[device name] UTF8String]); #endif // Max vertex attribs is the same on all devices fMaxVertexAttributes = 31; // Metal does not support scissor + clear fPerformPartialClearsAsDraws = true; // We always copy in/out of a transfer buffer so it's trivial to support row bytes. fReadPixelsRowBytesSupport = true; fWritePixelsRowBytesSupport = true; fTransferPixelsToRowBytesSupport = true; // RenderTarget and Texture size if (this->isMac() || fFamilyGroup >= 3) { fMaxRenderTargetSize = 16384; } else { fMaxRenderTargetSize = 8192; } fMaxPreferredRenderTargetSize = fMaxRenderTargetSize; fMaxTextureSize = fMaxRenderTargetSize; fMaxPushConstantsSize = 4*1024; fTransferBufferRowBytesAlignment = 1; // This is documented to be 4 for all Macs. However, on Apple GPUs on Mac it appears there is // no actual alignment requirement // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400767-copyfrombuffer if (this->isMac()) { fTransferFromBufferToBufferAlignment = 4; // Buffer updates are sometimes implemented through transfers in GrMtlBuffer. fBufferUpdateDataPreserveAlignment = 4; } // Metal buffers are initialized to zero (if not created with initial data) fBuffersAreInitiallyZero = true; // Init sample counts. All devices support 1 (i.e. 0 in skia). fSampleCounts.push_back(1); if (@available(iOS 9.0, tvOS 9.0, *)) { for (auto sampleCnt : {2, 4, 8}) { if ([device supportsTextureSampleCount:sampleCnt]) { fSampleCounts.push_back(sampleCnt); } } } // Clamp to border is supported on Mac 10.12 and higher. It is not supported on iOS. fClampToBorderSupport = false; #ifdef SK_BUILD_FOR_MAC if (@available(macOS 10.12, *)) { fClampToBorderSupport = true; } #endif // Starting with the assumption that there isn't a reason to not map small buffers. fBufferMapThreshold = 0; // Buffers are always fully mapped. fMapBufferFlags = kCanMap_MapFlag | kAsyncRead_MapFlag; fOversizedStencilSupport = true; fNPOTTextureTileSupport = true; // always available in Metal fMipmapSupport = true; // always available in Metal fAnisoSupport = true; // always available in Metal fReuseScratchTextures = true; // Assuming this okay fTransferFromBufferToTextureSupport = true; fTransferFromSurfaceToBufferSupport = true; fTransferFromBufferToBufferSupport = true; fTextureBarrierSupport = false; // Need to figure out if we can do this fSampleLocationsSupport = false; if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { if (this->isMac() || fFamilyGroup >= 3) { fDrawInstancedSupport = true; fNativeDrawIndirectSupport = true; } } fGpuTracingSupport = false; bool supportsMTLEvent = false; if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { supportsMTLEvent = true; } fSemaphoreSupport = supportsMTLEvent; fBackendSemaphoreSupport = fSemaphoreSupport; fFinishedProcAsyncCallbackSupport = true; fCrossContextTextureSupport = true; fHalfFloatVertexAttributeSupport = true; fDynamicStateArrayGeometryProcessorTextureSupport = true; } static bool format_is_srgb(MTLPixelFormat format) { switch (format) { case MTLPixelFormatRGBA8Unorm_sRGB: case MTLPixelFormatBGRA8Unorm_sRGB: return true; default: return false; } } bool GrMtlCaps::isFormatSRGB(const GrBackendFormat& format) const { return format_is_srgb(GrBackendFormatAsMTLPixelFormat(format)); } bool GrMtlCaps::isFormatTexturable(const GrBackendFormat& format, GrTextureType) const { MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); return this->isFormatTexturable(mtlFormat); } bool GrMtlCaps::isFormatTexturable(MTLPixelFormat format) const { const FormatInfo& formatInfo = this->getFormatInfo(format); return SkToBool(FormatInfo::kTexturable_Flag & formatInfo.fFlags); } bool GrMtlCaps::isFormatAsColorTypeRenderable(GrColorType ct, const GrBackendFormat& format, int sampleCount) const { if (!this->isFormatRenderable(format, sampleCount)) { return false; } MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); SkASSERT(mtlFormat != MTLPixelFormatInvalid); const auto& info = this->getFormatInfo(mtlFormat); if (!SkToBool(info.colorTypeFlags(ct) & ColorTypeInfo::kRenderable_Flag)) { return false; } return true; } bool GrMtlCaps::isFormatRenderable(const GrBackendFormat& format, int sampleCount) const { return this->isFormatRenderable(GrBackendFormatAsMTLPixelFormat(format), sampleCount); } bool GrMtlCaps::isFormatRenderable(MTLPixelFormat format, int sampleCount) const { return sampleCount <= this->maxRenderTargetSampleCount(format); } int GrMtlCaps::maxRenderTargetSampleCount(const GrBackendFormat& format) const { return this->maxRenderTargetSampleCount(GrBackendFormatAsMTLPixelFormat(format)); } int GrMtlCaps::maxRenderTargetSampleCount(MTLPixelFormat format) const { const FormatInfo& formatInfo = this->getFormatInfo(format); if (formatInfo.fFlags & FormatInfo::kMSAA_Flag) { return fSampleCounts[fSampleCounts.size() - 1]; } else if (formatInfo.fFlags & FormatInfo::kRenderable_Flag) { return 1; } return 0; } int GrMtlCaps::getRenderTargetSampleCount(int requestedCount, const GrBackendFormat& format) const { MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); return this->getRenderTargetSampleCount(requestedCount, mtlFormat); } int GrMtlCaps::getRenderTargetSampleCount(int requestedCount, MTLPixelFormat format) const { requestedCount = std::max(requestedCount, 1); const FormatInfo& formatInfo = this->getFormatInfo(format); if (!(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) { return 0; } if (formatInfo.fFlags & FormatInfo::kMSAA_Flag) { int count = fSampleCounts.size(); for (int i = 0; i < count; ++i) { if (fSampleCounts[i] >= requestedCount) { return fSampleCounts[i]; } } } return 1 == requestedCount ? 1 : 0; } void GrMtlCaps::initShaderCaps() { GrShaderCaps* shaderCaps = fShaderCaps.get(); // 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; // We haven't yet tested that using flat attributes perform well. shaderCaps->fPreferFlatInterpolation = true; shaderCaps->fShaderDerivativeSupport = true; shaderCaps->fExplicitTextureLodSupport = true; if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { shaderCaps->fDualSourceBlendingSupport = true; } else { shaderCaps->fDualSourceBlendingSupport = false; } // TODO(skia:8270): Re-enable this once bug 8270 is fixed. Will also need to remove asserts in // GrMtlPipelineStateBuilder which assert we aren't using this feature. #if 0 if (this->isIOS()) { shaderCaps->fFBFetchSupport = true; shaderCaps->fFBFetchNeedsCustomOutput = true; // ?? shaderCaps->fFBFetchColorName = ""; // Somehow add [[color(0)]] to arguments to frag shader } #endif shaderCaps->fDstReadInShaderSupport = shaderCaps->fFBFetchSupport; shaderCaps->fIntegerSupport = true; shaderCaps->fNonsquareMatrixSupport = true; shaderCaps->fInverseHyperbolicSupport = true; shaderCaps->fVertexIDSupport = true; shaderCaps->fInfinitySupport = true; shaderCaps->fNonconstantArrayIndexSupport = true; // Metal uses IEEE float and half floats so assuming those values here. shaderCaps->fFloatIs32Bits = true; shaderCaps->fHalfIs32Bits = false; shaderCaps->fMaxFragmentSamplers = 16; } void GrMtlCaps::applyDriverCorrectnessWorkarounds(const GrContextOptions&, const id) { // We don't have any active Metal workarounds. } // Define these so we can use them 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 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, MTLPixelFormatInvalid, }; void GrMtlCaps::setColorType(GrColorType colorType, std::initializer_list formats) { #ifdef SK_DEBUG for (size_t i = 0; i < kNumMtlFormats; ++i) { const auto& formatInfo = fFormatTable[i]; for (int j = 0; j < formatInfo.fColorTypeInfoCount; ++j) { const auto& ctInfo = formatInfo.fColorTypeInfos[j]; if (ctInfo.fColorType == colorType) { bool found = false; for (auto it = formats.begin(); it != formats.end(); ++it) { if (kMtlFormats[i] == *it) { found = true; } } SkASSERT(found); } } } #endif int idx = static_cast(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 GrMtlCaps::GetFormatIndex(MTLPixelFormat pixelFormat) { static_assert(std::size(kMtlFormats) == GrMtlCaps::kNumMtlFormats, "Size of kMtlFormats array must match static value in header"); for (size_t i = 0; i < GrMtlCaps::kNumMtlFormats; ++i) { if (kMtlFormats[i] == pixelFormat) { return i; } } SK_ABORT("Invalid MTLPixelFormat: %d", static_cast(pixelFormat)); } void GrMtlCaps::initFormatTable() { FormatInfo* info; if (@available(macos 11.0, *)) { SkASSERT(kMTLPixelFormatB5G6R5Unorm == MTLPixelFormatB5G6R5Unorm); SkASSERT(kMTLPixelFormatABGR4Unorm == MTLPixelFormatABGR4Unorm); SkASSERT(kMTLPixelFormatETC2_RGB8 == MTLPixelFormatETC2_RGB8); } // Format: R8Unorm { info = &fFormatTable[GetFormatIndex(MTLPixelFormatR8Unorm)]; info->fFlags = FormatInfo::kAllFlags; info->fColorTypeInfoCount = 3; info->fColorTypeInfos = std::make_unique(info->fColorTypeInfoCount); int ctIdx = 0; // Format: R8Unorm, Surface: kAlpha_8 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kR_8; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } // Format: R8Unorm, Surface: kAlpha_8 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kAlpha_8; 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 = GrColorType::kGray_8; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: A8Unorm, Surface: kAlpha_8 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kAlpha_8; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: B5G6R5Unorm, Surface: kBGR_565 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kBGR_565; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: ABGR4Unorm, Surface: kABGR_4444 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kABGR_4444; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } } } } // Format: RGBA8Unorm { info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA8Unorm)]; info->fFlags = FormatInfo::kAllFlags; info->fColorTypeInfoCount = 2; info->fColorTypeInfos = std::make_unique(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RGBA8Unorm, Surface: kRGBA_8888 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGBA_8888; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } // Format: RGBA8Unorm, Surface: kRGB_888x { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGB_888x; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag; ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1(); } } // Format: RG8Unorm { info = &fFormatTable[GetFormatIndex(MTLPixelFormatRG8Unorm)]; info->fFlags = FormatInfo::kAllFlags; info->fColorTypeInfoCount = 1; info->fColorTypeInfos = std::make_unique(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RG8Unorm, Surface: kRG_88 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRG_88; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: BGRA8Unorm, Surface: kBGRA_8888 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kBGRA_8888; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RGBA8Unorm_sRGB, Surface: kRGBA_8888_SRGB { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGBA_8888_SRGB; 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 = 2; info->fColorTypeInfos = std::make_unique(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RGB10A2Unorm, Surface: kRGBA_1010102 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGBA_1010102; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } // Format: RGB10A2Unorm, Surface: kRGB_101010x { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGB_101010x; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag; ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1(); } } // Format: BGR10A2Unorm if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { info = &fFormatTable[GetFormatIndex(MTLPixelFormatBGR10A2Unorm)]; if (this->isMac() && fFamilyGroup == 1) { info->fFlags = FormatInfo::kTexturable_Flag; } else { info->fFlags = FormatInfo::kAllFlags; } info->fColorTypeInfoCount = 1; info->fColorTypeInfos = std::make_unique(info->fColorTypeInfoCount); int ctIdx = 0; // Format: BGR10A2Unorm, Surface: kBGRA_1010102 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kBGRA_1010102; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: R16Float, Surface: kAlpha_F16 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kAlpha_F16; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; ctInfo.fReadSwizzle = skgpu::Swizzle("000r"); ctInfo.fWriteSwizzle = skgpu::Swizzle("a000"); } } // Format: RGBA16Float { info = &fFormatTable[GetFormatIndex(MTLPixelFormatRGBA16Float)]; info->fFlags = FormatInfo::kAllFlags; info->fColorTypeInfoCount = 3; info->fColorTypeInfos = std::make_unique(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RGBA16Float, Surface: kRGBA_F16 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGBA_F16; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } // Format: RGBA16Float, Surface: kRGBA_F16_Clamped { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGBA_F16_Clamped; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } // Format: RGBA16Float, Surface: kRGB_F16F16F16x { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGB_F16F16F16x; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag; ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1(); } } // 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: R16Unorm, Surface: kAlpha_16 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kAlpha_16; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RG16Unorm, Surface: kRG_1616 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRG_1616; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } } if (@available(macOS 11.0, iOS 8.0, tvOS 9.0, *)) { if (this->isApple()) { // ETC2_RGB8 info = &fFormatTable[GetFormatIndex(MTLPixelFormatETC2_RGB8)]; info->fFlags = FormatInfo::kTexturable_Flag; // NO supported colorTypes } } #ifdef SK_BUILD_FOR_MAC if (this->isMac()) { // BC1_RGBA info = &fFormatTable[GetFormatIndex(MTLPixelFormatBC1_RGBA)]; info->fFlags = FormatInfo::kTexturable_Flag; // NO supported colorTypes } #endif // 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RGBA16Unorm, Surface: kRGBA_16161616 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRGBA_16161616; 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(info->fColorTypeInfoCount); int ctIdx = 0; // Format: RG16Float, Surface: kRG_F16 { auto& ctInfo = info->fColorTypeInfos[ctIdx++]; ctInfo.fColorType = GrColorType::kRG_F16; ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag; } } //////////////////////////////////////////////////////////////////////////// // Map GrColorTypes (used for creating GrSurfaces) 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 GrcolorType. std::fill_n(fColorTypeToFormatTable, kGrColorTypeCnt, MTLPixelFormatInvalid); this->setColorType(GrColorType::kAlpha_8, { MTLPixelFormatR8Unorm, MTLPixelFormatA8Unorm }); if (@available(macOS 11.0, iOS 8.0, tvOS 9.0, *)) { if (this->isApple()) { this->setColorType(GrColorType::kBGR_565, { MTLPixelFormatB5G6R5Unorm }); this->setColorType(GrColorType::kABGR_4444, { MTLPixelFormatABGR4Unorm }); } } this->setColorType(GrColorType::kRGBA_8888, { MTLPixelFormatRGBA8Unorm }); this->setColorType(GrColorType::kRGBA_8888_SRGB, { MTLPixelFormatRGBA8Unorm_sRGB }); this->setColorType(GrColorType::kRGB_888x, { MTLPixelFormatRGBA8Unorm }); this->setColorType(GrColorType::kRG_88, { MTLPixelFormatRG8Unorm }); this->setColorType(GrColorType::kBGRA_8888, { MTLPixelFormatBGRA8Unorm }); this->setColorType(GrColorType::kRGBA_1010102, { MTLPixelFormatRGB10A2Unorm }); if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { this->setColorType(GrColorType::kBGRA_1010102, { MTLPixelFormatBGR10A2Unorm }); } this->setColorType(GrColorType::kRGB_101010x, { MTLPixelFormatRGB10A2Unorm }); this->setColorType(GrColorType::kGray_8, { MTLPixelFormatR8Unorm }); this->setColorType(GrColorType::kAlpha_F16, { MTLPixelFormatR16Float }); this->setColorType(GrColorType::kRGBA_F16, { MTLPixelFormatRGBA16Float }); this->setColorType(GrColorType::kRGBA_F16_Clamped, { MTLPixelFormatRGBA16Float }); this->setColorType(GrColorType::kRGB_F16F16F16x, { MTLPixelFormatRGBA16Float }); this->setColorType(GrColorType::kAlpha_16, { MTLPixelFormatR16Unorm }); this->setColorType(GrColorType::kRG_1616, { MTLPixelFormatRG16Unorm }); this->setColorType(GrColorType::kRGBA_16161616, { MTLPixelFormatRGBA16Unorm }); this->setColorType(GrColorType::kRG_F16, { MTLPixelFormatRG16Float }); } void GrMtlCaps::initStencilFormat(id physDev) { fPreferredStencilFormat = MTLPixelFormatStencil8; } bool GrMtlCaps::onSurfaceSupportsWritePixels(const GrSurface* surface) const { if (auto rt = surface->asRenderTarget()) { return rt->numSamples() <= 1 && SkToBool(surface->asTexture()); } return true; } GrCaps::SurfaceReadPixelsSupport GrMtlCaps::surfaceSupportsReadPixels( const GrSurface* surface) const { if (auto tex = static_cast(surface->asTexture())) { // We disallow reading back directly from compressed textures. if (skgpu::MtlFormatIsCompressed(tex->attachment()->mtlFormat())) { return SurfaceReadPixelsSupport::kCopyToTexture2D; } } if (auto mtlRT = static_cast(surface->asRenderTarget())) { if (mtlRT->numSamples() > 1 && !mtlRT->resolveAttachment()) { return SurfaceReadPixelsSupport::kCopyToTexture2D; } } return SurfaceReadPixelsSupport::kSupported; } GrCaps::DstCopyRestrictions GrMtlCaps::getDstCopyRestrictions(const GrRenderTargetProxy* src, GrColorType ct) const { // If the src is a MSAA RT then the only supported copy action (not considering falling back // to a draw) is to resolve from the MSAA src to the non-MSAA dst. Currently we only support // resolving the entire texture to a resolve buffer of the same size. DstCopyRestrictions restrictions = {}; if (auto rtProxy = src->asRenderTargetProxy()) { if (rtProxy->numSamples() > 1) { restrictions.fMustCopyWholeSrc = true; restrictions.fRectsMustMatch = GrSurfaceProxy::RectsMustMatch::kYes; } } return restrictions; } bool GrMtlCaps::onAreColorTypeAndFormatCompatible(GrColorType ct, const GrBackendFormat& format) const { MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); const auto& info = this->getFormatInfo(mtlFormat); for (int i = 0; i < info.fColorTypeInfoCount; ++i) { if (info.fColorTypeInfos[i].fColorType == ct) { return true; } } return false; } GrBackendFormat GrMtlCaps::onGetDefaultBackendFormat(GrColorType ct) const { MTLPixelFormat format = this->getFormatFromColorType(ct); if (!format) { return {}; } return GrBackendFormats::MakeMtl(format); } GrBackendFormat GrMtlCaps::getBackendFormatFromCompressionType( SkTextureCompressionType compressionType) const { switch (compressionType) { case SkTextureCompressionType::kNone: return {}; case SkTextureCompressionType::kETC2_RGB8_UNORM: if (@available(macOS 11.0, *)) { if (this->isApple()) { return GrBackendFormats::MakeMtl(MTLPixelFormatETC2_RGB8); } else { return {}; } } else { return {}; } case SkTextureCompressionType::kBC1_RGB8_UNORM: // Metal only supports the RGBA BC1 variant (see following) return {}; case SkTextureCompressionType::kBC1_RGBA8_UNORM: #ifdef SK_BUILD_FOR_MAC if (this->isMac()) { return GrBackendFormats::MakeMtl(MTLPixelFormatBC1_RGBA); } else { return {}; } #else return {}; #endif } SK_ABORT("Invalid compression type"); } skgpu::Swizzle GrMtlCaps::onGetReadSwizzle(const GrBackendFormat& format, GrColorType colorType) const { MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); SkASSERT(mtlFormat != MTLPixelFormatInvalid); const auto& info = this->getFormatInfo(mtlFormat); for (int i = 0; i < info.fColorTypeInfoCount; ++i) { const auto& ctInfo = info.fColorTypeInfos[i]; if (ctInfo.fColorType == colorType) { return ctInfo.fReadSwizzle; } } SkDEBUGFAILF("Illegal color type (%d) and format (%d) combination.", (int)colorType, static_cast(mtlFormat)); return {}; } skgpu::Swizzle GrMtlCaps::getWriteSwizzle(const GrBackendFormat& format, GrColorType colorType) const { MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); SkASSERT(mtlFormat != MTLPixelFormatInvalid); const auto& info = this->getFormatInfo(mtlFormat); for (int i = 0; i < info.fColorTypeInfoCount; ++i) { const auto& ctInfo = info.fColorTypeInfos[i]; if (ctInfo.fColorType == colorType) { return ctInfo.fWriteSwizzle; } } SkDEBUGFAILF("Illegal color type (%d) and format (%d) combination.", (int)colorType, static_cast(mtlFormat)); return {}; } uint64_t GrMtlCaps::computeFormatKey(const GrBackendFormat& format) const { MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); SkASSERT(mtlFormat != MTLPixelFormatInvalid); // 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)); return (uint64_t)mtlFormat; } GrCaps::SupportedWrite GrMtlCaps::supportedWritePixelsColorType( GrColorType surfaceColorType, const GrBackendFormat& surfaceFormat, GrColorType srcColorType) const { // Metal requires the destination offset for copyFromTexture to be a multiple of the textures // pixels size. size_t offsetAlignment = GrColorTypeBytesPerPixel(surfaceColorType); const auto& info = this->getFormatInfo(GrBackendFormatAsMTLPixelFormat(surfaceFormat)); for (int i = 0; i < info.fColorTypeInfoCount; ++i) { const auto& ctInfo = info.fColorTypeInfos[i]; if (ctInfo.fColorType == surfaceColorType) { return {surfaceColorType, offsetAlignment}; } } return {GrColorType::kUnknown, 0}; } GrCaps::SupportedRead GrMtlCaps::onSupportedReadPixelsColorType( GrColorType srcColorType, const GrBackendFormat& srcBackendFormat, GrColorType dstColorType) const { SkTextureCompressionType compression = GrBackendFormatToCompressionType(srcBackendFormat); if (compression != SkTextureCompressionType::kNone) { #ifdef SK_BUILD_FOR_IOS // Reading back to kRGB_888x doesn't work on Metal/iOS (skbug.com/9839) return { GrColorType::kUnknown, 0 }; #else return { SkTextureCompressionTypeIsOpaque(compression) ? GrColorType::kRGB_888x : GrColorType::kRGBA_8888, 0 }; #endif } // Metal requires the destination offset for copyFromTexture to be a multiple of the textures // pixels size. size_t offsetAlignment = GrColorTypeBytesPerPixel(srcColorType); MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(srcBackendFormat); const auto& info = this->getFormatInfo(mtlFormat); for (int i = 0; i < info.fColorTypeInfoCount; ++i) { const auto& ctInfo = info.fColorTypeInfos[i]; if (ctInfo.fColorType == srcColorType) { return {srcColorType, offsetAlignment}; } } return {GrColorType::kUnknown, 0}; } /** * For Metal we want to cache the entire pipeline for reuse of draws. The Desc here holds all * the information needed to differentiate one pipeline from another. * * The GrProgramDesc contains all the information need to create the actual shaders for the * pipeline. * * For Metal we need to add to the GrProgramDesc to include the rest of the state on the * pipeline. This includes blending information and primitive type. The pipeline is immutable * so any remaining dynamic state is set via the MtlRenderCmdEncoder. */ GrProgramDesc GrMtlCaps::makeDesc(GrRenderTarget*, const GrProgramInfo& programInfo, ProgramDescOverrideFlags overrideFlags) const { SkASSERT(overrideFlags == ProgramDescOverrideFlags::kNone); GrProgramDesc desc; GrProgramDesc::Build(&desc, programInfo, *this); skgpu::KeyBuilder b(desc.key()); // If ordering here is changed, update getStencilPixelFormat() below b.add32(GrBackendFormats::AsMtlFormat(programInfo.backendFormat())); b.add32(programInfo.numSamples()); b.add32(programInfo.needsStencil() ? this->preferredStencilFormat() : MTLPixelFormatInvalid); b.add32((uint32_t)programInfo.isStencilEnabled()); // Stencil samples don't seem to be tracked in the MTLRenderPipeline programInfo.pipeline().genKey(&b, *this); b.add32(programInfo.primitiveTypeKey()); b.flush(); return desc; } MTLPixelFormat GrMtlCaps::getStencilPixelFormat(const GrProgramDesc& desc) const { // Set up read buffer to point to platform-dependent part of the key SkReadBuffer readBuffer(desc.asKey() + desc.initialKeyLength()/sizeof(uint32_t), desc.keyLength() - desc.initialKeyLength()); // skip backend format readBuffer.readUInt(); // skip raster samples readBuffer.readUInt(); return (MTLPixelFormat) readBuffer.readUInt(); } bool GrMtlCaps::renderTargetSupportsDiscardableMSAA(const GrMtlRenderTarget* rt) const { return rt->resolveAttachment() && !rt->resolveAttachment()->framebufferOnly() && (rt->numSamples() > 1 && this->preferDiscardableMSAAAttachment()); } #if defined(GPU_TEST_UTILS) std::vector GrMtlCaps::getTestingCombinations() const { std::vector combos = { { GrColorType::kAlpha_8, GrBackendFormats::MakeMtl(MTLPixelFormatA8Unorm) }, { GrColorType::kAlpha_8, GrBackendFormats::MakeMtl(MTLPixelFormatR8Unorm) }, { GrColorType::kBGR_565, GrBackendFormats::MakeMtl(kMTLPixelFormatB5G6R5Unorm) }, { GrColorType::kABGR_4444, GrBackendFormats::MakeMtl(kMTLPixelFormatABGR4Unorm) }, { GrColorType::kRGBA_8888, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA8Unorm) }, { GrColorType::kRGBA_8888_SRGB, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA8Unorm_sRGB) }, { GrColorType::kRGB_888x, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA8Unorm) }, { GrColorType::kRGB_888x, GrBackendFormats::MakeMtl(kMTLPixelFormatETC2_RGB8) }, #ifdef SK_BUILD_FOR_MAC { GrColorType::kRGBA_8888, GrBackendFormats::MakeMtl(MTLPixelFormatBC1_RGBA) }, #endif { GrColorType::kRG_88, GrBackendFormats::MakeMtl(MTLPixelFormatRG8Unorm) }, { GrColorType::kBGRA_8888, GrBackendFormats::MakeMtl(MTLPixelFormatBGRA8Unorm) }, { GrColorType::kRGBA_1010102, GrBackendFormats::MakeMtl(MTLPixelFormatRGB10A2Unorm) }, { GrColorType::kBGRA_1010102, GrBackendFormats::MakeMtl(MTLPixelFormatBGR10A2Unorm) }, { GrColorType::kRGB_101010x, GrBackendFormats::MakeMtl(MTLPixelFormatRGB10A2Unorm) }, { GrColorType::kGray_8, GrBackendFormats::MakeMtl(MTLPixelFormatR8Unorm) }, { GrColorType::kAlpha_F16, GrBackendFormats::MakeMtl(MTLPixelFormatR16Float) }, { GrColorType::kRGBA_F16, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA16Float) }, { GrColorType::kRGBA_F16_Clamped, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA16Float) }, { GrColorType::kRGB_F16F16F16x, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA16Float) }, { GrColorType::kAlpha_16, GrBackendFormats::MakeMtl(MTLPixelFormatR16Unorm) }, { GrColorType::kRG_1616, GrBackendFormats::MakeMtl(MTLPixelFormatRG16Unorm) }, { GrColorType::kRGBA_16161616, GrBackendFormats::MakeMtl(MTLPixelFormatRGBA16Unorm) }, { GrColorType::kRG_F16, GrBackendFormats::MakeMtl(MTLPixelFormatRG16Float) }, }; return combos; } #endif #ifdef SK_ENABLE_DUMP_GPU #include "src/utils/SkJSONWriter.h" void GrMtlCaps::onDumpJSON(SkJSONWriter* writer) const { // We are called by the base class, which has already called beginObject(). We choose to nest // all of our caps information in a named sub-object. writer->beginObject("Metal caps"); writer->beginObject("Preferred Stencil Format"); writer->appendS32("stencil bits", GrMtlFormatStencilBits(fPreferredStencilFormat)); writer->appendS32("total bytes", skgpu::MtlFormatBytesPerBlock(fPreferredStencilFormat)); writer->endObject(); switch (fGPUFamily) { case GPUFamily::kMac: writer->appendNString("GPU Family", "Mac"); break; case GPUFamily::kApple: writer->appendNString("GPU Family", "Apple"); break; default: writer->appendNString("GPU Family", "unknown"); break; } writer->appendS32("Family Group", fFamilyGroup); writer->beginArray("Sample Counts"); for (int v : fSampleCounts) { writer->appendS32(nullptr, v); } writer->endArray(); writer->endObject(); } #else void GrMtlCaps::onDumpJSON(SkJSONWriter* writer) const { } #endif GR_NORETAIN_END