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