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
17GrMtlCaps::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
41void 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
117bool 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
142bool 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
156bool 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
169bool 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
194void 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
271int 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
280int 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
295void 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 if (kRGB_888X_GrPixelConfig == config) {
308                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGB1();
309            } else {
310                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA();
311            }
312        }
313    }
314
315    // Setting this true with the assumption that this cap will eventually mean we support varying
316    // precisions and not just via modifiers.
317    shaderCaps->fUsesPrecisionModifiers = true;
318    shaderCaps->fFlatInterpolationSupport = true;
319    // We haven't yet tested that using flat attributes perform well.
320    shaderCaps->fPreferFlatInterpolation = true;
321
322    shaderCaps->fShaderDerivativeSupport = true;
323    shaderCaps->fGeometryShaderSupport = false;
324
325    if ((this->isMac() && fVersion >= 2) ||
326        (this->isIOS() && ((1 == fFamilyGroup && 4 == fVersion) ||
327                           (2 == fFamilyGroup && 4 == fVersion) ||
328                           (3 == fFamilyGroup && 3 == fVersion)))) {
329        shaderCaps->fDualSourceBlendingSupport = true;
330    }
331
332    // TODO: Re-enable this once skbug:8720 is fixed. Will also need to remove asserts in
333    // GrMtlPipelineStateBuilder which assert we aren't using this feature.
334#if 0
335    if (this->isIOS()) {
336        shaderCaps->fFBFetchSupport = true;
337        shaderCaps->fFBFetchNeedsCustomOutput = true; // ??
338        shaderCaps->fFBFetchColorName = ""; // Somehow add [[color(0)]] to arguments to frag shader
339    }
340#endif
341    shaderCaps->fDstReadInShaderSupport = shaderCaps->fFBFetchSupport;
342
343    shaderCaps->fIntegerSupport = true;
344    shaderCaps->fVertexIDSupport = false;
345    shaderCaps->fImageLoadStoreSupport = false;
346
347    // Metal uses IEEE float and half floats so assuming those values here.
348    shaderCaps->fFloatIs32Bits = true;
349    shaderCaps->fHalfIs32Bits = false;
350
351    // Metal supports unsigned integers.
352    shaderCaps->fUnsignedSupport = true;
353
354    shaderCaps->fMaxFragmentSamplers = 16;
355}
356
357void GrMtlCaps::initConfigTable() {
358    ConfigInfo* info;
359    // Alpha_8 uses R8Unorm
360    info = &fConfigTable[kAlpha_8_GrPixelConfig];
361    info->fFlags = ConfigInfo::kAllFlags;
362
363    // Gray_8 uses R8Unorm
364    info = &fConfigTable[kGray_8_GrPixelConfig];
365    info->fFlags = ConfigInfo::kAllFlags;
366
367    // RGB_565 uses B5G6R5Unorm, even though written opposite this format packs how we want
368    info = &fConfigTable[kRGB_565_GrPixelConfig];
369    if (this->isMac()) {
370        info->fFlags = 0;
371    } else {
372        info->fFlags = ConfigInfo::kAllFlags;
373    }
374
375    // RGBA_4444 uses ABGR4Unorm
376    info = &fConfigTable[kRGBA_4444_GrPixelConfig];
377    if (this->isMac()) {
378        info->fFlags = 0;
379    } else {
380        info->fFlags = ConfigInfo::kAllFlags;
381    }
382
383    // RGBA_8888 uses RGBA8Unorm
384    info = &fConfigTable[kRGBA_8888_GrPixelConfig];
385    info->fFlags = ConfigInfo::kAllFlags;
386
387    // RGB_888X uses RGBA8Unorm and we will swizzle the 1
388    info = &fConfigTable[kRGB_888X_GrPixelConfig];
389    info->fFlags = ConfigInfo::kTextureable_Flag;
390
391    // BGRA_8888 uses BGRA8Unorm
392    info = &fConfigTable[kBGRA_8888_GrPixelConfig];
393    info->fFlags = ConfigInfo::kAllFlags;
394
395    // SRGBA_8888 uses RGBA8Unorm_sRGB
396    info = &fConfigTable[kSRGBA_8888_GrPixelConfig];
397    info->fFlags = ConfigInfo::kAllFlags;
398
399    // SBGRA_8888 uses BGRA8Unorm_sRGB
400    info = &fConfigTable[kSBGRA_8888_GrPixelConfig];
401    info->fFlags = ConfigInfo::kAllFlags;
402
403    // RGBA_float uses RGBA32Float
404    info = &fConfigTable[kRGBA_float_GrPixelConfig];
405    if (this->isMac()) {
406        info->fFlags = ConfigInfo::kAllFlags;
407    } else {
408        info->fFlags = 0;
409    }
410
411    // RG_float uses RG32Float
412    info = &fConfigTable[kRG_float_GrPixelConfig];
413    if (this->isMac()) {
414        info->fFlags = ConfigInfo::kAllFlags;
415    } else {
416        info->fFlags = ConfigInfo::kRenderable_Flag;
417    }
418
419    // Alpha_half uses R16Float
420    info = &fConfigTable[kAlpha_half_GrPixelConfig];
421    info->fFlags = ConfigInfo::kAllFlags;
422
423    // RGBA_half uses RGBA16Float
424    info = &fConfigTable[kRGBA_half_GrPixelConfig];
425    info->fFlags = ConfigInfo::kAllFlags;
426
427    info = &fConfigTable[kRGBA_half_Clamped_GrPixelConfig];
428    info->fFlags = ConfigInfo::kAllFlags;
429}
430
431void GrMtlCaps::initStencilFormat(id<MTLDevice> physDev) {
432    fPreferredStencilFormat = StencilFormat{ MTLPixelFormatStencil8, 8, 8, true };
433}
434
435GrPixelConfig validate_sized_format(GrMTLPixelFormat grFormat, SkColorType ct) {
436    MTLPixelFormat format = static_cast<MTLPixelFormat>(grFormat);
437    switch (ct) {
438        case kUnknown_SkColorType:
439            return kUnknown_GrPixelConfig;
440        case kAlpha_8_SkColorType:
441            if (MTLPixelFormatA8Unorm == format) {
442                return kAlpha_8_as_Alpha_GrPixelConfig;
443            } else if (MTLPixelFormatR8Unorm == format) {
444                return kAlpha_8_as_Red_GrPixelConfig;
445            }
446            break;
447#ifdef SK_BUILD_FOR_MAC
448        case kRGB_565_SkColorType:
449        case kARGB_4444_SkColorType:
450            return kUnknown_GrPixelConfig;
451            break;
452#else
453        case kRGB_565_SkColorType:
454            if (MTLPixelFormatB5G6R5Unorm == format) {
455                return kRGB_565_GrPixelConfig;
456            }
457            break;
458        case kARGB_4444_SkColorType:
459            if (MTLPixelFormatABGR4Unorm == format) {
460                return kRGBA_4444_GrPixelConfig;
461            }
462            break;
463#endif
464        case kRGBA_8888_SkColorType:
465            if (MTLPixelFormatRGBA8Unorm == format) {
466                return kRGBA_8888_GrPixelConfig;
467            } else if (MTLPixelFormatRGBA8Unorm_sRGB == format) {
468                return kSRGBA_8888_GrPixelConfig;
469            }
470            break;
471        case kRGB_888x_SkColorType:
472            if (MTLPixelFormatRGBA8Unorm == format) {
473                return kRGB_888X_GrPixelConfig;
474            }
475            break;
476        case kBGRA_8888_SkColorType:
477            if (MTLPixelFormatBGRA8Unorm == format) {
478                return kBGRA_8888_GrPixelConfig;
479            } else if (MTLPixelFormatBGRA8Unorm_sRGB == format) {
480                return kSBGRA_8888_GrPixelConfig;
481            }
482            break;
483        case kRGBA_1010102_SkColorType:
484            if (MTLPixelFormatRGB10A2Unorm == format) {
485                return kRGBA_1010102_GrPixelConfig;
486            }
487            break;
488        case kRGB_101010x_SkColorType:
489            break;
490        case kGray_8_SkColorType:
491            if (MTLPixelFormatR8Unorm == format) {
492                return kGray_8_as_Red_GrPixelConfig;
493            }
494            break;
495        case kRGBA_F16Norm_SkColorType:
496            if (MTLPixelFormatRGBA16Float == format) {
497                return kRGBA_half_Clamped_GrPixelConfig;
498            }
499            break;
500        case kRGBA_F16_SkColorType:
501            if (MTLPixelFormatRGBA16Float == format) {
502                return kRGBA_half_GrPixelConfig;
503            }
504            break;
505        case kRGBA_F32_SkColorType:
506            if (MTLPixelFormatR32Float == format) {
507                return kRGBA_float_GrPixelConfig;
508            }
509            break;
510    }
511
512    return kUnknown_GrPixelConfig;
513}
514
515GrPixelConfig GrMtlCaps::validateBackendRenderTarget(const GrBackendRenderTarget& rt,
516                                                     SkColorType ct) const {
517    GrMtlTextureInfo fbInfo;
518    if (!rt.getMtlTextureInfo(&fbInfo)) {
519        return kUnknown_GrPixelConfig;
520    }
521
522    id<MTLTexture> texture = (__bridge id<MTLTexture>)fbInfo.fTexture;
523    return validate_sized_format(texture.pixelFormat, ct);
524}
525
526GrPixelConfig GrMtlCaps::getConfigFromBackendFormat(const GrBackendFormat& format,
527                                                    SkColorType ct) const {
528    const GrMTLPixelFormat* mtlFormat = format.getMtlFormat();
529    if (!mtlFormat) {
530        return kUnknown_GrPixelConfig;
531    }
532    return validate_sized_format(*mtlFormat, ct);
533}
534
535static GrPixelConfig get_yuva_config(GrMTLPixelFormat grFormat) {
536    MTLPixelFormat format = static_cast<MTLPixelFormat>(grFormat);
537
538    switch (format) {
539        case MTLPixelFormatA8Unorm:
540            return kAlpha_8_as_Alpha_GrPixelConfig;
541            break;
542        case MTLPixelFormatR8Unorm:
543            return kAlpha_8_as_Red_GrPixelConfig;
544            break;
545        // TODO: Add RG_88 format here
546        case MTLPixelFormatRGBA8Unorm:
547            return kRGBA_8888_GrPixelConfig;
548            break;
549        case MTLPixelFormatBGRA8Unorm:
550            return kBGRA_8888_GrPixelConfig;
551            break;
552        default:
553            return kUnknown_GrPixelConfig;
554            break;
555    }
556}
557
558GrPixelConfig GrMtlCaps::getYUVAConfigFromBackendFormat(const GrBackendFormat& format) const {
559    const GrMTLPixelFormat* mtlFormat = format.getMtlFormat();
560    if (!mtlFormat) {
561        return kUnknown_GrPixelConfig;
562    }
563    return get_yuva_config(*mtlFormat);
564}
565
566GrBackendFormat GrMtlCaps::getBackendFormatFromGrColorType(GrColorType ct,
567                                                           GrSRGBEncoded srgbEncoded) const {
568    GrPixelConfig config = GrColorTypeToPixelConfig(ct, srgbEncoded);
569    if (config == kUnknown_GrPixelConfig) {
570        return GrBackendFormat();
571    }
572    MTLPixelFormat format;
573    if (!GrPixelConfigToMTLFormat(config, &format)) {
574        return GrBackendFormat();
575    }
576    return GrBackendFormat::MakeMtl(format);
577}
578
579