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