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 {
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
355void 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
422void GrMtlCaps::initStencilFormat(id<MTLDevice> physDev) {
423    fPreferredStencilFormat = StencilFormat{ MTLPixelFormatStencil8, 8, 8, true };
424}
425
426GrBackendFormat 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