blob: 7a24770d32a3e545f4d82417cd927aad2362c35a [file] [log] [blame]
/*
* 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/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(GR_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<MTLDevice> device)
: INHERITED(contextOptions) {
fShaderCaps = std::make_unique<GrShaderCaps>();
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<MTLDevice> 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<MTLDevice> 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
#if GR_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 GrMtlCaps::initGPUFamily(id<MTLDevice> 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<MTLDevice> device) {
#if defined(GR_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<MTLDevice>) {
// 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<MTLPixelFormat> 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<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 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<int>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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 = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(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: 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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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 = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(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: 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: 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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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<ColorTypeInfo[]>(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::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::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<MTLDevice> 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<const GrMtlTexture*>(surface->asTexture())) {
// We disallow reading back directly from compressed textures.
if (skgpu::MtlFormatIsCompressed(tex->attachment()->mtlFormat())) {
return SurfaceReadPixelsSupport::kCopyToTexture2D;
}
}
if (auto mtlRT = static_cast<const GrMtlRenderTarget*>(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<int>(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<int>(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(GR_TEST_UTILS)
std::vector<GrTest::TestFormatColorTypeCombination> GrMtlCaps::getTestingCombinations() const {
std::vector<GrTest::TestFormatColorTypeCombination> 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::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::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