• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright (c) 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
11#include "libANGLE/Context.h"
12#include "libANGLE/Display.h"
13#include "libANGLE/Surface.h"
14#include "libANGLE/renderer/glslang_wrapper_utils.h"
15#include "libANGLE/renderer/metal/ContextMtl.h"
16#include "libANGLE/renderer/metal/SurfaceMtl.h"
17#include "libANGLE/renderer/metal/mtl_common.h"
18#include "platform/Platform.h"
19
20#include "EGL/eglext.h"
21
22namespace rx
23{
24
25bool IsMetalDisplayAvailable()
26{
27    // We only support macos 10.13+ and 11 for now. Since they are requirements for Metal 2.0.
28    if (ANGLE_APPLE_AVAILABLE_XCI(10.13, 13.0, 11))
29    {
30        return true;
31    }
32    return false;
33}
34
35DisplayImpl *CreateMetalDisplay(const egl::DisplayState &state)
36{
37    return new DisplayMtl(state);
38}
39
40DisplayMtl::DisplayMtl(const egl::DisplayState &state)
41    : DisplayImpl(state), mUtils(this), mGlslangInitialized(false)
42{}
43
44DisplayMtl::~DisplayMtl() {}
45
46egl::Error DisplayMtl::initialize(egl::Display *display)
47{
48    ASSERT(IsMetalDisplayAvailable());
49
50    angle::Result result = initializeImpl(display);
51    if (result != angle::Result::Continue)
52    {
53        return egl::EglNotInitialized();
54    }
55    return egl::NoError();
56}
57
58angle::Result DisplayMtl::initializeImpl(egl::Display *display)
59{
60    ANGLE_MTL_OBJC_SCOPE
61    {
62        mMetalDevice = [MTLCreateSystemDefaultDevice() ANGLE_MTL_AUTORELEASE];
63        if (!mMetalDevice)
64        {
65            return angle::Result::Stop;
66        }
67
68        mCmdQueue.set([[mMetalDevice.get() newCommandQueue] ANGLE_MTL_AUTORELEASE]);
69
70        mCapsInitialized = false;
71
72        if (!mGlslangInitialized)
73        {
74            GlslangInitialize();
75            mGlslangInitialized = true;
76        }
77
78        if (!mState.featuresAllDisabled)
79        {
80            initializeFeatures();
81        }
82
83        ANGLE_TRY(mFormatTable.initialize(this));
84
85        return mUtils.initialize();
86    }
87}
88
89void DisplayMtl::terminate()
90{
91    for (mtl::TextureRef &nullTex : mNullTextures)
92    {
93        nullTex.reset();
94    }
95    mUtils.onDestroy();
96    mCmdQueue.reset();
97    mMetalDevice     = nil;
98    mCapsInitialized = false;
99
100    if (mGlslangInitialized)
101    {
102        GlslangRelease();
103        mGlslangInitialized = false;
104    }
105}
106
107bool DisplayMtl::testDeviceLost()
108{
109    return false;
110}
111
112egl::Error DisplayMtl::restoreLostDevice(const egl::Display *display)
113{
114    return egl::NoError();
115}
116
117std::string DisplayMtl::getVendorString() const
118{
119    ANGLE_MTL_OBJC_SCOPE
120    {
121        std::string vendorString = "Google Inc.";
122        if (mMetalDevice)
123        {
124            vendorString += " Metal Renderer: ";
125            vendorString += mMetalDevice.get().name.UTF8String;
126        }
127
128        return vendorString;
129    }
130}
131
132DeviceImpl *DisplayMtl::createDevice()
133{
134    UNIMPLEMENTED();
135    return nullptr;
136}
137
138egl::Error DisplayMtl::waitClient(const gl::Context *context)
139{
140    auto contextMtl      = GetImplAs<ContextMtl>(context);
141    angle::Result result = contextMtl->finishCommandBuffer();
142
143    if (result != angle::Result::Continue)
144    {
145        return egl::EglBadAccess();
146    }
147    return egl::NoError();
148}
149
150egl::Error DisplayMtl::waitNative(const gl::Context *context, EGLint engine)
151{
152    UNIMPLEMENTED();
153    return egl::EglBadAccess();
154}
155
156SurfaceImpl *DisplayMtl::createWindowSurface(const egl::SurfaceState &state,
157                                             EGLNativeWindowType window,
158                                             const egl::AttributeMap &attribs)
159{
160    return new SurfaceMtl(this, state, window, attribs);
161}
162
163SurfaceImpl *DisplayMtl::createPbufferSurface(const egl::SurfaceState &state,
164                                              const egl::AttributeMap &attribs)
165{
166    UNIMPLEMENTED();
167    return static_cast<SurfaceImpl *>(0);
168}
169
170SurfaceImpl *DisplayMtl::createPbufferFromClientBuffer(const egl::SurfaceState &state,
171                                                       EGLenum buftype,
172                                                       EGLClientBuffer clientBuffer,
173                                                       const egl::AttributeMap &attribs)
174{
175    UNIMPLEMENTED();
176    return static_cast<SurfaceImpl *>(0);
177}
178
179SurfaceImpl *DisplayMtl::createPixmapSurface(const egl::SurfaceState &state,
180                                             NativePixmapType nativePixmap,
181                                             const egl::AttributeMap &attribs)
182{
183    UNIMPLEMENTED();
184    return static_cast<SurfaceImpl *>(0);
185}
186
187ImageImpl *DisplayMtl::createImage(const egl::ImageState &state,
188                                   const gl::Context *context,
189                                   EGLenum target,
190                                   const egl::AttributeMap &attribs)
191{
192    UNIMPLEMENTED();
193    return nullptr;
194}
195
196rx::ContextImpl *DisplayMtl::createContext(const gl::State &state,
197                                           gl::ErrorSet *errorSet,
198                                           const egl::Config *configuration,
199                                           const gl::Context *shareContext,
200                                           const egl::AttributeMap &attribs)
201{
202    return new ContextMtl(state, errorSet, this);
203}
204
205StreamProducerImpl *DisplayMtl::createStreamProducerD3DTexture(
206    egl::Stream::ConsumerType consumerType,
207    const egl::AttributeMap &attribs)
208{
209    UNIMPLEMENTED();
210    return nullptr;
211}
212
213gl::Version DisplayMtl::getMaxSupportedESVersion() const
214{
215    return mtl::kMaxSupportedGLVersion;
216}
217
218gl::Version DisplayMtl::getMaxConformantESVersion() const
219{
220    return std::min(getMaxSupportedESVersion(), gl::Version(2, 0));
221}
222
223EGLSyncImpl *DisplayMtl::createSync(const egl::AttributeMap &attribs)
224{
225    UNIMPLEMENTED();
226    return nullptr;
227}
228
229egl::Error DisplayMtl::makeCurrent(egl::Surface *drawSurface,
230                                   egl::Surface *readSurface,
231                                   gl::Context *context)
232{
233    if (!context)
234    {
235        return egl::NoError();
236    }
237
238    return egl::NoError();
239}
240
241void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const
242{
243    outExtensions->flexibleSurfaceCompatibility = true;
244}
245
246void DisplayMtl::generateCaps(egl::Caps *outCaps) const {}
247
248void DisplayMtl::populateFeatureList(angle::FeatureList *features) {}
249
250egl::ConfigSet DisplayMtl::generateConfigs()
251{
252    // NOTE(hqle): generate more config permutations
253    egl::ConfigSet configs;
254
255    const gl::Version &maxVersion = getMaxSupportedESVersion();
256    ASSERT(maxVersion >= gl::Version(2, 0));
257    bool supportsES3 = maxVersion >= gl::Version(3, 0);
258
259    egl::Config config;
260
261    // Native stuff
262    config.nativeVisualID   = 0;
263    config.nativeVisualType = 0;
264    config.nativeRenderable = EGL_TRUE;
265
266    config.colorBufferType = EGL_RGB_BUFFER;
267    config.luminanceSize   = 0;
268    config.alphaMaskSize   = 0;
269
270    config.transparentType = EGL_NONE;
271
272    // Pbuffer
273    config.maxPBufferWidth  = 4096;
274    config.maxPBufferHeight = 4096;
275    config.maxPBufferPixels = 4096 * 4096;
276
277    // Caveat
278    config.configCaveat = EGL_NONE;
279
280    // Misc
281    config.sampleBuffers     = 0;
282    config.samples           = 0;
283    config.level             = 0;
284    config.bindToTextureRGB  = EGL_FALSE;
285    config.bindToTextureRGBA = EGL_FALSE;
286
287    config.surfaceType = EGL_WINDOW_BIT;
288
289    config.minSwapInterval = 1;
290    config.maxSwapInterval = 1;
291
292    config.renderTargetFormat = GL_RGBA8;
293    config.depthStencilFormat = GL_DEPTH24_STENCIL8;
294
295    config.conformant     = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0);
296    config.renderableType = config.conformant;
297
298    config.matchNativePixmap = EGL_NONE;
299
300    config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
301
302    // Buffer sizes
303    config.redSize    = 8;
304    config.greenSize  = 8;
305    config.blueSize   = 8;
306    config.alphaSize  = 8;
307    config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
308
309    // With DS
310    config.depthSize   = 24;
311    config.stencilSize = 8;
312    configs.add(config);
313
314    // With D
315    config.depthSize   = 24;
316    config.stencilSize = 0;
317    configs.add(config);
318
319    // With S
320    config.depthSize   = 0;
321    config.stencilSize = 8;
322    configs.add(config);
323
324    // No DS
325    config.depthSize   = 0;
326    config.stencilSize = 0;
327    configs.add(config);
328
329    return configs;
330}
331
332bool DisplayMtl::isValidNativeWindow(EGLNativeWindowType window) const
333{
334    NSObject *layer = (__bridge NSObject *)(window);
335    return [layer isKindOfClass:[CALayer class]];
336}
337
338std::string DisplayMtl::getRendererDescription() const
339{
340    ANGLE_MTL_OBJC_SCOPE
341    {
342        std::string desc = "Metal Renderer";
343
344        if (mMetalDevice)
345        {
346            desc += ": ";
347            desc += mMetalDevice.get().name.UTF8String;
348        }
349
350        return desc;
351    }
352}
353
354gl::Caps DisplayMtl::getNativeCaps() const
355{
356    ensureCapsInitialized();
357    return mNativeCaps;
358}
359const gl::TextureCapsMap &DisplayMtl::getNativeTextureCaps() const
360{
361    ensureCapsInitialized();
362    return mNativeTextureCaps;
363}
364const gl::Extensions &DisplayMtl::getNativeExtensions() const
365{
366    ensureCapsInitialized();
367    return mNativeExtensions;
368}
369
370const mtl::TextureRef &DisplayMtl::getNullTexture(const gl::Context *context, gl::TextureType type)
371{
372    // TODO(hqle): Use rx::IncompleteTextureSet.
373    ContextMtl *contextMtl = mtl::GetImpl(context);
374    if (!mNullTextures[type])
375    {
376        // initialize content with zeros
377        MTLRegion region           = MTLRegionMake2D(0, 0, 1, 1);
378        const uint8_t zeroPixel[4] = {0, 0, 0, 255};
379
380        const auto &rgbaFormat = getPixelFormat(angle::FormatID::R8G8B8A8_UNORM);
381
382        switch (type)
383        {
384            case gl::TextureType::_2D:
385                (void)(mtl::Texture::Make2DTexture(contextMtl, rgbaFormat, 1, 1, 1, false, false,
386                                                   &mNullTextures[type]));
387                mNullTextures[type]->replaceRegion(contextMtl, region, 0, 0, zeroPixel,
388                                                   sizeof(zeroPixel));
389                break;
390            case gl::TextureType::CubeMap:
391                (void)(mtl::Texture::MakeCubeTexture(contextMtl, rgbaFormat, 1, 1, false, false,
392                                                     &mNullTextures[type]));
393                for (int f = 0; f < 6; ++f)
394                {
395                    mNullTextures[type]->replaceRegion(contextMtl, region, 0, f, zeroPixel,
396                                                       sizeof(zeroPixel));
397                }
398                break;
399            default:
400                UNREACHABLE();
401                // NOTE(hqle): Support more texture types.
402        }
403        ASSERT(mNullTextures[type]);
404    }
405
406    return mNullTextures[type];
407}
408
409void DisplayMtl::ensureCapsInitialized() const
410{
411    if (mCapsInitialized)
412    {
413        return;
414    }
415
416    mCapsInitialized = true;
417
418    // Reset
419    mNativeCaps = gl::Caps();
420
421    // Fill extension and texture caps
422    initializeExtensions();
423    initializeTextureCaps();
424
425    // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
426    mNativeCaps.maxElementIndex  = std::numeric_limits<GLuint>::max() - 1;
427    mNativeCaps.max3DTextureSize = 2048;
428#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
429    mNativeCaps.max2DTextureSize          = 16384;
430    mNativeCaps.maxVaryingVectors         = 31;
431    mNativeCaps.maxVertexOutputComponents = 124;
432#else
433    if ([getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1])
434    {
435        mNativeCaps.max2DTextureSize          = 16384;
436        mNativeCaps.maxVertexOutputComponents = 124;
437        mNativeCaps.maxVaryingVectors         = mNativeCaps.maxVertexOutputComponents / 4;
438    }
439    else
440    {
441        mNativeCaps.max2DTextureSize          = 8192;
442        mNativeCaps.maxVertexOutputComponents = 60;
443        mNativeCaps.maxVaryingVectors         = mNativeCaps.maxVertexOutputComponents / 4;
444    }
445#endif
446
447    mNativeCaps.maxArrayTextureLayers = 2048;
448    mNativeCaps.maxLODBias            = 0;
449    mNativeCaps.maxCubeMapTextureSize = mNativeCaps.max2DTextureSize;
450    mNativeCaps.maxRenderbufferSize   = mNativeCaps.max2DTextureSize;
451    mNativeCaps.minAliasedPointSize   = 1;
452    mNativeCaps.maxAliasedPointSize   = 511;
453
454    mNativeCaps.minAliasedLineWidth = 1.0f;
455    mNativeCaps.maxAliasedLineWidth = 1.0f;
456
457    mNativeCaps.maxDrawBuffers       = mtl::kMaxRenderTargets;
458    mNativeCaps.maxFramebufferWidth  = mNativeCaps.max2DTextureSize;
459    mNativeCaps.maxFramebufferHeight = mNativeCaps.max2DTextureSize;
460    mNativeCaps.maxColorAttachments  = mtl::kMaxRenderTargets;
461    mNativeCaps.maxViewportWidth     = mNativeCaps.max2DTextureSize;
462    mNativeCaps.maxViewportHeight    = mNativeCaps.max2DTextureSize;
463
464    // NOTE(hqle): MSAA
465    mNativeCaps.maxSampleMaskWords     = 0;
466    mNativeCaps.maxColorTextureSamples = 1;
467    mNativeCaps.maxDepthTextureSamples = 1;
468    mNativeCaps.maxIntegerSamples      = 1;
469
470    mNativeCaps.maxVertexAttributes           = mtl::kMaxVertexAttribs;
471    mNativeCaps.maxVertexAttribBindings       = mtl::kMaxVertexAttribs;
472    mNativeCaps.maxVertexAttribRelativeOffset = std::numeric_limits<GLint>::max();
473    mNativeCaps.maxVertexAttribStride         = std::numeric_limits<GLint>::max();
474
475    mNativeCaps.maxElementsIndices  = std::numeric_limits<GLuint>::max();
476    mNativeCaps.maxElementsVertices = std::numeric_limits<GLuint>::max();
477
478    // Looks like all floats are IEEE according to the docs here:
479    mNativeCaps.vertexHighpFloat.setIEEEFloat();
480    mNativeCaps.vertexMediumpFloat.setIEEEFloat();
481    mNativeCaps.vertexLowpFloat.setIEEEFloat();
482    mNativeCaps.fragmentHighpFloat.setIEEEFloat();
483    mNativeCaps.fragmentMediumpFloat.setIEEEFloat();
484    mNativeCaps.fragmentLowpFloat.setIEEEFloat();
485
486    mNativeCaps.vertexHighpInt.setTwosComplementInt(32);
487    mNativeCaps.vertexMediumpInt.setTwosComplementInt(32);
488    mNativeCaps.vertexLowpInt.setTwosComplementInt(32);
489    mNativeCaps.fragmentHighpInt.setTwosComplementInt(32);
490    mNativeCaps.fragmentMediumpInt.setTwosComplementInt(32);
491    mNativeCaps.fragmentLowpInt.setTwosComplementInt(32);
492
493    GLuint maxUniformVectors = mtl::kDefaultUniformsMaxSize / (sizeof(GLfloat) * 4);
494
495    const GLuint maxUniformComponents = maxUniformVectors * 4;
496
497    // Uniforms are implemented using a uniform buffer, so the max number of uniforms we can
498    // support is the max buffer range divided by the size of a single uniform (4X float).
499    mNativeCaps.maxVertexUniformVectors                              = maxUniformVectors;
500    mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex]   = maxUniformComponents;
501    mNativeCaps.maxFragmentUniformVectors                            = maxUniformVectors;
502    mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxUniformComponents;
503
504    // NOTE(hqle): support UBO (ES 3.0 feature)
505    mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex]   = 0;
506    mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = 0;
507    mNativeCaps.maxCombinedUniformBlocks                         = 0;
508
509    // Note that we currently implement textures as combined image+samplers, so the limit is
510    // the minimum of supported samplers and sampled images.
511    mNativeCaps.maxCombinedTextureImageUnits                         = mtl::kMaxShaderSamplers;
512    mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Fragment] = mtl::kMaxShaderSamplers;
513    mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Vertex]   = mtl::kMaxShaderSamplers;
514
515    // NOTE(hqle): support storage buffer.
516    const uint32_t maxPerStageStorageBuffers                     = 0;
517    mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex]   = maxPerStageStorageBuffers;
518    mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Fragment] = maxPerStageStorageBuffers;
519    mNativeCaps.maxCombinedShaderStorageBlocks                   = maxPerStageStorageBuffers;
520
521    // Fill in additional limits for UBOs and SSBOs.
522    mNativeCaps.maxUniformBufferBindings     = 0;
523    mNativeCaps.maxUniformBlockSize          = 0;
524    mNativeCaps.uniformBufferOffsetAlignment = 0;
525
526    mNativeCaps.maxShaderStorageBufferBindings     = 0;
527    mNativeCaps.maxShaderStorageBlockSize          = 0;
528    mNativeCaps.shaderStorageBufferOffsetAlignment = 0;
529
530    // NOTE(hqle): support UBO
531    for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
532    {
533        mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxUniformComponents;
534    }
535
536    mNativeCaps.maxCombinedShaderOutputResources = 0;
537
538    mNativeCaps.maxTransformFeedbackInterleavedComponents =
539        gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS;
540    mNativeCaps.maxTransformFeedbackSeparateAttributes =
541        gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS;
542    mNativeCaps.maxTransformFeedbackSeparateComponents =
543        gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS;
544
545    // NOTE(hqle): support MSAA.
546    mNativeCaps.maxSamples = 1;
547}
548
549void DisplayMtl::initializeExtensions() const
550{
551    // Reset
552    mNativeExtensions = gl::Extensions();
553
554    // Enable this for simple buffer readback testing, but some functionality is missing.
555    // NOTE(hqle): Support full mapBufferRange extension.
556    mNativeExtensions.mapBufferOES           = true;
557    mNativeExtensions.mapBufferRange         = false;
558    mNativeExtensions.textureStorage         = true;
559    mNativeExtensions.drawBuffers            = false;
560    mNativeExtensions.fragDepth              = true;
561    mNativeExtensions.framebufferBlit        = false;
562    mNativeExtensions.framebufferMultisample = false;
563    mNativeExtensions.copyTexture            = false;
564    mNativeExtensions.copyCompressedTexture  = false;
565    mNativeExtensions.debugMarker            = false;
566    mNativeExtensions.robustness             = true;
567    mNativeExtensions.textureBorderClampOES  = false;  // not implemented yet
568    mNativeExtensions.translatedShaderSource = true;
569    mNativeExtensions.discardFramebuffer     = true;
570
571    // Enable EXT_blend_minmax
572    mNativeExtensions.blendMinMax = true;
573
574    mNativeExtensions.eglImageOES         = false;
575    mNativeExtensions.eglImageExternalOES = false;
576    // NOTE(hqle): Support GL_OES_EGL_image_external_essl3.
577    mNativeExtensions.eglImageExternalEssl3OES = false;
578
579    mNativeExtensions.memoryObject   = false;
580    mNativeExtensions.memoryObjectFd = false;
581
582    mNativeExtensions.semaphore   = false;
583    mNativeExtensions.semaphoreFd = false;
584
585    mNativeExtensions.instancedArraysANGLE = mFeatures.hasBaseVertexInstancedDraw.enabled;
586    mNativeExtensions.instancedArraysEXT   = mNativeExtensions.instancedArraysANGLE;
587
588    mNativeExtensions.robustBufferAccessBehavior = false;
589
590    mNativeExtensions.eglSyncOES = false;
591
592    // NOTE(hqle): support occlusion query
593    mNativeExtensions.occlusionQueryBoolean = false;
594
595    mNativeExtensions.disjointTimerQuery          = false;
596    mNativeExtensions.queryCounterBitsTimeElapsed = false;
597    mNativeExtensions.queryCounterBitsTimestamp   = false;
598
599    mNativeExtensions.textureFilterAnisotropic = true;
600    mNativeExtensions.maxTextureAnisotropy     = 16;
601
602    // NOTE(hqle): Support true NPOT textures.
603    mNativeExtensions.textureNPOTOES = false;
604
605    mNativeExtensions.texture3DOES = false;
606
607    mNativeExtensions.standardDerivativesOES = true;
608
609    mNativeExtensions.elementIndexUintOES = true;
610}
611
612void DisplayMtl::initializeTextureCaps() const
613{
614    mNativeTextureCaps.clear();
615
616    mFormatTable.generateTextureCaps(this, &mNativeTextureCaps,
617                                     &mNativeCaps.compressedTextureFormats);
618
619    // Re-verify texture extensions.
620    mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps);
621
622    // Disable all depth buffer and stencil buffer readback extensions until we need them
623    mNativeExtensions.readDepthNV         = false;
624    mNativeExtensions.readStencilNV       = false;
625    mNativeExtensions.depthBufferFloat2NV = false;
626}
627
628void DisplayMtl::initializeFeatures()
629{
630    // default values:
631    mFeatures.hasBaseVertexInstancedDraw.enabled        = true;
632    mFeatures.hasDepthTextureFiltering.enabled          = false;
633    mFeatures.hasNonUniformDispatch.enabled             = true;
634    mFeatures.hasTextureSwizzle.enabled                 = false;
635    mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
636
637#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
638    mFeatures.hasDepthTextureFiltering.enabled = true;
639
640    // Texture swizzle is only supported if macos sdk 10.15 is present
641#    if defined(__MAC_10_15)
642    if (ANGLE_APPLE_AVAILABLE_XC(10.15, 13.0))
643    {
644        // The runtime OS must be MacOS 10.15+ or Mac Catalyst for this to be supported:
645        ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle,
646                                [getMetalDevice() supportsFamily:MTLGPUFamilyMac2]);
647    }
648#    endif
649#elif TARGET_OS_IOS
650    // Base Vertex drawing is only supported since GPU family 3.
651    ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw,
652                            [getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]);
653
654    ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch,
655                            [getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]);
656
657#    if !TARGET_OS_SIMULATOR
658    mFeatures.allowSeparatedDepthStencilBuffers.enabled = true;
659#    endif
660#endif
661
662    angle::PlatformMethods *platform = ANGLEPlatformCurrent();
663    platform->overrideFeaturesMtl(platform, &mFeatures);
664}
665
666}  // namespace rx
667