1// 2// Copyright 2019 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6 7// DisplayMtl.mm: Metal implementation of DisplayImpl 8 9#include "libANGLE/renderer/metal/DisplayMtl.h" 10#include <sys/param.h> 11 12#include "common/apple_platform_utils.h" 13#include "common/system_utils.h" 14#include "gpu_info_util/SystemInfo.h" 15#include "libANGLE/Context.h" 16#include "libANGLE/Display.h" 17#include "libANGLE/Surface.h" 18#include "libANGLE/renderer/driver_utils.h" 19#include "libANGLE/renderer/metal/CompilerMtl.h" 20#include "libANGLE/renderer/metal/ContextMtl.h" 21#include "libANGLE/renderer/metal/DeviceMtl.h" 22#include "libANGLE/renderer/metal/IOSurfaceSurfaceMtl.h" 23#include "libANGLE/renderer/metal/ImageMtl.h" 24#include "libANGLE/renderer/metal/SurfaceMtl.h" 25#include "libANGLE/renderer/metal/SyncMtl.h" 26#include "libANGLE/renderer/metal/mtl_common.h" 27#include "libANGLE/trace.h" 28#include "mtl_command_buffer.h" 29#include "platform/PlatformMethods.h" 30 31#if ANGLE_METAL_XCODE_BUILDS_SHADERS 32# include "libANGLE/renderer/metal/shaders/mtl_internal_shaders_metallib.h" 33#elif ANGLE_METAL_HAS_PREBUILT_INTERNAL_SHADERS 34# include "mtl_internal_shaders_metallib.h" 35#else 36# include "libANGLE/renderer/metal/shaders/mtl_internal_shaders_src_autogen.h" 37#endif 38 39#include "EGL/eglext.h" 40 41namespace rx 42{ 43 44static EGLint GetDepthSize(GLint internalformat) 45{ 46 switch (internalformat) 47 { 48 case GL_STENCIL_INDEX8: 49 return 0; 50 case GL_DEPTH_COMPONENT16: 51 return 16; 52 case GL_DEPTH_COMPONENT24: 53 return 24; 54 case GL_DEPTH_COMPONENT32_OES: 55 return 32; 56 case GL_DEPTH_COMPONENT32F: 57 return 32; 58 case GL_DEPTH24_STENCIL8: 59 return 24; 60 case GL_DEPTH32F_STENCIL8: 61 return 32; 62 default: 63 // UNREACHABLE(internalformat); 64 return 0; 65 } 66} 67 68static EGLint GetStencilSize(GLint internalformat) 69{ 70 switch (internalformat) 71 { 72 case GL_STENCIL_INDEX8: 73 return 8; 74 case GL_DEPTH_COMPONENT16: 75 return 0; 76 case GL_DEPTH_COMPONENT24: 77 return 0; 78 case GL_DEPTH_COMPONENT32_OES: 79 return 0; 80 case GL_DEPTH_COMPONENT32F: 81 return 0; 82 case GL_DEPTH24_STENCIL8: 83 return 8; 84 case GL_DEPTH32F_STENCIL8: 85 return 8; 86 default: 87 // UNREACHABLE(internalformat); 88 return 0; 89 } 90} 91 92bool IsMetalDisplayAvailable() 93{ 94 return angle::IsMetalRendererAvailable(); 95} 96 97DisplayImpl *CreateMetalDisplay(const egl::DisplayState &state) 98{ 99 return new DisplayMtl(state); 100} 101 102DisplayMtl::DisplayMtl(const egl::DisplayState &state) 103 : DisplayImpl(state), mDisplay(nullptr), mStateCache(mFeatures) 104{} 105 106DisplayMtl::~DisplayMtl() {} 107 108egl::Error DisplayMtl::initialize(egl::Display *display) 109{ 110 ASSERT(IsMetalDisplayAvailable()); 111 112 angle::Result result = initializeImpl(display); 113 if (result != angle::Result::Continue) 114 { 115 terminate(); 116 return egl::EglNotInitialized(); 117 } 118 return egl::NoError(); 119} 120 121angle::Result DisplayMtl::initializeImpl(egl::Display *display) 122{ 123 ANGLE_MTL_OBJC_SCOPE 124 { 125 mDisplay = display; 126 127 mMetalDevice = getMetalDeviceMatchingAttribute(display->getAttributeMap()); 128 // If we can't create a device, fail initialization. 129 if (!mMetalDevice.get()) 130 { 131 return angle::Result::Stop; 132 } 133 134 mMetalDeviceVendorId = mtl::GetDeviceVendorId(mMetalDevice); 135 136 mCapsInitialized = false; 137 138 initializeFeatures(); 139 140 if (mFeatures.requireGpuFamily2.enabled && !supportsEitherGPUFamily(1, 2)) 141 { 142 ANGLE_MTL_LOG("Could not initialize: Metal device does not support Mac GPU family 2."); 143 return angle::Result::Stop; 144 } 145 146 if (mFeatures.requireMsl21.enabled && !supportsMetal2_1()) 147 { 148 ANGLE_MTL_LOG("Could not initialize: MSL 2.1 is not available."); 149 return angle::Result::Stop; 150 } 151 152 if (mFeatures.disableMetalOnNvidia.enabled && isNVIDIA()) 153 { 154 ANGLE_MTL_LOG("Could not initialize: Metal not supported on NVIDIA GPUs."); 155 return angle::Result::Stop; 156 } 157 158 mCmdQueue.set([[mMetalDevice newCommandQueue] ANGLE_MTL_AUTORELEASE]); 159 160 ANGLE_TRY(mFormatTable.initialize(this)); 161 ANGLE_TRY(initializeShaderLibrary()); 162 163 mUtils = std::make_unique<mtl::RenderUtils>(this); 164 165 return angle::Result::Continue; 166 } 167} 168 169void DisplayMtl::terminate() 170{ 171 mUtils = nullptr; 172 mCmdQueue.reset(); 173 mDefaultShaders = nil; 174 mMetalDevice = nil; 175#if ANGLE_MTL_EVENT_AVAILABLE 176 mSharedEventListener = nil; 177#endif 178 mCapsInitialized = false; 179 180 mMetalDeviceVendorId = 0; 181 mComputedAMDBronze = false; 182 mIsAMDBronze = false; 183} 184 185bool DisplayMtl::testDeviceLost() 186{ 187 return false; 188} 189 190egl::Error DisplayMtl::restoreLostDevice(const egl::Display *display) 191{ 192 return egl::NoError(); 193} 194 195std::string DisplayMtl::getRendererDescription() 196{ 197 ANGLE_MTL_OBJC_SCOPE 198 { 199 std::string desc = "ANGLE Metal Renderer"; 200 201 if (mMetalDevice) 202 { 203 desc += ": "; 204 desc += mMetalDevice.get().name.UTF8String; 205 } 206 207 return desc; 208 } 209} 210 211std::string DisplayMtl::getVendorString() 212{ 213 return GetVendorString(mMetalDeviceVendorId); 214} 215 216std::string DisplayMtl::getVersionString(bool includeFullVersion) 217{ 218 if (!includeFullVersion) 219 { 220 // For WebGL contexts it's inappropriate to include any 221 // additional version information, but Chrome requires 222 // something to be present here. 223 return "Unspecified Version"; 224 } 225 226 ANGLE_MTL_OBJC_SCOPE 227 { 228 NSProcessInfo *procInfo = [NSProcessInfo processInfo]; 229 return procInfo.operatingSystemVersionString.UTF8String; 230 } 231} 232 233DeviceImpl *DisplayMtl::createDevice() 234{ 235 return new DeviceMtl(); 236} 237 238mtl::AutoObjCPtr<id<MTLDevice>> DisplayMtl::getMetalDeviceMatchingAttribute( 239 const egl::AttributeMap &attribs) 240{ 241#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) 242 auto deviceList = mtl::adoptObjCObj(MTLCopyAllDevices()); 243 244 EGLAttrib high = attribs.get(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE, 0); 245 EGLAttrib low = attribs.get(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE, 0); 246 uint64_t deviceId = 247 angle::GetSystemDeviceIdFromParts(static_cast<uint32_t>(high), static_cast<uint32_t>(low)); 248 // Check EGL_ANGLE_platform_angle_device_id to see if a device was specified. 249 if (deviceId != 0) 250 { 251 for (id<MTLDevice> device in deviceList.get()) 252 { 253 if ([device registryID] == deviceId) 254 { 255 return device; 256 } 257 } 258 } 259 260 auto externalGPUs = 261 mtl::adoptObjCObj<NSMutableArray<id<MTLDevice>>>([[NSMutableArray alloc] init]); 262 auto integratedGPUs = 263 mtl::adoptObjCObj<NSMutableArray<id<MTLDevice>>>([[NSMutableArray alloc] init]); 264 auto discreteGPUs = 265 mtl::adoptObjCObj<NSMutableArray<id<MTLDevice>>>([[NSMutableArray alloc] init]); 266 for (id<MTLDevice> device in deviceList.get()) 267 { 268 if (device.removable) 269 { 270 [externalGPUs addObject:device]; 271 } 272 else if (device.lowPower) 273 { 274 [integratedGPUs addObject:device]; 275 } 276 else 277 { 278 [discreteGPUs addObject:device]; 279 } 280 } 281 // TODO(kpiddington: External GPU support. Do we prefer high power / low bandwidth for general 282 // WebGL applications? 283 // Can we support hot-swapping in GPU's? 284 if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, 0) == EGL_HIGH_POWER_ANGLE) 285 { 286 // Search for a discrete GPU first. 287 for (id<MTLDevice> device in discreteGPUs.get()) 288 { 289 if (![device isHeadless]) 290 return device; 291 } 292 } 293 else if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, 0) == EGL_LOW_POWER_ANGLE) 294 { 295 // If we've selected a low power device, look through integrated devices. 296 for (id<MTLDevice> device in integratedGPUs.get()) 297 { 298 if (![device isHeadless]) 299 return device; 300 } 301 } 302 303 const std::string preferredDeviceString = angle::GetPreferredDeviceString(); 304 if (!preferredDeviceString.empty()) 305 { 306 for (id<MTLDevice> device in deviceList.get()) 307 { 308 if ([device.name.lowercaseString 309 containsString:[NSString stringWithUTF8String:preferredDeviceString.c_str()]]) 310 { 311 NSLog(@"Using Metal Device: %@", [device name]); 312 return device; 313 } 314 } 315 } 316 317#endif 318 // If we can't find anything, or are on a platform that doesn't support power options, create a 319 // default device. 320 return mtl::adoptObjCObj(MTLCreateSystemDefaultDevice()); 321} 322 323egl::Error DisplayMtl::waitClient(const gl::Context *context) 324{ 325 auto contextMtl = GetImplAs<ContextMtl>(context); 326 angle::Result result = contextMtl->finishCommandBuffer(); 327 328 if (result != angle::Result::Continue) 329 { 330 return egl::EglBadAccess(); 331 } 332 return egl::NoError(); 333} 334 335egl::Error DisplayMtl::waitNative(const gl::Context *context, EGLint engine) 336{ 337 UNIMPLEMENTED(); 338 return egl::NoError(); 339} 340 341egl::Error DisplayMtl::waitUntilWorkScheduled() 342{ 343 for (auto context : mState.contextMap) 344 { 345 auto contextMtl = GetImplAs<ContextMtl>(context.second); 346 contextMtl->flushCommandBuffer(mtl::WaitUntilScheduled); 347 } 348 return egl::NoError(); 349} 350 351SurfaceImpl *DisplayMtl::createWindowSurface(const egl::SurfaceState &state, 352 EGLNativeWindowType window, 353 const egl::AttributeMap &attribs) 354{ 355 return new WindowSurfaceMtl(this, state, window, attribs); 356} 357 358SurfaceImpl *DisplayMtl::createPbufferSurface(const egl::SurfaceState &state, 359 const egl::AttributeMap &attribs) 360{ 361 return new PBufferSurfaceMtl(this, state, attribs); 362} 363 364SurfaceImpl *DisplayMtl::createPbufferFromClientBuffer(const egl::SurfaceState &state, 365 EGLenum buftype, 366 EGLClientBuffer clientBuffer, 367 const egl::AttributeMap &attribs) 368{ 369 switch (buftype) 370 { 371 case EGL_IOSURFACE_ANGLE: 372 return new IOSurfaceSurfaceMtl(this, state, clientBuffer, attribs); 373 default: 374 UNREACHABLE(); 375 } 376 return nullptr; 377} 378 379SurfaceImpl *DisplayMtl::createPixmapSurface(const egl::SurfaceState &state, 380 NativePixmapType nativePixmap, 381 const egl::AttributeMap &attribs) 382{ 383 UNIMPLEMENTED(); 384 return static_cast<SurfaceImpl *>(0); 385} 386 387ImageImpl *DisplayMtl::createImage(const egl::ImageState &state, 388 const gl::Context *context, 389 EGLenum target, 390 const egl::AttributeMap &attribs) 391{ 392 return new ImageMtl(state, context); 393} 394 395rx::ContextImpl *DisplayMtl::createContext(const gl::State &state, 396 gl::ErrorSet *errorSet, 397 const egl::Config *configuration, 398 const gl::Context *shareContext, 399 const egl::AttributeMap &attribs) 400{ 401 return new ContextMtl(state, errorSet, attribs, this); 402} 403 404StreamProducerImpl *DisplayMtl::createStreamProducerD3DTexture( 405 egl::Stream::ConsumerType consumerType, 406 const egl::AttributeMap &attribs) 407{ 408 UNIMPLEMENTED(); 409 return nullptr; 410} 411 412ShareGroupImpl *DisplayMtl::createShareGroup(const egl::ShareGroupState &state) 413{ 414 return new ShareGroupMtl(state); 415} 416 417ExternalImageSiblingImpl *DisplayMtl::createExternalImageSibling(const gl::Context *context, 418 EGLenum target, 419 EGLClientBuffer buffer, 420 const egl::AttributeMap &attribs) 421{ 422 switch (target) 423 { 424 case EGL_METAL_TEXTURE_ANGLE: 425 return new TextureImageSiblingMtl(buffer, attribs); 426 427 default: 428 UNREACHABLE(); 429 return nullptr; 430 } 431} 432 433gl::Version DisplayMtl::getMaxSupportedESVersion() const 434{ 435#if TARGET_OS_SIMULATOR 436 // Simulator should be able to support ES3, despite not supporting iOS GPU 437 // Family 3 in its entirety. 438 // FIXME: None of the feature conditions are checked for simulator support. 439 return gl::Version(3, 0); 440#else 441 if (supportsEitherGPUFamily(3, 1)) 442 { 443 return mtl::kMaxSupportedGLVersion; 444 } 445 return gl::Version(2, 0); 446#endif 447} 448 449gl::Version DisplayMtl::getMaxConformantESVersion() const 450{ 451 return std::min(getMaxSupportedESVersion(), gl::Version(3, 0)); 452} 453 454Optional<gl::Version> DisplayMtl::getMaxSupportedDesktopVersion() const 455{ 456 return Optional<gl::Version>::Invalid(); 457} 458 459EGLSyncImpl *DisplayMtl::createSync() 460{ 461 return new EGLSyncMtl(); 462} 463 464egl::Error DisplayMtl::makeCurrent(egl::Display *display, 465 egl::Surface *drawSurface, 466 egl::Surface *readSurface, 467 gl::Context *context) 468{ 469 if (!context) 470 { 471 return egl::NoError(); 472 } 473 474 return egl::NoError(); 475} 476 477void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const 478{ 479 outExtensions->iosurfaceClientBuffer = true; 480 outExtensions->surfacelessContext = true; 481 outExtensions->noConfigContext = true; 482 outExtensions->displayTextureShareGroup = true; 483 outExtensions->displaySemaphoreShareGroup = true; 484 outExtensions->mtlTextureClientBuffer = true; 485 outExtensions->waitUntilWorkScheduled = true; 486 487 if (mFeatures.hasEvents.enabled) 488 { 489 // MTLSharedEvent is only available since Metal 2.1 490 outExtensions->fenceSync = true; 491 outExtensions->waitSync = true; 492 } 493 494 // Note that robust resource initialization is not yet implemented. We only expose 495 // this extension so that ANGLE can be initialized in Chrome. WebGL will fail to use 496 // this extension (anglebug.com/4929) 497 outExtensions->robustResourceInitializationANGLE = true; 498 499 // EGL_KHR_image 500 outExtensions->image = true; 501 outExtensions->imageBase = true; 502 503 // EGL_ANGLE_metal_create_context_ownership_identity 504 outExtensions->metalCreateContextOwnershipIdentityANGLE = true; 505 506 // EGL_ANGLE_metal_sync_shared_event 507 outExtensions->mtlSyncSharedEventANGLE = true; 508} 509 510void DisplayMtl::generateCaps(egl::Caps *outCaps) const 511{ 512 outCaps->textureNPOT = true; 513} 514 515void DisplayMtl::initializeFrontendFeatures(angle::FrontendFeatures *features) const 516{ 517 // The Metal backend's handling of compile is thread-safe 518 ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, true); 519 520 // The link job in this backend references gl::Context and ContextMtl, and thread-safety is not 521 // guaranteed. The link subtasks are safe however, they are still parallelized. 522 // 523 // Once the link jobs are made thread-safe and using mtl::Context, this feature can be removed. 524 ANGLE_FEATURE_CONDITION(features, linkJobIsThreadSafe, false); 525} 526 527void DisplayMtl::populateFeatureList(angle::FeatureList *features) 528{ 529 mFeatures.populateFeatureList(features); 530} 531 532EGLenum DisplayMtl::EGLDrawingBufferTextureTarget() 533{ 534 // TODO(anglebug.com/6395): Apple's implementation conditionalized this on 535 // MacCatalyst and whether it was running on ARM64 or X64, preferring 536 // EGL_TEXTURE_RECTANGLE_ANGLE. Metal can bind IOSurfaces to regular 2D 537 // textures, and rectangular textures don't work in the SPIR-V Metal 538 // backend, so for the time being use EGL_TEXTURE_2D on all platforms. 539 return EGL_TEXTURE_2D; 540} 541 542egl::ConfigSet DisplayMtl::generateConfigs() 543{ 544 // NOTE(hqle): generate more config permutations 545 egl::ConfigSet configs; 546 547 const gl::Version &maxVersion = getMaxSupportedESVersion(); 548 ASSERT(maxVersion >= gl::Version(2, 0)); 549 bool supportsES3 = maxVersion >= gl::Version(3, 0); 550 551 egl::Config config; 552 553 // Native stuff 554 config.nativeVisualID = 0; 555 config.nativeVisualType = 0; 556 config.nativeRenderable = EGL_TRUE; 557 558 config.colorBufferType = EGL_RGB_BUFFER; 559 config.luminanceSize = 0; 560 config.alphaMaskSize = 0; 561 562 config.transparentType = EGL_NONE; 563 564 // Pbuffer 565 config.bindToTextureTarget = EGLDrawingBufferTextureTarget(); 566 config.maxPBufferWidth = 4096; 567 config.maxPBufferHeight = 4096; 568 config.maxPBufferPixels = 4096 * 4096; 569 570 // Caveat 571 config.configCaveat = EGL_NONE; 572 573 // Misc 574 config.sampleBuffers = 0; 575 config.samples = 0; 576 config.level = 0; 577 config.bindToTextureRGB = EGL_FALSE; 578 config.bindToTextureRGBA = EGL_TRUE; 579 580 config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; 581 582#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 583 config.minSwapInterval = 0; 584 config.maxSwapInterval = 1; 585#else 586 config.minSwapInterval = 1; 587 config.maxSwapInterval = 1; 588#endif 589 590 config.renderTargetFormat = GL_RGBA8; 591 592 config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); 593 config.renderableType = config.conformant; 594 595 config.matchNativePixmap = EGL_NONE; 596 597 config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; 598 599 constexpr int samplesSupported[] = {0, 4}; 600 601 for (int samples : samplesSupported) 602 { 603 config.samples = samples; 604 config.sampleBuffers = (samples == 0) ? 0 : 1; 605 606 // Buffer sizes 607 config.redSize = 8; 608 config.greenSize = 8; 609 config.blueSize = 8; 610 config.alphaSize = 8; 611 config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize; 612 613 // With DS 614 config.depthSize = 24; 615 config.stencilSize = 8; 616 config.depthStencilFormat = GL_DEPTH24_STENCIL8; 617 618 configs.add(config); 619 620 // With D 621 config.depthSize = 24; 622 config.stencilSize = 0; 623 config.depthStencilFormat = GL_DEPTH_COMPONENT24; 624 configs.add(config); 625 626 // With S 627 config.depthSize = 0; 628 config.stencilSize = 8; 629 config.depthStencilFormat = GL_STENCIL_INDEX8; 630 configs.add(config); 631 632 // No DS 633 config.depthSize = 0; 634 config.stencilSize = 0; 635 config.depthStencilFormat = GL_NONE; 636 configs.add(config); 637 638 // Tests like dEQP-GLES2.functional.depth_range.* assume EGL_DEPTH_SIZE is properly set even 639 // if renderConfig attributes are set to glu::RenderConfig::DONT_CARE 640 config.depthSize = GetDepthSize(config.depthStencilFormat); 641 config.stencilSize = GetStencilSize(config.depthStencilFormat); 642 configs.add(config); 643 } 644 645 return configs; 646} 647 648bool DisplayMtl::isValidNativeWindow(EGLNativeWindowType window) const 649{ 650 ANGLE_MTL_OBJC_SCOPE 651 { 652 NSObject *layer = (__bridge NSObject *)(window); 653 return [layer isKindOfClass:[CALayer class]]; 654 } 655} 656 657egl::Error DisplayMtl::validateClientBuffer(const egl::Config *configuration, 658 EGLenum buftype, 659 EGLClientBuffer clientBuffer, 660 const egl::AttributeMap &attribs) const 661{ 662 switch (buftype) 663 { 664 case EGL_IOSURFACE_ANGLE: 665 if (!IOSurfaceSurfaceMtl::ValidateAttributes(clientBuffer, attribs)) 666 { 667 return egl::EglBadAttribute(); 668 } 669 break; 670 default: 671 UNREACHABLE(); 672 return egl::EglBadAttribute(); 673 } 674 return egl::NoError(); 675} 676 677egl::Error DisplayMtl::validateImageClientBuffer(const gl::Context *context, 678 EGLenum target, 679 EGLClientBuffer clientBuffer, 680 const egl::AttributeMap &attribs) const 681{ 682 switch (target) 683 { 684 case EGL_METAL_TEXTURE_ANGLE: 685 return TextureImageSiblingMtl::ValidateClientBuffer(this, clientBuffer, attribs); 686 default: 687 UNREACHABLE(); 688 return egl::EglBadAttribute(); 689 } 690} 691 692gl::Caps DisplayMtl::getNativeCaps() const 693{ 694 ensureCapsInitialized(); 695 return mNativeCaps; 696} 697const gl::TextureCapsMap &DisplayMtl::getNativeTextureCaps() const 698{ 699 ensureCapsInitialized(); 700 return mNativeTextureCaps; 701} 702const gl::Extensions &DisplayMtl::getNativeExtensions() const 703{ 704 ensureCapsInitialized(); 705 return mNativeExtensions; 706} 707const gl::Limitations &DisplayMtl::getNativeLimitations() const 708{ 709 ensureCapsInitialized(); 710 return mNativeLimitations; 711} 712const ShPixelLocalStorageOptions &DisplayMtl::getNativePixelLocalStorageOptions() const 713{ 714 ensureCapsInitialized(); 715 return mNativePLSOptions; 716} 717 718void DisplayMtl::ensureCapsInitialized() const 719{ 720 if (mCapsInitialized) 721 { 722 return; 723 } 724 725 mCapsInitialized = true; 726 727 // Reset 728 mNativeCaps = gl::Caps(); 729 730 // Fill extension and texture caps 731 initializeExtensions(); 732 initializeTextureCaps(); 733 734 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf 735 mNativeCaps.maxElementIndex = std::numeric_limits<GLuint>::max() - 1; 736 mNativeCaps.max3DTextureSize = 2048; 737#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 738 mNativeCaps.max2DTextureSize = 16384; 739 // On macOS exclude [[position]] from maxVaryingVectors. 740 mNativeCaps.maxVaryingVectors = 31 - 1; 741 mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 124 - 4; 742#else 743 if (supportsAppleGPUFamily(3)) 744 { 745 mNativeCaps.max2DTextureSize = 16384; 746 mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 124; 747 mNativeCaps.maxVaryingVectors = mNativeCaps.maxVertexOutputComponents / 4; 748 } 749 else 750 { 751 mNativeCaps.max2DTextureSize = 8192; 752 mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 60; 753 mNativeCaps.maxVaryingVectors = mNativeCaps.maxVertexOutputComponents / 4; 754 } 755#endif 756 757 mNativeCaps.maxArrayTextureLayers = 2048; 758 mNativeCaps.maxLODBias = std::log2(mNativeCaps.max2DTextureSize) + 1; 759 mNativeCaps.maxCubeMapTextureSize = mNativeCaps.max2DTextureSize; 760 mNativeCaps.maxRenderbufferSize = mNativeCaps.max2DTextureSize; 761 mNativeCaps.minAliasedPointSize = 1; 762 // NOTE(hqle): Metal has some problems drawing big point size even though 763 // Metal-Feature-Set-Tables.pdf says that max supported point size is 511. We limit it to 64 764 // for now. http://anglebug.com/4816 765 766 // NOTE(kpiddington): This seems to be fixed in macOS Monterey 767 if (ANGLE_APPLE_AVAILABLE_XCI(12.0, 15.0, 15.0)) 768 { 769 mNativeCaps.maxAliasedPointSize = 511; 770 } 771 else 772 { 773 mNativeCaps.maxAliasedPointSize = 64; 774 } 775 mNativeCaps.minAliasedLineWidth = 1.0f; 776 mNativeCaps.maxAliasedLineWidth = 1.0f; 777 778 if (supportsEitherGPUFamily(2, 1) && !mFeatures.limitMaxDrawBuffersForTesting.enabled) 779 { 780 mNativeCaps.maxDrawBuffers = mtl::kMaxRenderTargets; 781 mNativeCaps.maxColorAttachments = mtl::kMaxRenderTargets; 782 } 783 else 784 { 785 mNativeCaps.maxDrawBuffers = mtl::kMaxRenderTargetsOlderGPUFamilies; 786 mNativeCaps.maxColorAttachments = mtl::kMaxRenderTargetsOlderGPUFamilies; 787 } 788 ASSERT(static_cast<uint32_t>(mNativeCaps.maxDrawBuffers) <= mtl::kMaxRenderTargets); 789 ASSERT(static_cast<uint32_t>(mNativeCaps.maxColorAttachments) <= mtl::kMaxRenderTargets); 790 791 mNativeCaps.maxFramebufferWidth = mNativeCaps.max2DTextureSize; 792 mNativeCaps.maxFramebufferHeight = mNativeCaps.max2DTextureSize; 793 mNativeCaps.maxViewportWidth = mNativeCaps.max2DTextureSize; 794 mNativeCaps.maxViewportHeight = mNativeCaps.max2DTextureSize; 795 796 bool isCatalyst = TARGET_OS_MACCATALYST; 797 798 mMaxColorTargetBits = mtl::kMaxColorTargetBitsApple1To3; 799 if (supportsMacGPUFamily(1) || isCatalyst) 800 { 801 mMaxColorTargetBits = mtl::kMaxColorTargetBitsMacAndCatalyst; 802 } 803 else if (supportsAppleGPUFamily(4)) 804 { 805 mMaxColorTargetBits = mtl::kMaxColorTargetBitsApple4Plus; 806 } 807 808 if (mFeatures.limitMaxColorTargetBitsForTesting.enabled) 809 { 810 // Set so we have enough for RGBA8 on every attachment 811 // but not enough for RGBA32UI. 812 mMaxColorTargetBits = mNativeCaps.maxColorAttachments * 32; 813 } 814 815 // MSAA 816 mNativeCaps.maxSamples = mFormatTable.getMaxSamples(); 817 mNativeCaps.maxSampleMaskWords = 0; 818 mNativeCaps.maxColorTextureSamples = mNativeCaps.maxSamples; 819 mNativeCaps.maxDepthTextureSamples = mNativeCaps.maxSamples; 820 mNativeCaps.maxIntegerSamples = 1; 821 822 mNativeCaps.maxVertexAttributes = mtl::kMaxVertexAttribs; 823 mNativeCaps.maxVertexAttribBindings = mtl::kMaxVertexAttribs; 824 mNativeCaps.maxVertexAttribRelativeOffset = std::numeric_limits<GLint>::max(); 825 mNativeCaps.maxVertexAttribStride = std::numeric_limits<GLint>::max(); 826 827 // glGet() use signed integer as parameter so we have to use GLint's max here, not GLuint. 828 mNativeCaps.maxElementsIndices = std::numeric_limits<GLint>::max(); 829 mNativeCaps.maxElementsVertices = std::numeric_limits<GLint>::max(); 830 831 // Looks like all floats are IEEE according to the docs here: 832 mNativeCaps.vertexHighpFloat.setIEEEFloat(); 833 mNativeCaps.vertexMediumpFloat.setIEEEFloat(); 834 mNativeCaps.vertexLowpFloat.setIEEEFloat(); 835 mNativeCaps.fragmentHighpFloat.setIEEEFloat(); 836 mNativeCaps.fragmentMediumpFloat.setIEEEFloat(); 837 mNativeCaps.fragmentLowpFloat.setIEEEFloat(); 838 839 mNativeCaps.vertexHighpInt.setTwosComplementInt(32); 840 mNativeCaps.vertexMediumpInt.setTwosComplementInt(32); 841 mNativeCaps.vertexLowpInt.setTwosComplementInt(32); 842 mNativeCaps.fragmentHighpInt.setTwosComplementInt(32); 843 mNativeCaps.fragmentMediumpInt.setTwosComplementInt(32); 844 mNativeCaps.fragmentLowpInt.setTwosComplementInt(32); 845 846 GLuint maxDefaultUniformVectors = mtl::kDefaultUniformsMaxSize / (sizeof(GLfloat) * 4); 847 848 const GLuint maxDefaultUniformComponents = maxDefaultUniformVectors * 4; 849 850 // Uniforms are implemented using a uniform buffer, so the max number of uniforms we can 851 // support is the max buffer range divided by the size of a single uniform (4X float). 852 mNativeCaps.maxVertexUniformVectors = maxDefaultUniformVectors; 853 mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex] = maxDefaultUniformComponents; 854 mNativeCaps.maxFragmentUniformVectors = maxDefaultUniformVectors; 855 mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxDefaultUniformComponents; 856 857 mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = mtl::kMaxShaderUBOs; 858 mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = mtl::kMaxShaderUBOs; 859 mNativeCaps.maxCombinedUniformBlocks = mtl::kMaxGLUBOBindings; 860 861 // Note that we currently implement textures as combined image+samplers, so the limit is 862 // the minimum of supported samplers and sampled images. 863 mNativeCaps.maxCombinedTextureImageUnits = mtl::kMaxGLSamplerBindings; 864 mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Fragment] = mtl::kMaxShaderSamplers; 865 mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Vertex] = mtl::kMaxShaderSamplers; 866 867 // No info from Metal given, use default GLES3 spec values: 868 mNativeCaps.minProgramTexelOffset = -8; 869 mNativeCaps.maxProgramTexelOffset = 7; 870 871 // NOTE(hqle): support storage buffer. 872 const uint32_t maxPerStageStorageBuffers = 0; 873 mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex] = maxPerStageStorageBuffers; 874 mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Fragment] = maxPerStageStorageBuffers; 875 mNativeCaps.maxCombinedShaderStorageBlocks = maxPerStageStorageBuffers; 876 877 // Fill in additional limits for UBOs and SSBOs. 878 mNativeCaps.maxUniformBufferBindings = mNativeCaps.maxCombinedUniformBlocks; 879 mNativeCaps.maxUniformBlockSize = mtl::kMaxUBOSize; // Default according to GLES 3.0 spec. 880 if (supportsAppleGPUFamily(1)) 881 { 882 mNativeCaps.uniformBufferOffsetAlignment = 883 16; // on Apple based GPU's We can ignore data types when setting constant buffer 884 // alignment at 16. 885 } 886 else 887 { 888 mNativeCaps.uniformBufferOffsetAlignment = 889 256; // constant buffers on all other GPUs must be aligned to 256. 890 } 891 892 mNativeCaps.maxShaderStorageBufferBindings = 0; 893 mNativeCaps.maxShaderStorageBlockSize = 0; 894 mNativeCaps.shaderStorageBufferOffsetAlignment = 0; 895 896 // UBO plus default uniform limits 897 const uint32_t maxCombinedUniformComponents = 898 maxDefaultUniformComponents + mtl::kMaxUBOSize * mtl::kMaxShaderUBOs / 4; 899 for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) 900 { 901 mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxCombinedUniformComponents; 902 } 903 904 mNativeCaps.maxCombinedShaderOutputResources = 0; 905 906 mNativeCaps.maxTransformFeedbackInterleavedComponents = 907 gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS; 908 mNativeCaps.maxTransformFeedbackSeparateAttributes = 909 gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS; 910 mNativeCaps.maxTransformFeedbackSeparateComponents = 911 gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS; 912 913 // GL_OES_get_program_binary 914 mNativeCaps.programBinaryFormats.push_back(GL_PROGRAM_BINARY_ANGLE); 915 916 // GL_APPLE_clip_distance / GL_ANGLE_clip_cull_distance 917 mNativeCaps.maxClipDistances = 8; 918 919 // Metal doesn't support GL_TEXTURE_COMPARE_MODE=GL_NONE for shadow samplers 920 mNativeLimitations.noShadowSamplerCompareModeNone = true; 921 922 // Apple platforms require PVRTC1 textures to be squares. 923 mNativeLimitations.squarePvrtc1 = true; 924 925 // Older Metal does not support compressed formats for TEXTURE_3D target. 926 if (ANGLE_APPLE_AVAILABLE_XCI(10.15, 13.1, 13.0)) 927 { 928 mNativeLimitations.noCompressedTexture3D = !supportsEitherGPUFamily(3, 1); 929 } 930 else 931 { 932 mNativeLimitations.noCompressedTexture3D = true; 933 } 934} 935 936void DisplayMtl::initializeExtensions() const 937{ 938 // Reset 939 mNativeExtensions = gl::Extensions(); 940 941 // Enable this for simple buffer readback testing, but some functionality is missing. 942 // NOTE(hqle): Support full mapBufferRangeEXT extension. 943 mNativeExtensions.mapbufferOES = true; 944 mNativeExtensions.mapBufferRangeEXT = true; 945 mNativeExtensions.textureStorageEXT = true; 946 mNativeExtensions.clipControlEXT = true; 947 mNativeExtensions.drawBuffersEXT = true; 948 mNativeExtensions.drawBuffersIndexedEXT = true; 949 mNativeExtensions.drawBuffersIndexedOES = true; 950 mNativeExtensions.fboRenderMipmapOES = true; 951 mNativeExtensions.fragDepthEXT = true; 952 mNativeExtensions.conservativeDepthEXT = true; 953 mNativeExtensions.framebufferBlitANGLE = true; 954 mNativeExtensions.framebufferBlitNV = true; 955 mNativeExtensions.framebufferMultisampleANGLE = true; 956 mNativeExtensions.polygonModeANGLE = true; 957 mNativeExtensions.polygonOffsetClampEXT = true; 958 mNativeExtensions.stencilTexturingANGLE = true; 959 mNativeExtensions.copyTextureCHROMIUM = true; 960 mNativeExtensions.copyCompressedTextureCHROMIUM = false; 961 962#if !ANGLE_PLATFORM_WATCHOS 963 if (@available(iOS 14.0, macOS 10.11, macCatalyst 14.0, tvOS 16.0, *)) 964 { 965 mNativeExtensions.textureMirrorClampToEdgeEXT = true; 966 } 967#endif 968 969 if (ANGLE_APPLE_AVAILABLE_XCI(10.11, 13.1, 11.0)) 970 { 971 mNativeExtensions.depthClampEXT = true; 972 } 973 974 // EXT_debug_marker is not implemented yet, but the entry points must be exposed for the 975 // Metal backend to be used in Chrome (http://anglebug.com/4946) 976 mNativeExtensions.debugMarkerEXT = true; 977 978 mNativeExtensions.robustnessEXT = true; 979 mNativeExtensions.robustnessKHR = true; 980 mNativeExtensions.textureBorderClampOES = false; // not implemented yet 981 mNativeExtensions.multiDrawIndirectEXT = true; 982 mNativeExtensions.translatedShaderSourceANGLE = true; 983 mNativeExtensions.discardFramebufferEXT = true; 984 // TODO(anglebug.com/6395): Apple's implementation exposed 985 // mNativeExtensions.textureRectangle = true here and 986 // EGL_TEXTURE_RECTANGLE_ANGLE as the eglBindTexImage texture target on 987 // macOS. This no longer seems necessary as IOSurfaces can be bound to 988 // regular 2D textures with Metal, and causes other problems such as 989 // breaking the SPIR-V Metal compiler. 990 991 // TODO(anglebug.com/6395): figure out why WebGL drawing buffer 992 // creation fails on macOS when the Metal backend advertises the 993 // EXT_multisampled_render_to_texture extension. 994 // TODO(anglebug.com/3107): Metal doesn't implement render to texture 995 // correctly. A texture (if used as a color attachment for a framebuffer) 996 // is always created with sample count == 1, which results in creation of a 997 // render pipeline with the same value. Moreover, if there is a more 998 // sophisticated case and a framebuffer also has a stencil/depth attachment, 999 // it will result in creation of a render pipeline with those attachment's 1000 // sample count, but the texture that was used as a color attachment, will 1001 // still remain with sample count 1. That results in Metal validation error 1002 // if enabled. 1003 mNativeExtensions.multisampledRenderToTextureEXT = false; 1004 1005 // Enable EXT_blend_minmax 1006 mNativeExtensions.blendMinmaxEXT = true; 1007 1008 mNativeExtensions.EGLImageOES = true; 1009 mNativeExtensions.EGLImageExternalOES = false; 1010 // NOTE(hqle): Support GL_OES_EGL_image_external_essl3. 1011 mNativeExtensions.EGLImageExternalEssl3OES = false; 1012 1013 mNativeExtensions.memoryObjectEXT = false; 1014 mNativeExtensions.memoryObjectFdEXT = false; 1015 1016 mNativeExtensions.semaphoreEXT = false; 1017 mNativeExtensions.semaphoreFdEXT = false; 1018 1019 mNativeExtensions.instancedArraysANGLE = true; 1020 mNativeExtensions.instancedArraysEXT = mNativeExtensions.instancedArraysANGLE; 1021 1022 mNativeExtensions.robustBufferAccessBehaviorKHR = false; 1023 1024 mNativeExtensions.EGLSyncOES = false; 1025 1026 mNativeExtensions.occlusionQueryBooleanEXT = true; 1027 1028 mNativeExtensions.disjointTimerQueryEXT = true; 1029 mNativeCaps.queryCounterBitsTimeElapsed = 64; 1030 mNativeCaps.queryCounterBitsTimestamp = 0; 1031 1032 mNativeExtensions.textureFilterAnisotropicEXT = true; 1033 mNativeCaps.maxTextureAnisotropy = 16; 1034 1035 mNativeExtensions.textureNpotOES = true; 1036 1037 mNativeExtensions.texture3DOES = true; 1038 1039 mNativeExtensions.sampleVariablesOES = true; 1040 1041 if (ANGLE_APPLE_AVAILABLE_XCI(11.0, 14.0, 14.0)) 1042 { 1043 mNativeExtensions.shaderMultisampleInterpolationOES = 1044 [mMetalDevice supportsPullModelInterpolation]; 1045 if (mNativeExtensions.shaderMultisampleInterpolationOES) 1046 { 1047 mNativeCaps.subPixelInterpolationOffsetBits = 4; 1048 if (supportsAppleGPUFamily(1)) 1049 { 1050 mNativeCaps.minInterpolationOffset = -0.5f; 1051 mNativeCaps.maxInterpolationOffset = +0.5f; 1052 } 1053 else 1054 { 1055 // On non-Apple GPUs, the actual range is usually 1056 // [-0.5, +0.4375] but due to framebuffer Y-flip 1057 // the effective range for the Y direction will be 1058 // [-0.4375, +0.5] when the default FBO is bound. 1059 mNativeCaps.minInterpolationOffset = -0.4375f; // -0.5 + (2 ^ -4) 1060 mNativeCaps.maxInterpolationOffset = +0.4375f; // +0.5 - (2 ^ -4) 1061 } 1062 } 1063 } 1064 1065 mNativeExtensions.shaderNoperspectiveInterpolationNV = true; 1066 1067 mNativeExtensions.shaderTextureLodEXT = true; 1068 1069 mNativeExtensions.standardDerivativesOES = true; 1070 1071 mNativeExtensions.elementIndexUintOES = true; 1072 1073 // GL_OES_get_program_binary 1074 mNativeExtensions.getProgramBinaryOES = true; 1075 1076 // GL_APPLE_clip_distance 1077 mNativeExtensions.clipDistanceAPPLE = true; 1078 1079 // GL_ANGLE_clip_cull_distance 1080 mNativeExtensions.clipCullDistanceANGLE = true; 1081 1082 // GL_NV_pixel_buffer_object 1083 mNativeExtensions.pixelBufferObjectNV = true; 1084 1085 if (mFeatures.hasEvents.enabled) 1086 { 1087 // MTLSharedEvent is only available since Metal 2.1 1088 1089 // GL_NV_fence 1090 mNativeExtensions.fenceNV = true; 1091 1092 // GL_OES_EGL_sync 1093 mNativeExtensions.EGLSyncOES = true; 1094 1095 // GL_ARB_sync 1096 mNativeExtensions.syncARB = true; 1097 } 1098 1099 // GL_KHR_parallel_shader_compile 1100 mNativeExtensions.parallelShaderCompileKHR = true; 1101 1102 mNativeExtensions.baseInstanceEXT = mFeatures.hasBaseVertexInstancedDraw.enabled; 1103 mNativeExtensions.baseVertexBaseInstanceANGLE = mFeatures.hasBaseVertexInstancedDraw.enabled; 1104 mNativeExtensions.baseVertexBaseInstanceShaderBuiltinANGLE = 1105 mFeatures.hasBaseVertexInstancedDraw.enabled; 1106 1107 // Metal uses the opposite provoking vertex as GLES so emulation is required to use the GLES 1108 // behaviour. Allow users to change the provoking vertex for improved performance. 1109 mNativeExtensions.provokingVertexANGLE = true; 1110 1111 // GL_EXT_blend_func_extended 1112 if (ANGLE_APPLE_AVAILABLE_XCI(10.12, 13.1, 11.0)) 1113 { 1114 mNativeExtensions.blendFuncExtendedEXT = true; 1115 mNativeCaps.maxDualSourceDrawBuffers = 1; 1116 } 1117 1118 // GL_ANGLE_shader_pixel_local_storage. 1119 if (!mFeatures.disableProgrammableBlending.enabled && supportsAppleGPUFamily(1)) 1120 { 1121 // Programmable blending is supported on all Apple GPU families, and is always coherent. 1122 mNativePLSOptions.type = ShPixelLocalStorageType::FramebufferFetch; 1123 1124 // Raster order groups are NOT required to make framebuffer fetch coherent, however, they 1125 // may improve performance by allowing finer grained synchronization (e.g., by assigning 1126 // attachments to different raster order groups when they don't depend on each other). 1127 bool rasterOrderGroupsSupported = 1128 !mFeatures.disableRasterOrderGroups.enabled && supportsAppleGPUFamily(4); 1129 mNativePLSOptions.fragmentSyncType = 1130 rasterOrderGroupsSupported ? ShFragmentSynchronizationType::RasterOrderGroups_Metal 1131 : ShFragmentSynchronizationType::Automatic; 1132 1133 mNativeExtensions.shaderPixelLocalStorageANGLE = true; 1134 mNativeExtensions.shaderPixelLocalStorageCoherentANGLE = true; 1135 } 1136 else 1137 { 1138 MTLReadWriteTextureTier readWriteTextureTier = [mMetalDevice readWriteTextureSupport]; 1139 if (readWriteTextureTier != MTLReadWriteTextureTierNone) 1140 { 1141 mNativePLSOptions.type = ShPixelLocalStorageType::ImageLoadStore; 1142 1143 // Raster order groups are required to make PLS coherent when using read_write textures. 1144 bool rasterOrderGroupsSupported = !mFeatures.disableRasterOrderGroups.enabled && 1145 [mMetalDevice areRasterOrderGroupsSupported]; 1146 mNativePLSOptions.fragmentSyncType = 1147 rasterOrderGroupsSupported ? ShFragmentSynchronizationType::RasterOrderGroups_Metal 1148 : ShFragmentSynchronizationType::NotSupported; 1149 1150 mNativePLSOptions.supportsNativeRGBA8ImageFormats = 1151 !mFeatures.disableRWTextureTier2Support.enabled && 1152 readWriteTextureTier == MTLReadWriteTextureTier2; 1153 1154 if (rasterOrderGroupsSupported && isAMD()) 1155 { 1156 // anglebug.com/7792 -- [[raster_order_group()]] does not work for read_write 1157 // textures on AMD when the render pass doesn't have a color attachment on slot 0. 1158 // To work around this we attach one of the PLS textures to GL_COLOR_ATTACHMENT0, if 1159 // there isn't one already. 1160 mNativePLSOptions.renderPassNeedsAMDRasterOrderGroupsWorkaround = true; 1161 } 1162 1163 mNativeExtensions.shaderPixelLocalStorageANGLE = true; 1164 mNativeExtensions.shaderPixelLocalStorageCoherentANGLE = rasterOrderGroupsSupported; 1165 1166 // Set up PLS caps here because the higher level context won't have enough info to set 1167 // them up itself. Shader images and other ES3.1 caps aren't fully exposed yet. 1168 static_assert(mtl::kMaxShaderImages >= 1169 gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES); 1170 mNativeCaps.maxPixelLocalStoragePlanes = 1171 gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; 1172 mNativeCaps.maxColorAttachmentsWithActivePixelLocalStorage = 1173 gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; 1174 mNativeCaps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes = 1175 gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES + 1176 gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; 1177 mNativeCaps.maxShaderImageUniforms[gl::ShaderType::Fragment] = 1178 gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; 1179 mNativeCaps.maxImageUnits = gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; 1180 } 1181 } 1182 // "The GPUs in Apple3 through Apple8 families only support memory barriers for compute command 1183 // encoders, and for vertex-to-vertex and vertex-to-fragment stages of render command encoders." 1184 mHasFragmentMemoryBarriers = !supportsAppleGPUFamily(3); 1185} 1186 1187void DisplayMtl::initializeTextureCaps() const 1188{ 1189 mNativeTextureCaps.clear(); 1190 1191 mFormatTable.generateTextureCaps(this, &mNativeTextureCaps); 1192 1193 // Re-verify texture extensions. 1194 mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps); 1195 1196 // When ETC2/EAC formats are natively supported, enable ANGLE-specific extension string to 1197 // expose them to WebGL. In other case, mark potentially-available ETC1 extension as 1198 // emulated. 1199 if (supportsAppleGPUFamily(1) && gl::DetermineCompressedTextureETCSupport(mNativeTextureCaps)) 1200 { 1201 mNativeExtensions.compressedTextureEtcANGLE = true; 1202 } 1203 else 1204 { 1205 mNativeLimitations.emulatedEtc1 = true; 1206 } 1207 1208 // Enable EXT_compressed_ETC1_RGB8_sub_texture if ETC1 is supported. 1209 mNativeExtensions.compressedETC1RGB8SubTextureEXT = 1210 mNativeExtensions.compressedETC1RGB8TextureOES; 1211 1212 // Enable ASTC sliced 3D, requires MTLGPUFamilyApple3 1213 if (supportsAppleGPUFamily(3) && mNativeExtensions.textureCompressionAstcLdrKHR) 1214 { 1215 mNativeExtensions.textureCompressionAstcSliced3dKHR = true; 1216 } 1217 1218 // Enable ASTC HDR, requires MTLGPUFamilyApple6 1219 if (supportsAppleGPUFamily(6) && mNativeExtensions.textureCompressionAstcLdrKHR) 1220 { 1221 mNativeExtensions.textureCompressionAstcHdrKHR = true; 1222 } 1223 1224 // Disable all depth buffer and stencil buffer readback extensions until we need them 1225 mNativeExtensions.readDepthNV = false; 1226 mNativeExtensions.readStencilNV = false; 1227 mNativeExtensions.depthBufferFloat2NV = false; 1228} 1229 1230void DisplayMtl::initializeLimitations() 1231{ 1232 mNativeLimitations.noVertexAttributeAliasing = true; 1233} 1234 1235void DisplayMtl::initializeFeatures() 1236{ 1237 bool isOSX = TARGET_OS_OSX; 1238 bool isCatalyst = TARGET_OS_MACCATALYST; 1239 bool isSimulator = TARGET_OS_SIMULATOR; 1240 bool isARM = ANGLE_APPLE_IS_ARM; 1241 1242 ApplyFeatureOverrides(&mFeatures, getState().featureOverrides); 1243 if (mState.featureOverrides.allDisabled) 1244 { 1245 return; 1246 } 1247 1248 ANGLE_FEATURE_CONDITION((&mFeatures), allowGenMultipleMipsPerPass, true); 1249 ANGLE_FEATURE_CONDITION((&mFeatures), forceBufferGPUStorage, false); 1250 ANGLE_FEATURE_CONDITION((&mFeatures), hasExplicitMemBarrier, 1251 supportsMetal2_1() && (isOSX || isCatalyst) && !isARM); 1252 ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthAutoResolve, supportsEitherGPUFamily(3, 2)); 1253 ANGLE_FEATURE_CONDITION((&mFeatures), hasStencilAutoResolve, supportsEitherGPUFamily(5, 2)); 1254 ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve, 1255 supportsEitherGPUFamily(3, 1)); 1256 1257 ANGLE_FEATURE_CONDITION((&mFeatures), allowRuntimeSamplerCompareMode, 1258 supportsEitherGPUFamily(3, 1)); 1259 // AMD does not support sample_compare_grad 1260 ANGLE_FEATURE_CONDITION((&mFeatures), allowSamplerCompareGradient, 1261 supportsEitherGPUFamily(3, 1) && !isAMD()); 1262 ANGLE_FEATURE_CONDITION((&mFeatures), allowSamplerCompareLod, supportsEitherGPUFamily(3, 1)); 1263 1264 // http://anglebug.com/4919 1265 // Stencil blit shader is not compiled on Intel & NVIDIA, need investigation. 1266 ANGLE_FEATURE_CONDITION((&mFeatures), hasShaderStencilOutput, 1267 supportsMetal2_1() && !isIntel() && !isNVIDIA()); 1268 1269 ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle, 1270 supportsMetal2_2() && supportsEitherGPUFamily(3, 2) && !isSimulator); 1271 1272 ANGLE_FEATURE_CONDITION((&mFeatures), avoidStencilTextureSwizzle, isIntel()); 1273 1274 // http://crbug.com/1136673 1275 // Fence sync is flaky on Nvidia 1276 ANGLE_FEATURE_CONDITION((&mFeatures), hasEvents, supportsMetal2_1() && !isNVIDIA()); 1277 1278 ANGLE_FEATURE_CONDITION((&mFeatures), hasCheapRenderPass, (isOSX || isCatalyst) && !isARM); 1279 1280 // http://anglebug.com/5235 1281 // D24S8 is unreliable on AMD. 1282 ANGLE_FEATURE_CONDITION((&mFeatures), forceD24S8AsUnsupported, isAMD()); 1283 1284 // Base Vertex drawing is only supported since GPU family 3. 1285 ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw, 1286 isOSX || isCatalyst || supportsAppleGPUFamily(3)); 1287 1288 ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch, 1289 isOSX || isCatalyst || supportsAppleGPUFamily(4)); 1290 1291 ANGLE_FEATURE_CONDITION((&mFeatures), allowSeparateDepthStencilBuffers, 1292 !isOSX && !isCatalyst && !isSimulator); 1293 ANGLE_FEATURE_CONDITION((&mFeatures), emulateTransformFeedback, true); 1294 1295 ANGLE_FEATURE_CONDITION((&mFeatures), intelExplicitBoolCastWorkaround, 1296 isIntel() && GetMacOSVersion() < OSVersion(11, 0, 0)); 1297 ANGLE_FEATURE_CONDITION((&mFeatures), intelDisableFastMath, 1298 isIntel() && GetMacOSVersion() < OSVersion(12, 0, 0)); 1299 1300 ANGLE_FEATURE_CONDITION((&mFeatures), emulateAlphaToCoverage, 1301 isSimulator || !supportsAppleGPUFamily(1)); 1302 1303 ANGLE_FEATURE_CONDITION((&mFeatures), writeHelperSampleMask, supportsAppleGPUFamily(1)); 1304 1305 ANGLE_FEATURE_CONDITION((&mFeatures), multisampleColorFormatShaderReadWorkaround, isAMD()); 1306 ANGLE_FEATURE_CONDITION((&mFeatures), copyIOSurfaceToNonIOSurfaceForReadOptimization, 1307 isIntel() || isAMD()); 1308 ANGLE_FEATURE_CONDITION((&mFeatures), copyTextureToBufferForReadOptimization, isAMD()); 1309 1310 ANGLE_FEATURE_CONDITION((&mFeatures), forceNonCSBaseMipmapGeneration, isIntel()); 1311 1312 ANGLE_FEATURE_CONDITION((&mFeatures), preemptivelyStartProvokingVertexCommandBuffer, isAMD()); 1313 1314 ANGLE_FEATURE_CONDITION((&mFeatures), alwaysUseStagedBufferUpdates, isAMD()); 1315 ANGLE_FEATURE_CONDITION((&mFeatures), alwaysUseManagedStorageModeForBuffers, isAMD()); 1316 1317 ANGLE_FEATURE_CONDITION((&mFeatures), alwaysUseSharedStorageModeForBuffers, isIntel()); 1318 ANGLE_FEATURE_CONDITION((&mFeatures), useShadowBuffersWhenAppropriate, isIntel()); 1319 1320 // At least one of these must not be set. 1321 ASSERT(!mFeatures.alwaysUseManagedStorageModeForBuffers.enabled || 1322 !mFeatures.alwaysUseSharedStorageModeForBuffers.enabled); 1323 1324 ANGLE_FEATURE_CONDITION((&mFeatures), uploadDataToIosurfacesWithStagingBuffers, isAMD()); 1325 1326 // Render passes can be rendered without attachments on Apple4 , mac2 hardware. 1327 ANGLE_FEATURE_CONDITION(&(mFeatures), allowRenderpassWithoutAttachment, 1328 supportsEitherGPUFamily(4, 2)); 1329 1330 ANGLE_FEATURE_CONDITION((&mFeatures), enableInMemoryMtlLibraryCache, true); 1331 ANGLE_FEATURE_CONDITION((&mFeatures), enableParallelMtlLibraryCompilation, true); 1332 1333 // Uploading texture data via staging buffers improves performance on all tested systems. 1334 // http://anglebug.com/8092: Disabled on intel due to some texture formats uploading incorrectly 1335 // with staging buffers 1336 ANGLE_FEATURE_CONDITION(&mFeatures, alwaysPreferStagedTextureUploads, true); 1337 ANGLE_FEATURE_CONDITION(&mFeatures, disableStagedInitializationOfPackedTextureFormats, 1338 isIntel() || isAMD()); 1339 1340 ANGLE_FEATURE_CONDITION((&mFeatures), generateShareableShaders, true); 1341 1342 // http://anglebug.com/8170: NVIDIA GPUs are unsupported due to scarcity of the hardware. 1343 ANGLE_FEATURE_CONDITION((&mFeatures), disableMetalOnNvidia, true); 1344 1345 // The AMDMTLBronzeDriver seems to have bugs flushing vertex data to the GPU during some kinds 1346 // of buffer uploads which require a flush to work around. 1347 ANGLE_FEATURE_CONDITION((&mFeatures), flushAfterStreamVertexData, isAMDBronzeDriver()); 1348 1349 // TODO(anglebug.com/7952): GPUs that don't support Mac GPU family 2 or greater are 1350 // unsupported by the Metal backend. 1351 ANGLE_FEATURE_CONDITION((&mFeatures), requireGpuFamily2, true); 1352 1353 // anglebug.com/8258 Builtin shaders currently require MSL 2.1 1354 ANGLE_FEATURE_CONDITION((&mFeatures), requireMsl21, true); 1355 1356 // http://anglebug.com/8311: Rescope global variables which are only used in one function to be 1357 // function local. Disabled on AMD FirePro devices: http://anglebug.com/8317 1358 ANGLE_FEATURE_CONDITION((&mFeatures), rescopeGlobalVariables, !isAMDFireProDevice()); 1359 1360 // Apple-specific pre-transform for explicit cubemap derivatives 1361 ANGLE_FEATURE_CONDITION((&mFeatures), preTransformTextureCubeGradDerivatives, 1362 supportsAppleGPUFamily(1)); 1363 1364 // On tile-based GPUs, always resolving MSAA render buffers to single-sampled 1365 // is preferred. Because it would save bandwidth by avoiding the cost of storing the MSAA 1366 // textures to memory. Traditional desktop GPUs almost always store MSAA textures to memory 1367 // anyway, so this feature would have no benefit besides adding additional resolve step and 1368 // memory overhead of the hidden single-sampled textures. 1369 ANGLE_FEATURE_CONDITION((&mFeatures), alwaysResolveMultisampleRenderBuffers, isARM); 1370 1371 // Metal compiler optimizations may remove infinite loops causing crashes later in shader 1372 // execution. http://crbug.com/1513738 1373 // Disabled on Mac11 due to test failures. http://crbug.com/1522730 1374 ANGLE_FEATURE_CONDITION((&mFeatures), injectAsmStatementIntoLoopBodies, 1375 GetMacOSVersion() >= OSVersion(12, 0, 0)); 1376} 1377 1378angle::Result DisplayMtl::initializeShaderLibrary() 1379{ 1380 mtl::AutoObjCPtr<NSError *> err = nil; 1381#if ANGLE_METAL_XCODE_BUILDS_SHADERS || ANGLE_METAL_HAS_PREBUILT_INTERNAL_SHADERS 1382 mDefaultShaders = mtl::CreateShaderLibraryFromBinary(getMetalDevice(), gDefaultMetallib, 1383 std::size(gDefaultMetallib), &err); 1384#else 1385 mDefaultShaders = mtl::CreateShaderLibrary(getMetalDevice(), gDefaultMetallibSrc, 1386 std::size(gDefaultMetallibSrc), &err); 1387#endif 1388 1389 if (err) 1390 { 1391 ERR() << "Internal error: " << err.get().localizedDescription.UTF8String; 1392 return angle::Result::Stop; 1393 } 1394 1395 return angle::Result::Continue; 1396} 1397 1398id<MTLLibrary> DisplayMtl::getDefaultShadersLib() 1399{ 1400 return mDefaultShaders; 1401} 1402 1403bool DisplayMtl::supportsAppleGPUFamily(uint8_t iOSFamily) const 1404{ 1405 return mtl::SupportsAppleGPUFamily(getMetalDevice(), iOSFamily); 1406} 1407 1408bool DisplayMtl::supportsMacGPUFamily(uint8_t macFamily) const 1409{ 1410 return mtl::SupportsMacGPUFamily(getMetalDevice(), macFamily); 1411} 1412 1413bool DisplayMtl::supportsEitherGPUFamily(uint8_t iOSFamily, uint8_t macFamily) const 1414{ 1415 return supportsAppleGPUFamily(iOSFamily) || supportsMacGPUFamily(macFamily); 1416} 1417 1418bool DisplayMtl::supportsMetal2_1() const 1419{ 1420 if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.1, 12.0)) 1421 { 1422 return true; 1423 } 1424 else 1425 { 1426 return false; 1427 } 1428} 1429bool DisplayMtl::supportsMetal2_2() const 1430{ 1431 if (ANGLE_APPLE_AVAILABLE_XCI(10.15, 13.1, 13.0)) 1432 { 1433 return true; 1434 } 1435 else 1436 { 1437 return false; 1438 } 1439} 1440 1441bool DisplayMtl::supports32BitFloatFiltering() const 1442{ 1443#if (defined(__MAC_11_0) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_11_0) || \ 1444 (defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_14_0) || \ 1445 (defined(__TVOS_14_0) && __TV_OS_VERSION_MIN_REQUIRED >= __TVOS_14_0) 1446 if (@available(ios 14.0, macOS 11.0, *)) 1447 { 1448 return [mMetalDevice supports32BitFloatFiltering]; 1449 } 1450 else 1451#endif 1452 { 1453 return supportsMacGPUFamily(1); 1454 } 1455} 1456 1457bool DisplayMtl::supportsDepth24Stencil8PixelFormat() const 1458{ 1459#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1460 return [mMetalDevice isDepth24Stencil8PixelFormatSupported]; 1461#else 1462 return false; 1463#endif 1464} 1465bool DisplayMtl::isAMD() const 1466{ 1467 return angle::IsAMD(mMetalDeviceVendorId); 1468} 1469bool DisplayMtl::isAMDBronzeDriver() const 1470{ 1471 if (!isAMD()) 1472 { 1473 return false; 1474 } 1475 1476 if (mComputedAMDBronze) 1477 { 1478 return mIsAMDBronze; 1479 } 1480 1481 // All devices known to be covered by AMDMTlBronzeDriver. 1482 // 1483 // Note that we can not compare substrings because some devices 1484 // (AMD Radeon Pro 560) are substrings of ones supported by a 1485 // later driver (AMD Radeon Pro 5600M). 1486 NSString *kMTLBronzeDeviceNames[22] = { 1487 @"FirePro D300", @"FirePro D500", @"FirePro D700", @"Radeon R9 M290", 1488 @"Radeon R9 M290X", @"Radeon R9 M370X", @"Radeon R9 M380", @"Radeon R9 M390", 1489 @"Radeon R9 M395", @"Radeon Pro 450", @"Radeon Pro 455", @"Radeon Pro 460", 1490 @"Radeon Pro 555", @"Radeon Pro 555X", @"Radeon Pro 560", @"Radeon Pro 560X", 1491 @"Radeon Pro 570", @"Radeon Pro 570X", @"Radeon Pro 575", @"Radeon Pro 575X", 1492 @"Radeon Pro 580", @"Radeon Pro 580X"}; 1493 1494 for (size_t i = 0; i < ArraySize(kMTLBronzeDeviceNames); ++i) 1495 { 1496 if ([[mMetalDevice name] hasSuffix:kMTLBronzeDeviceNames[i]]) 1497 { 1498 mIsAMDBronze = true; 1499 break; 1500 } 1501 } 1502 1503 mComputedAMDBronze = true; 1504 return mIsAMDBronze; 1505} 1506 1507bool DisplayMtl::isAMDFireProDevice() const 1508{ 1509 if (!isAMD()) 1510 { 1511 return false; 1512 } 1513 1514 return [[mMetalDevice name] containsString:@"FirePro"]; 1515} 1516 1517bool DisplayMtl::isIntel() const 1518{ 1519 return angle::IsIntel(mMetalDeviceVendorId); 1520} 1521 1522bool DisplayMtl::isNVIDIA() const 1523{ 1524 return angle::IsNVIDIA(mMetalDeviceVendorId); 1525} 1526 1527bool DisplayMtl::isSimulator() const 1528{ 1529 return TARGET_OS_SIMULATOR; 1530} 1531 1532#if ANGLE_MTL_EVENT_AVAILABLE 1533mtl::AutoObjCObj<MTLSharedEventListener> DisplayMtl::getOrCreateSharedEventListener() 1534{ 1535 if (!mSharedEventListener) 1536 { 1537 ANGLE_MTL_OBJC_SCOPE 1538 { 1539 mSharedEventListener = [[[MTLSharedEventListener alloc] init] ANGLE_MTL_AUTORELEASE]; 1540 ASSERT(mSharedEventListener); // Failure here most probably means a sandbox issue. 1541 } 1542 } 1543 return mSharedEventListener; 1544} 1545#endif 1546 1547} // namespace rx 1548