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 "GrShaderCaps.h"
11
12GrMtlCaps::GrMtlCaps(const GrContextOptions& contextOptions, const id<MTLDevice> device,
13                     MTLFeatureSet featureSet)
14        : INHERITED(contextOptions) {
15    fShaderCaps.reset(new GrShaderCaps(contextOptions));
16
17    this->initFeatureSet(featureSet);
18    this->initGrCaps(device);
19    this->initShaderCaps();
20    this->initConfigTable();
21
22    this->applyOptionsOverrides(contextOptions);
23    fShaderCaps->applyOptionsOverrides(contextOptions);
24}
25
26void GrMtlCaps::initFeatureSet(MTLFeatureSet featureSet) {
27    // Mac OSX
28#ifdef SK_BUILD_FOR_MAC
29    if (MTLFeatureSet_OSX_GPUFamily1_v2 == featureSet) {
30        fPlatform = Platform::kMac;
31        fFamilyGroup = 1;
32        fVersion = 2;
33        return;
34    }
35    if (MTLFeatureSet_OSX_GPUFamily1_v1 == featureSet) {
36        fPlatform = Platform::kMac;
37        fFamilyGroup = 1;
38        fVersion = 1;
39        return;
40    }
41#endif
42
43    // iOS Family group 3
44#ifdef SK_BUILD_FOR_IOS
45    if (MTLFeatureSet_iOS_GPUFamily3_v2 == featureSet) {
46        fPlatform = Platform::kIOS;
47        fFamilyGroup = 3;
48        fVersion = 2;
49        return;
50    }
51    if (MTLFeatureSet_iOS_GPUFamily3_v1 == featureSet) {
52        fPlatform = Platform::kIOS;
53        fFamilyGroup = 3;
54        fVersion = 1;
55        return;
56    }
57
58    // iOS Family group 2
59    if (MTLFeatureSet_iOS_GPUFamily2_v3 == featureSet) {
60        fPlatform = Platform::kIOS;
61        fFamilyGroup = 2;
62        fVersion = 3;
63        return;
64    }
65    if (MTLFeatureSet_iOS_GPUFamily2_v2 == featureSet) {
66        fPlatform = Platform::kIOS;
67        fFamilyGroup = 2;
68        fVersion = 2;
69        return;
70    }
71    if (MTLFeatureSet_iOS_GPUFamily2_v1 == featureSet) {
72        fPlatform = Platform::kIOS;
73        fFamilyGroup = 2;
74        fVersion = 1;
75        return;
76    }
77
78    // iOS Family group 1
79    if (MTLFeatureSet_iOS_GPUFamily1_v3 == featureSet) {
80        fPlatform = Platform::kIOS;
81        fFamilyGroup = 1;
82        fVersion = 3;
83        return;
84    }
85    if (MTLFeatureSet_iOS_GPUFamily1_v2 == featureSet) {
86        fPlatform = Platform::kIOS;
87        fFamilyGroup = 1;
88        fVersion = 2;
89        return;
90    }
91    if (MTLFeatureSet_iOS_GPUFamily1_v1 == featureSet) {
92        fPlatform = Platform::kIOS;
93        fFamilyGroup = 1;
94        fVersion = 1;
95        return;
96    }
97#endif
98    // No supported feature sets were found
99    SK_ABORT("Requested an unsupported feature set");
100}
101
102void GrMtlCaps::initGrCaps(const id<MTLDevice> device) {
103    // Max vertex attribs is the same on all devices
104    fMaxVertexAttributes = 31;
105
106    // RenderTarget and Texture size
107    if (this->isMac()) {
108        fMaxRenderTargetSize = 16384;
109    } else {
110        if (3 == fFamilyGroup) {
111            fMaxRenderTargetSize = 16384;
112        } else {
113            // Family group 1 and 2 support 8192 for version 2 and above, 4096 for v1
114            if (1 == fVersion) {
115                fMaxRenderTargetSize = 4096;
116            } else {
117                fMaxRenderTargetSize = 8192;
118            }
119        }
120    }
121    fMaxPreferredRenderTargetSize = fMaxRenderTargetSize;
122    fMaxTextureSize = fMaxRenderTargetSize;
123
124    // Init sample counts. All devices support 1 (i.e. 0 in skia).
125    fSampleCounts.push(1);
126    for (auto sampleCnt : {2, 4, 8}) {
127        if ([device supportsTextureSampleCount:sampleCnt]) {
128            fSampleCounts.push(sampleCnt);
129        }
130    }
131
132    // Starting with the assumption that there isn't a reason to not map small buffers.
133    fBufferMapThreshold = 0;
134
135    // Buffers are always fully mapped.
136    fMapBufferFlags = kCanMap_MapFlag;
137
138    fOversizedStencilSupport = true;
139
140    // Looks like there is a field called rasterSampleCount labeled as beta in the Metal docs. This
141    // may be what we eventually need here, but it has no description.
142    fSampleShadingSupport = false;
143
144    fSRGBSupport = true;   // always available in Metal
145    fSRGBWriteControl = false;
146    fMipMapSupport = true;   // always available in Metal
147    fNPOTTextureTileSupport = true;  // always available in Metal
148    fDiscardRenderTargetSupport = true;
149
150    fReuseScratchTextures = true; // Assuming this okay
151
152    fTextureBarrierSupport = false; // Need to figure out if we can do this
153
154    fSampleLocationsSupport = false;
155    fMultisampleDisableSupport = false;
156
157    if (this->isMac() || 3 == fFamilyGroup) {
158        fInstanceAttribSupport = true;
159    }
160
161    fUsesMixedSamples = false;
162    fGpuTracingSupport = false;
163
164    fFenceSyncSupport = true;   // always available in Metal
165    fCrossContextTextureSupport = false;
166}
167
168
169int GrMtlCaps::maxRenderTargetSampleCount(GrPixelConfig config) const {
170    if (fConfigTable[config].fFlags & ConfigInfo::kMSAA_Flag) {
171        return fSampleCounts[fSampleCounts.count() - 1];
172    } else if (fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag) {
173        return 1;
174    }
175    return 0;
176}
177
178int GrMtlCaps::getRenderTargetSampleCount(int requestedCount, GrPixelConfig config) const {
179    requestedCount = SkTMax(requestedCount, 1);
180    if (fConfigTable[config].fFlags & ConfigInfo::kMSAA_Flag) {
181        int count = fSampleCounts.count();
182        for (int i = 0; i < count; ++i) {
183            if (fSampleCounts[i] >= requestedCount) {
184                return fSampleCounts[i];
185            }
186        }
187    } else if (fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag) {
188        return 1 == requestedCount ? 1 : 0;
189    }
190    return 0;
191}
192
193void GrMtlCaps::initShaderCaps() {
194    GrShaderCaps* shaderCaps = fShaderCaps.get();
195
196    // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config.
197    for (int i = 0; i < kGrPixelConfigCnt; ++i) {
198        GrPixelConfig config = static_cast<GrPixelConfig>(i);
199        if (GrPixelConfigIsAlphaOnly(config)) {
200            shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR();
201            shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA();
202        } else {
203            if (kGray_8_GrPixelConfig == config) {
204                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRA();
205            } else {
206                shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA();
207            }
208        }
209    }
210
211    // Setting this true with the assumption that this cap will eventually mean we support varying
212    // precisions and not just via modifiers.
213    shaderCaps->fUsesPrecisionModifiers = true;
214    shaderCaps->fFlatInterpolationSupport = true;
215    // We haven't yet tested that using flat attributes perform well.
216    shaderCaps->fPreferFlatInterpolation = true;
217
218    shaderCaps->fShaderDerivativeSupport = true;
219    shaderCaps->fGeometryShaderSupport = false;
220
221    if ((this->isMac() && fVersion >= 2) ||
222        (this->isIOS() && ((1 == fFamilyGroup && 4 == fVersion) ||
223                           (2 == fFamilyGroup && 4 == fVersion) ||
224                           (3 == fFamilyGroup && 3 == fVersion)))) {
225        shaderCaps->fDualSourceBlendingSupport = true;
226    }
227
228    if (this->isIOS()) {
229        shaderCaps->fFBFetchSupport = true;
230        shaderCaps->fFBFetchNeedsCustomOutput = true; // ??
231        shaderCaps->fFBFetchColorName = ""; // Somehow add [[color(0)]] to arguments to frag shader
232    }
233    shaderCaps->fDstReadInShaderSupport = shaderCaps->fFBFetchSupport;
234
235    shaderCaps->fIntegerSupport = true;
236    shaderCaps->fTexelBufferSupport = false;
237    shaderCaps->fTexelFetchSupport = false;
238    shaderCaps->fVertexIDSupport = false;
239    shaderCaps->fImageLoadStoreSupport = false;
240
241    // Metal uses IEEE float and half floats so assuming those values here.
242    shaderCaps->fFloatIs32Bits = true;
243    shaderCaps->fHalfIs32Bits = false;
244
245    shaderCaps->fMaxVertexSamplers =
246    shaderCaps->fMaxFragmentSamplers = 16;
247    // For now just cap at the per stage max. If we hit this limit we can come back to adjust this
248    shaderCaps->fMaxCombinedSamplers = shaderCaps->fMaxVertexSamplers;
249}
250
251void GrMtlCaps::initConfigTable() {
252    ConfigInfo* info;
253    // Alpha_8 uses R8Unorm
254    info = &fConfigTable[kAlpha_8_GrPixelConfig];
255    info->fFlags = ConfigInfo::kAllFlags;
256
257    // Gray_8 uses R8Unorm
258    info = &fConfigTable[kGray_8_GrPixelConfig];
259    info->fFlags = ConfigInfo::kAllFlags;
260
261    // RGB_565 uses B5G6R5Unorm, even though written opposite this format packs how we want
262    info = &fConfigTable[kRGB_565_GrPixelConfig];
263    if (this->isMac()) {
264        info->fFlags = 0;
265    } else {
266        info->fFlags = ConfigInfo::kAllFlags;
267    }
268
269    // RGBA_4444 uses ABGR4Unorm
270    info = &fConfigTable[kRGBA_4444_GrPixelConfig];
271    if (this->isMac()) {
272        info->fFlags = 0;
273    } else {
274        info->fFlags = ConfigInfo::kAllFlags;
275    }
276
277    // RGBA_8888 uses RGBA8Unorm
278    info = &fConfigTable[kRGBA_8888_GrPixelConfig];
279    info->fFlags = ConfigInfo::kAllFlags;
280
281    // BGRA_8888 uses BGRA8Unorm
282    info = &fConfigTable[kBGRA_8888_GrPixelConfig];
283    info->fFlags = ConfigInfo::kAllFlags;
284
285    // SRGBA_8888 uses RGBA8Unorm_sRGB
286    info = &fConfigTable[kSRGBA_8888_GrPixelConfig];
287    info->fFlags = ConfigInfo::kAllFlags;
288
289    // SBGRA_8888 uses BGRA8Unorm_sRGB
290    info = &fConfigTable[kSBGRA_8888_GrPixelConfig];
291    info->fFlags = ConfigInfo::kAllFlags;
292
293    // RGBA_float uses RGBA32Float
294    info = &fConfigTable[kRGBA_float_GrPixelConfig];
295    if (this->isMac()) {
296        info->fFlags = ConfigInfo::kAllFlags;
297    } else {
298        info->fFlags = 0;
299    }
300
301    // RG_float uses RG32Float
302    info = &fConfigTable[kRG_float_GrPixelConfig];
303    if (this->isMac()) {
304        info->fFlags = ConfigInfo::kAllFlags;
305    } else {
306        info->fFlags = ConfigInfo::kRenderable_Flag;
307    }
308
309    // Alpha_half uses R16Float
310    info = &fConfigTable[kAlpha_half_GrPixelConfig];
311    info->fFlags = ConfigInfo::kAllFlags;
312
313    // RGBA_half uses RGBA16Float
314    info = &fConfigTable[kRGBA_half_GrPixelConfig];
315    info->fFlags = ConfigInfo::kAllFlags;
316}
317