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