Home | History | Annotate | Download | only in mtl
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrMtlCaps.h"
      9 
     10 #include "GrBackendSurface.h"
     11 #include "GrMtlUtil.h"
     12 #include "GrRenderTargetProxy.h"
     13 #include "GrShaderCaps.h"
     14 #include "GrSurfaceProxy.h"
     15 #include "SkRect.h"
     16 
     17 GrMtlCaps::GrMtlCaps(const GrContextOptions& contextOptions, const id<MTLDevice> device,
     18                      MTLFeatureSet featureSet)
     19         : INHERITED(contextOptions) {
     20     fShaderCaps.reset(new GrShaderCaps(contextOptions));
     21 
     22     this->initFeatureSet(featureSet);
     23     this->initGrCaps(device);
     24     this->initShaderCaps();
     25     this->initConfigTable();
     26     this->initStencilFormat(device);
     27 
     28     this->applyOptionsOverrides(contextOptions);
     29     fShaderCaps->applyOptionsOverrides(contextOptions);
     30 
     31     // The following are disabled due to the unfinished Metal backend, not because Metal itself
     32     // doesn't support it.
     33     fBlacklistCoverageCounting = true;   // CCPR shaders have some incompatabilities with SkSLC
     34     fFenceSyncSupport = false;           // Fences are not implemented yet
     35     fMipMapSupport = false;              // GrMtlGpu::onRegenerateMipMapLevels() not implemented
     36     fMultisampleDisableSupport = true;   // MSAA and resolving not implemented yet
     37     fDiscardRenderTargetSupport = false; // GrMtlGpuCommandBuffer::discard() not implemented
     38     fCrossContextTextureSupport = false; // GrMtlGpu::prepareTextureForCrossContextUsage() not impl
     39 }
     40 
     41 void GrMtlCaps::initFeatureSet(MTLFeatureSet featureSet) {
     42     // Mac OSX
     43 #ifdef SK_BUILD_FOR_MAC
     44     if (MTLFeatureSet_OSX_GPUFamily1_v2 == featureSet) {
     45         fPlatform = Platform::kMac;
     46         fFamilyGroup = 1;
     47         fVersion = 2;
     48         return;
     49     }
     50     if (MTLFeatureSet_OSX_GPUFamily1_v1 == featureSet) {
     51         fPlatform = Platform::kMac;
     52         fFamilyGroup = 1;
     53         fVersion = 1;
     54         return;
     55     }
     56 #endif
     57 
     58     // iOS Family group 3
     59 #ifdef SK_BUILD_FOR_IOS
     60     if (MTLFeatureSet_iOS_GPUFamily3_v2 == featureSet) {
     61         fPlatform = Platform::kIOS;
     62         fFamilyGroup = 3;
     63         fVersion = 2;
     64         return;
     65     }
     66     if (MTLFeatureSet_iOS_GPUFamily3_v1 == featureSet) {
     67         fPlatform = Platform::kIOS;
     68         fFamilyGroup = 3;
     69         fVersion = 1;
     70         return;
     71     }
     72 
     73     // iOS Family group 2
     74     if (MTLFeatureSet_iOS_GPUFamily2_v3 == featureSet) {
     75         fPlatform = Platform::kIOS;
     76         fFamilyGroup = 2;
     77         fVersion = 3;
     78         return;
     79     }
     80     if (MTLFeatureSet_iOS_GPUFamily2_v2 == featureSet) {
     81         fPlatform = Platform::kIOS;
     82         fFamilyGroup = 2;
     83         fVersion = 2;
     84         return;
     85     }
     86     if (MTLFeatureSet_iOS_GPUFamily2_v1 == featureSet) {
     87         fPlatform = Platform::kIOS;
     88         fFamilyGroup = 2;
     89         fVersion = 1;
     90         return;
     91     }
     92 
     93     // iOS Family group 1
     94     if (MTLFeatureSet_iOS_GPUFamily1_v3 == featureSet) {
     95         fPlatform = Platform::kIOS;
     96         fFamilyGroup = 1;
     97         fVersion = 3;
     98         return;
     99     }
    100     if (MTLFeatureSet_iOS_GPUFamily1_v2 == featureSet) {
    101         fPlatform = Platform::kIOS;
    102         fFamilyGroup = 1;
    103         fVersion = 2;
    104         return;
    105     }
    106     if (MTLFeatureSet_iOS_GPUFamily1_v1 == featureSet) {
    107         fPlatform = Platform::kIOS;
    108         fFamilyGroup = 1;
    109         fVersion = 1;
    110         return;
    111     }
    112 #endif
    113     // No supported feature sets were found
    114     SK_ABORT("Requested an unsupported feature set");
    115 }
    116 
    117 bool GrMtlCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCount,
    118                               GrSurfaceOrigin dstOrigin,
    119                               GrPixelConfig srcConfig, int srcSampleCount,
    120                               GrSurfaceOrigin srcOrigin,
    121                               const SkIRect& srcRect, const SkIPoint& dstPoint,
    122                               bool areDstSrcSameObj) const {
    123     if (dstConfig != srcConfig) {
    124         return false;
    125     }
    126     if ((dstSampleCount > 1 || srcSampleCount > 1) && (dstSampleCount != srcSampleCount)) {
    127         return false;
    128     }
    129     if (dstOrigin != srcOrigin) {
    130         return false;
    131     }
    132     if (areDstSrcSameObj) {
    133         SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
    134                                             srcRect.width(), srcRect.height());
    135         if (dstRect.intersect(srcRect)) {
    136             return false;
    137         }
    138     }
    139     return true;
    140 }
    141 
    142 bool GrMtlCaps::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
    143                               GrPixelConfig srcConfig, bool srcIsTextureable) const {
    144     // TODO: Make copySurfaceAsDraw handle the swizzle
    145     if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
    146         this->shaderCaps()->configOutputSwizzle(dstConfig)) {
    147         return false;
    148     }
    149 
    150     if (!dstIsRenderable || !srcIsTextureable) {
    151         return false;
    152     }
    153     return true;
    154 }
    155 
    156 bool GrMtlCaps::canCopyAsDrawThenBlit(GrPixelConfig dstConfig, GrPixelConfig srcConfig,
    157                                       bool srcIsTextureable) const {
    158     // TODO: Make copySurfaceAsDraw handle the swizzle
    159     if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
    160         this->shaderCaps()->configOutputSwizzle(dstConfig)) {
    161         return false;
    162     }
    163     if (!srcIsTextureable) {
    164         return false;
    165     }
    166     return true;
    167 }
    168 
    169 bool GrMtlCaps::onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
    170                                  const SkIRect& srcRect, const SkIPoint& dstPoint) const {
    171     GrSurfaceOrigin dstOrigin = dst->origin();
    172     GrSurfaceOrigin srcOrigin = src->origin();
    173 
    174     int dstSampleCnt = 0;
    175     int srcSampleCnt = 0;
    176     if (const GrRenderTargetProxy* rtProxy = dst->asRenderTargetProxy()) {
    177         dstSampleCnt = rtProxy->numColorSamples();
    178     }
    179     if (const GrRenderTargetProxy* rtProxy = src->asRenderTargetProxy()) {
    180         srcSampleCnt = rtProxy->numColorSamples();
    181     }
    182     SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
    183     SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
    184 
    185     return this->canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
    186                                src->config(), srcSampleCnt, srcOrigin,
    187                                srcRect, dstPoint, dst == src) ||
    188            this->canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTargetProxy()),
    189                                src->config(), SkToBool(src->asTextureProxy())) ||
    190            this->canCopyAsDrawThenBlit(dst->config(), src->config(),
    191                                        SkToBool(src->asTextureProxy()));
    192 }
    193 
    194 void GrMtlCaps::initGrCaps(const id<MTLDevice> device) {
    195     // Max vertex attribs is the same on all devices
    196     fMaxVertexAttributes = 31;
    197 
    198     // Metal does not support scissor + clear
    199     fPerformPartialClearsAsDraws = true;
    200 
    201     // RenderTarget and Texture size
    202     if (this->isMac()) {
    203         fMaxRenderTargetSize = 16384;
    204     } else {
    205         if (3 == fFamilyGroup) {
    206             fMaxRenderTargetSize = 16384;
    207         } else {
    208             // Family group 1 and 2 support 8192 for version 2 and above, 4096 for v1
    209             if (1 == fVersion) {
    210                 fMaxRenderTargetSize = 4096;
    211             } else {
    212                 fMaxRenderTargetSize = 8192;
    213             }
    214         }
    215     }
    216     fMaxPreferredRenderTargetSize = fMaxRenderTargetSize;
    217     fMaxTextureSize = fMaxRenderTargetSize;
    218 
    219     // Init sample counts. All devices support 1 (i.e. 0 in skia).
    220     fSampleCounts.push_back(1);
    221     for (auto sampleCnt : {2, 4, 8}) {
    222         if ([device supportsTextureSampleCount:sampleCnt]) {
    223             fSampleCounts.push_back(sampleCnt);
    224         }
    225     }
    226 
    227     // Clamp to border is supported on Mac 10.12 and higher (gpu family.version >= 1.2). It is not
    228     // supported on iOS.
    229     if (this->isMac()) {
    230         if (fFamilyGroup == 1 && fVersion < 2) {
    231             fClampToBorderSupport = false;
    232         }
    233     } else {
    234         fClampToBorderSupport = false;
    235     }
    236 
    237     // Starting with the assumption that there isn't a reason to not map small buffers.
    238     fBufferMapThreshold = 0;
    239 
    240     // Buffers are always fully mapped.
    241     fMapBufferFlags = kCanMap_MapFlag;
    242 
    243     fOversizedStencilSupport = true;
    244 
    245     fSRGBSupport = true;   // always available in Metal
    246     fSRGBWriteControl = false;
    247     fMipMapSupport = true;   // always available in Metal
    248     fNPOTTextureTileSupport = true;  // always available in Metal
    249     fDiscardRenderTargetSupport = true;
    250 
    251     fReuseScratchTextures = true; // Assuming this okay
    252 
    253     fTextureBarrierSupport = false; // Need to figure out if we can do this
    254 
    255     fSampleLocationsSupport = false;
    256     fMultisampleDisableSupport = false;
    257 
    258     if (this->isMac() || 3 == fFamilyGroup) {
    259         fInstanceAttribSupport = true;
    260     }
    261 
    262     fUsesMixedSamples = false;
    263     fGpuTracingSupport = false;
    264 
    265     fFenceSyncSupport = true;   // always available in Metal
    266     fCrossContextTextureSupport = false;
    267     fHalfFloatVertexAttributeSupport = true;
    268 }
    269 
    270 
    271 int GrMtlCaps::maxRenderTargetSampleCount(GrPixelConfig config) const {
    272     if (fConfigTable[config].fFlags & ConfigInfo::kMSAA_Flag) {
    273         return fSampleCounts[fSampleCounts.count() - 1];
    274     } else if (fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag) {
    275         return 1;
    276     }
    277     return 0;
    278 }
    279 
    280 int GrMtlCaps::getRenderTargetSampleCount(int requestedCount, GrPixelConfig config) const {
    281     requestedCount = SkTMax(requestedCount, 1);
    282     if (fConfigTable[config].fFlags & ConfigInfo::kMSAA_Flag) {
    283         int count = fSampleCounts.count();
    284         for (int i = 0; i < count; ++i) {
    285             if (fSampleCounts[i] >= requestedCount) {
    286                 return fSampleCounts[i];
    287             }
    288         }
    289     } else if (fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag) {
    290         return 1 == requestedCount ? 1 : 0;
    291     }
    292     return 0;
    293 }
    294 
    295 void GrMtlCaps::initShaderCaps() {
    296     GrShaderCaps* shaderCaps = fShaderCaps.get();
    297 
    298     // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config.
    299     for (int i = 0; i < kGrPixelConfigCnt; ++i) {
    300         GrPixelConfig config = static_cast<GrPixelConfig>(i);
    301         if (GrPixelConfigIsAlphaOnly(config)) {
    302             shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR();
    303             shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA();
    304         } else {
    305             if (kGray_8_GrPixelConfig == config) {
    306                 shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRA();
    307             } else {
    308                 shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA();
    309             }
    310         }
    311     }
    312 
    313     // Setting this true with the assumption that this cap will eventually mean we support varying
    314     // precisions and not just via modifiers.
    315     shaderCaps->fUsesPrecisionModifiers = true;
    316     shaderCaps->fFlatInterpolationSupport = true;
    317     // We haven't yet tested that using flat attributes perform well.
    318     shaderCaps->fPreferFlatInterpolation = true;
    319 
    320     shaderCaps->fShaderDerivativeSupport = true;
    321     shaderCaps->fGeometryShaderSupport = false;
    322 
    323     if ((this->isMac() && fVersion >= 2) ||
    324         (this->isIOS() && ((1 == fFamilyGroup && 4 == fVersion) ||
    325                            (2 == fFamilyGroup && 4 == fVersion) ||
    326                            (3 == fFamilyGroup && 3 == fVersion)))) {
    327         shaderCaps->fDualSourceBlendingSupport = true;
    328     }
    329 
    330     // TODO: Re-enable this once skbug:8720 is fixed. Will also need to remove asserts in
    331     // GrMtlPipelineStateBuilder which assert we aren't using this feature.
    332 #if 0
    333     if (this->isIOS()) {
    334         shaderCaps->fFBFetchSupport = true;
    335         shaderCaps->fFBFetchNeedsCustomOutput = true; // ??
    336         shaderCaps->fFBFetchColorName = ""; // Somehow add [[color(0)]] to arguments to frag shader
    337     }
    338 #endif
    339     shaderCaps->fDstReadInShaderSupport = shaderCaps->fFBFetchSupport;
    340 
    341     shaderCaps->fIntegerSupport = true;
    342     shaderCaps->fVertexIDSupport = false;
    343     shaderCaps->fImageLoadStoreSupport = false;
    344 
    345     // Metal uses IEEE float and half floats so assuming those values here.
    346     shaderCaps->fFloatIs32Bits = true;
    347     shaderCaps->fHalfIs32Bits = false;
    348 
    349     // Metal supports unsigned integers.
    350     shaderCaps->fUnsignedSupport = true;
    351 
    352     shaderCaps->fMaxFragmentSamplers = 16;
    353 }
    354 
    355 void GrMtlCaps::initConfigTable() {
    356     ConfigInfo* info;
    357     // Alpha_8 uses R8Unorm
    358     info = &fConfigTable[kAlpha_8_GrPixelConfig];
    359     info->fFlags = ConfigInfo::kAllFlags;
    360 
    361     // Gray_8 uses R8Unorm
    362     info = &fConfigTable[kGray_8_GrPixelConfig];
    363     info->fFlags = ConfigInfo::kAllFlags;
    364 
    365     // RGB_565 uses B5G6R5Unorm, even though written opposite this format packs how we want
    366     info = &fConfigTable[kRGB_565_GrPixelConfig];
    367     if (this->isMac()) {
    368         info->fFlags = 0;
    369     } else {
    370         info->fFlags = ConfigInfo::kAllFlags;
    371     }
    372 
    373     // RGBA_4444 uses ABGR4Unorm
    374     info = &fConfigTable[kRGBA_4444_GrPixelConfig];
    375     if (this->isMac()) {
    376         info->fFlags = 0;
    377     } else {
    378         info->fFlags = ConfigInfo::kAllFlags;
    379     }
    380 
    381     // RGBA_8888 uses RGBA8Unorm
    382     info = &fConfigTable[kRGBA_8888_GrPixelConfig];
    383     info->fFlags = ConfigInfo::kAllFlags;
    384 
    385     // BGRA_8888 uses BGRA8Unorm
    386     info = &fConfigTable[kBGRA_8888_GrPixelConfig];
    387     info->fFlags = ConfigInfo::kAllFlags;
    388 
    389     // SRGBA_8888 uses RGBA8Unorm_sRGB
    390     info = &fConfigTable[kSRGBA_8888_GrPixelConfig];
    391     info->fFlags = ConfigInfo::kAllFlags;
    392 
    393     // SBGRA_8888 uses BGRA8Unorm_sRGB
    394     info = &fConfigTable[kSBGRA_8888_GrPixelConfig];
    395     info->fFlags = ConfigInfo::kAllFlags;
    396 
    397     // RGBA_float uses RGBA32Float
    398     info = &fConfigTable[kRGBA_float_GrPixelConfig];
    399     if (this->isMac()) {
    400         info->fFlags = ConfigInfo::kAllFlags;
    401     } else {
    402         info->fFlags = 0;
    403     }
    404 
    405     // RG_float uses RG32Float
    406     info = &fConfigTable[kRG_float_GrPixelConfig];
    407     if (this->isMac()) {
    408         info->fFlags = ConfigInfo::kAllFlags;
    409     } else {
    410         info->fFlags = ConfigInfo::kRenderable_Flag;
    411     }
    412 
    413     // Alpha_half uses R16Float
    414     info = &fConfigTable[kAlpha_half_GrPixelConfig];
    415     info->fFlags = ConfigInfo::kAllFlags;
    416 
    417     // RGBA_half uses RGBA16Float
    418     info = &fConfigTable[kRGBA_half_GrPixelConfig];
    419     info->fFlags = ConfigInfo::kAllFlags;
    420 }
    421 
    422 void GrMtlCaps::initStencilFormat(id<MTLDevice> physDev) {
    423     fPreferredStencilFormat = StencilFormat{ MTLPixelFormatStencil8, 8, 8, true };
    424 }
    425 
    426 GrBackendFormat GrMtlCaps::getBackendFormatFromGrColorType(GrColorType ct,
    427                                                            GrSRGBEncoded srgbEncoded) const {
    428     GrPixelConfig config = GrColorTypeToPixelConfig(ct, srgbEncoded);
    429     if (config == kUnknown_GrPixelConfig) {
    430         return GrBackendFormat();
    431     }
    432     MTLPixelFormat format;
    433     if (!GrPixelConfigToMTLFormat(config, &format)) {
    434         return GrBackendFormat();
    435     }
    436     return GrBackendFormat::MakeMtl(format);
    437 }
    438 
    439