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