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