blob: 880b0faacac19132844422eabd6e189034075b9c [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 "GrMtlCaps.h"
#include "GrBackendSurface.h"
#include "GrMtlUtil.h"
#include "GrRenderTargetProxy.h"
#include "GrShaderCaps.h"
#include "GrSurfaceProxy.h"
#include "SkRect.h"
GrMtlCaps::GrMtlCaps(const GrContextOptions& contextOptions, const id<MTLDevice> device,
MTLFeatureSet featureSet)
: INHERITED(contextOptions) {
fShaderCaps.reset(new GrShaderCaps(contextOptions));
this->initFeatureSet(featureSet);
this->initGrCaps(device);
this->initShaderCaps();
this->initConfigTable();
this->initStencilFormat(device);
this->applyOptionsOverrides(contextOptions);
fShaderCaps->applyOptionsOverrides(contextOptions);
// The following are disabled due to the unfinished Metal backend, not because Metal itself
// doesn't support it.
fBlacklistCoverageCounting = true; // CCPR shaders have some incompatabilities with SkSLC
fFenceSyncSupport = false; // Fences are not implemented yet
fMipMapSupport = false; // GrMtlGpu::onRegenerateMipMapLevels() not implemented
fMultisampleDisableSupport = true; // MSAA and resolving not implemented yet
fDiscardRenderTargetSupport = false; // GrMtlGpuCommandBuffer::discard() not implemented
fCrossContextTextureSupport = false; // GrMtlGpu::prepareTextureForCrossContextUsage() not impl
}
void GrMtlCaps::initFeatureSet(MTLFeatureSet featureSet) {
// Mac OSX
#ifdef SK_BUILD_FOR_MAC
if (MTLFeatureSet_OSX_GPUFamily1_v2 == featureSet) {
fPlatform = Platform::kMac;
fFamilyGroup = 1;
fVersion = 2;
return;
}
if (MTLFeatureSet_OSX_GPUFamily1_v1 == featureSet) {
fPlatform = Platform::kMac;
fFamilyGroup = 1;
fVersion = 1;
return;
}
#endif
// iOS Family group 3
#ifdef SK_BUILD_FOR_IOS
if (MTLFeatureSet_iOS_GPUFamily3_v2 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 3;
fVersion = 2;
return;
}
if (MTLFeatureSet_iOS_GPUFamily3_v1 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 3;
fVersion = 1;
return;
}
// iOS Family group 2
if (MTLFeatureSet_iOS_GPUFamily2_v3 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 2;
fVersion = 3;
return;
}
if (MTLFeatureSet_iOS_GPUFamily2_v2 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 2;
fVersion = 2;
return;
}
if (MTLFeatureSet_iOS_GPUFamily2_v1 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 2;
fVersion = 1;
return;
}
// iOS Family group 1
if (MTLFeatureSet_iOS_GPUFamily1_v3 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 1;
fVersion = 3;
return;
}
if (MTLFeatureSet_iOS_GPUFamily1_v2 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 1;
fVersion = 2;
return;
}
if (MTLFeatureSet_iOS_GPUFamily1_v1 == featureSet) {
fPlatform = Platform::kIOS;
fFamilyGroup = 1;
fVersion = 1;
return;
}
#endif
// No supported feature sets were found
SK_ABORT("Requested an unsupported feature set");
}
bool GrMtlCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCount,
GrSurfaceOrigin dstOrigin,
GrPixelConfig srcConfig, int srcSampleCount,
GrSurfaceOrigin srcOrigin,
const SkIRect& srcRect, const SkIPoint& dstPoint,
bool areDstSrcSameObj) const {
if (dstConfig != srcConfig) {
return false;
}
if ((dstSampleCount > 1 || srcSampleCount > 1) && (dstSampleCount != srcSampleCount)) {
return false;
}
if (dstOrigin != srcOrigin) {
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::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
GrPixelConfig srcConfig, bool srcIsTextureable) const {
// TODO: Make copySurfaceAsDraw handle the swizzle
if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
this->shaderCaps()->configOutputSwizzle(dstConfig)) {
return false;
}
if (!dstIsRenderable || !srcIsTextureable) {
return false;
}
return true;
}
bool GrMtlCaps::canCopyAsDrawThenBlit(GrPixelConfig dstConfig, GrPixelConfig srcConfig,
bool srcIsTextureable) const {
// TODO: Make copySurfaceAsDraw handle the swizzle
if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
this->shaderCaps()->configOutputSwizzle(dstConfig)) {
return false;
}
if (!srcIsTextureable) {
return false;
}
return true;
}
bool GrMtlCaps::onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
const SkIRect& srcRect, const SkIPoint& dstPoint) const {
GrSurfaceOrigin dstOrigin = dst->origin();
GrSurfaceOrigin srcOrigin = src->origin();
int dstSampleCnt = 0;
int srcSampleCnt = 0;
if (const GrRenderTargetProxy* rtProxy = dst->asRenderTargetProxy()) {
dstSampleCnt = rtProxy->numColorSamples();
}
if (const GrRenderTargetProxy* rtProxy = src->asRenderTargetProxy()) {
srcSampleCnt = rtProxy->numColorSamples();
}
SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
return this->canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
src->config(), srcSampleCnt, srcOrigin,
srcRect, dstPoint, dst == src) ||
this->canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTargetProxy()),
src->config(), SkToBool(src->asTextureProxy())) ||
this->canCopyAsDrawThenBlit(dst->config(), src->config(),
SkToBool(src->asTextureProxy()));
}
void GrMtlCaps::initGrCaps(const id<MTLDevice> device) {
// Max vertex attribs is the same on all devices
fMaxVertexAttributes = 31;
// RenderTarget and Texture size
if (this->isMac()) {
fMaxRenderTargetSize = 16384;
} else {
if (3 == fFamilyGroup) {
fMaxRenderTargetSize = 16384;
} else {
// Family group 1 and 2 support 8192 for version 2 and above, 4096 for v1
if (1 == fVersion) {
fMaxRenderTargetSize = 4096;
} else {
fMaxRenderTargetSize = 8192;
}
}
}
fMaxPreferredRenderTargetSize = fMaxRenderTargetSize;
fMaxTextureSize = fMaxRenderTargetSize;
// Init sample counts. All devices support 1 (i.e. 0 in skia).
fSampleCounts.push_back(1);
for (auto sampleCnt : {2, 4, 8}) {
if ([device supportsTextureSampleCount:sampleCnt]) {
fSampleCounts.push_back(sampleCnt);
}
}
// 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;
fOversizedStencilSupport = true;
// Looks like there is a field called rasterSampleCount labeled as beta in the Metal docs. This
// may be what we eventually need here, but it has no description.
fSampleShadingSupport = false;
fSRGBSupport = true; // always available in Metal
fSRGBWriteControl = false;
fMipMapSupport = true; // always available in Metal
fNPOTTextureTileSupport = true; // always available in Metal
fDiscardRenderTargetSupport = true;
fReuseScratchTextures = true; // Assuming this okay
fTextureBarrierSupport = false; // Need to figure out if we can do this
fSampleLocationsSupport = false;
fMultisampleDisableSupport = false;
if (this->isMac() || 3 == fFamilyGroup) {
fInstanceAttribSupport = true;
}
fUsesMixedSamples = false;
fGpuTracingSupport = false;
fFenceSyncSupport = true; // always available in Metal
fCrossContextTextureSupport = false;
fHalfFloatVertexAttributeSupport = true;
}
int GrMtlCaps::maxRenderTargetSampleCount(GrPixelConfig config) const {
if (fConfigTable[config].fFlags & ConfigInfo::kMSAA_Flag) {
return fSampleCounts[fSampleCounts.count() - 1];
} else if (fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag) {
return 1;
}
return 0;
}
int GrMtlCaps::getRenderTargetSampleCount(int requestedCount, GrPixelConfig config) const {
requestedCount = SkTMax(requestedCount, 1);
if (fConfigTable[config].fFlags & ConfigInfo::kMSAA_Flag) {
int count = fSampleCounts.count();
for (int i = 0; i < count; ++i) {
if (fSampleCounts[i] >= requestedCount) {
return fSampleCounts[i];
}
}
} else if (fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag) {
return 1 == requestedCount ? 1 : 0;
}
return 0;
}
void GrMtlCaps::initShaderCaps() {
GrShaderCaps* shaderCaps = fShaderCaps.get();
// fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config.
for (int i = 0; i < kGrPixelConfigCnt; ++i) {
GrPixelConfig config = static_cast<GrPixelConfig>(i);
if (GrPixelConfigIsAlphaOnly(config)) {
shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR();
shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA();
} else {
if (kGray_8_GrPixelConfig == config) {
shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRA();
} else {
shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA();
}
}
}
// 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->fGeometryShaderSupport = false;
if ((this->isMac() && fVersion >= 2) ||
(this->isIOS() && ((1 == fFamilyGroup && 4 == fVersion) ||
(2 == fFamilyGroup && 4 == fVersion) ||
(3 == fFamilyGroup && 3 == fVersion)))) {
shaderCaps->fDualSourceBlendingSupport = true;
}
// TODO: Re-enable this once skbug:8720 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->fVertexIDSupport = false;
shaderCaps->fImageLoadStoreSupport = false;
// Metal uses IEEE float and half floats so assuming those values here.
shaderCaps->fFloatIs32Bits = true;
shaderCaps->fHalfIs32Bits = false;
// Metal supports unsigned integers.
shaderCaps->fUnsignedSupport = true;
shaderCaps->fMaxFragmentSamplers = 16;
}
void GrMtlCaps::initConfigTable() {
ConfigInfo* info;
// Alpha_8 uses R8Unorm
info = &fConfigTable[kAlpha_8_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// Gray_8 uses R8Unorm
info = &fConfigTable[kGray_8_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// RGB_565 uses B5G6R5Unorm, even though written opposite this format packs how we want
info = &fConfigTable[kRGB_565_GrPixelConfig];
if (this->isMac()) {
info->fFlags = 0;
} else {
info->fFlags = ConfigInfo::kAllFlags;
}
// RGBA_4444 uses ABGR4Unorm
info = &fConfigTable[kRGBA_4444_GrPixelConfig];
if (this->isMac()) {
info->fFlags = 0;
} else {
info->fFlags = ConfigInfo::kAllFlags;
}
// RGBA_8888 uses RGBA8Unorm
info = &fConfigTable[kRGBA_8888_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// BGRA_8888 uses BGRA8Unorm
info = &fConfigTable[kBGRA_8888_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// SRGBA_8888 uses RGBA8Unorm_sRGB
info = &fConfigTable[kSRGBA_8888_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// SBGRA_8888 uses BGRA8Unorm_sRGB
info = &fConfigTable[kSBGRA_8888_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// RGBA_float uses RGBA32Float
info = &fConfigTable[kRGBA_float_GrPixelConfig];
if (this->isMac()) {
info->fFlags = ConfigInfo::kAllFlags;
} else {
info->fFlags = 0;
}
// RG_float uses RG32Float
info = &fConfigTable[kRG_float_GrPixelConfig];
if (this->isMac()) {
info->fFlags = ConfigInfo::kAllFlags;
} else {
info->fFlags = ConfigInfo::kRenderable_Flag;
}
// Alpha_half uses R16Float
info = &fConfigTable[kAlpha_half_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
// RGBA_half uses RGBA16Float
info = &fConfigTable[kRGBA_half_GrPixelConfig];
info->fFlags = ConfigInfo::kAllFlags;
}
void GrMtlCaps::initStencilFormat(id<MTLDevice> physDev) {
fPreferredStencilFormat = StencilFormat{ MTLPixelFormatStencil8, 8, 8, true };
}
GrBackendFormat GrMtlCaps::onCreateFormatFromBackendTexture(
const GrBackendTexture& backendTex) const {
GrMtlTextureInfo mtlInfo;
SkAssertResult(backendTex.getMtlTextureInfo(&mtlInfo));
id<MTLTexture> mtlTexture = GrGetMTLTexture(mtlInfo.fTexture,
GrWrapOwnership::kBorrow_GrWrapOwnership);
return GrBackendFormat::MakeMtl(mtlTexture.pixelFormat);
}
GrBackendFormat GrMtlCaps::getBackendFormatFromGrColorType(GrColorType ct,
GrSRGBEncoded srgbEncoded) const {
GrPixelConfig config = GrColorTypeToPixelConfig(ct, srgbEncoded);
if (config == kUnknown_GrPixelConfig) {
return GrBackendFormat();
}
MTLPixelFormat format;
if (!GrPixelConfigToMTLFormat(config, &format)) {
return GrBackendFormat();
}
return GrBackendFormat::MakeMtl(format);
}