xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/mtl_utils.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// mtl_utils.mm:
7//    Implements utilities functions that create Metal shaders, convert from angle enums
8//    to Metal enums and so on.
9//
10
11#include "libANGLE/renderer/metal/mtl_utils.h"
12
13#include <Availability.h>
14#include <TargetConditionals.h>
15#include <stdio.h>
16
17#include "common/MemoryBuffer.h"
18#include "common/string_utils.h"
19#include "common/system_utils.h"
20#include "gpu_info_util/SystemInfo_internal.h"
21#include "libANGLE/histogram_macros.h"
22#include "libANGLE/renderer/metal/ContextMtl.h"
23#include "libANGLE/renderer/metal/DisplayMtl.h"
24#include "libANGLE/renderer/metal/RenderTargetMtl.h"
25#include "libANGLE/renderer/metal/mtl_render_utils.h"
26#include "libANGLE/renderer/metal/process.h"
27#include "platform/PlatformMethods.h"
28
29// Compiler can turn on programmatical frame capture in release build by defining
30// ANGLE_METAL_FRAME_CAPTURE flag.
31#if defined(NDEBUG) && !defined(ANGLE_METAL_FRAME_CAPTURE)
32#    define ANGLE_METAL_FRAME_CAPTURE_ENABLED 0
33#else
34#    define ANGLE_METAL_FRAME_CAPTURE_ENABLED 1
35#endif
36
37namespace rx
38{
39
40ANGLE_APPLE_UNUSED
41bool IsFrameCaptureEnabled()
42{
43#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED
44    return false;
45#else
46    // We only support frame capture programmatically if the ANGLE_METAL_FRAME_CAPTURE
47    // environment flag is set. Otherwise, it will slow down the rendering. This allows user to
48    // finely control whether they want to capture the frame for particular application or not.
49    auto var                  = std::getenv("ANGLE_METAL_FRAME_CAPTURE");
50    static const bool enabled = var ? (strcmp(var, "1") == 0) : false;
51
52    return enabled;
53#endif
54}
55
56ANGLE_APPLE_UNUSED
57std::string GetMetalCaptureFile()
58{
59#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED
60    return {};
61#else
62    auto var                   = std::getenv("ANGLE_METAL_FRAME_CAPTURE_FILE");
63    const std::string filePath = var ? var : "";
64
65    return filePath;
66#endif
67}
68
69ANGLE_APPLE_UNUSED
70size_t MaxAllowedFrameCapture()
71{
72#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED
73    return 0;
74#else
75    auto var                      = std::getenv("ANGLE_METAL_FRAME_CAPTURE_MAX");
76    static const size_t maxFrames = var ? std::atoi(var) : 100;
77
78    return maxFrames;
79#endif
80}
81
82ANGLE_APPLE_UNUSED
83size_t MinAllowedFrameCapture()
84{
85#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED
86    return 0;
87#else
88    auto var                     = std::getenv("ANGLE_METAL_FRAME_CAPTURE_MIN");
89    static const size_t minFrame = var ? std::atoi(var) : 0;
90
91    return minFrame;
92#endif
93}
94
95ANGLE_APPLE_UNUSED
96bool FrameCaptureDeviceScope()
97{
98#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED
99    return false;
100#else
101    auto var                      = std::getenv("ANGLE_METAL_FRAME_CAPTURE_SCOPE");
102    static const bool scopeDevice = var ? (strcmp(var, "device") == 0) : false;
103
104    return scopeDevice;
105#endif
106}
107
108// Ensure that .gputrace files have RW permissions for the user or, if a
109// directory, RWX permissions for the user.
110ANGLE_APPLE_UNUSED
111static inline void FixGPUTracePathPermissions(NSString *path, bool isDir)
112{
113    // Ensure we're only change permissions on files in a gputrace bundle.
114    if (![path containsString:@".gputrace"])
115    {
116        return;
117    }
118
119    NSError *error = nil;
120    NSDictionary<NSFileAttributeKey, id> *attributes =
121        [NSFileManager.defaultManager attributesOfItemAtPath:path error:&error];
122    NSNumber *oldPerms = static_cast<NSNumber *>(attributes[NSFilePosixPermissions]);
123    if (!oldPerms)
124    {
125        NSString *msg =
126            attributes ? @"NSFilePosixPermissions unavailable" : error.localizedDescription;
127        NSLog(@"Unable to read permissions for %@ (%@)", path, msg);
128        return;
129    }
130
131    NSUInteger newPerms = oldPerms.unsignedIntegerValue | S_IRUSR | S_IWUSR;
132    if (isDir)
133    {
134        newPerms |= S_IXUSR;
135    }
136
137    if (![NSFileManager.defaultManager setAttributes:@{
138            NSFilePosixPermissions : @(newPerms)
139        }
140                                        ofItemAtPath:path
141                                               error:&error])
142    {
143        NSLog(@"Unable to set permissions=%3lo for %@ (%@)", static_cast<unsigned long>(newPerms),
144              path, error.localizedDescription);
145    }
146}
147
148ANGLE_APPLE_UNUSED
149static inline void FixGPUTraceBundlePermissions(NSString *bundlePath)
150{
151    FixGPUTracePathPermissions(bundlePath, true);
152    for (NSString *file in [NSFileManager.defaultManager enumeratorAtPath:bundlePath])
153    {
154        FixGPUTracePathPermissions([NSString pathWithComponents:@[ bundlePath, file ]], false);
155    }
156}
157
158ANGLE_APPLE_UNUSED
159std::atomic<size_t> gFrameCaptured(0);
160
161ANGLE_APPLE_UNUSED
162NSString *gFrameCapturePath;
163
164ANGLE_APPLE_UNUSED
165void StartFrameCapture(id<MTLDevice> metalDevice, id<MTLCommandQueue> metalCmdQueue)
166{
167#if ANGLE_METAL_FRAME_CAPTURE_ENABLED
168    if (!IsFrameCaptureEnabled())
169    {
170        return;
171    }
172
173    if (gFrameCaptured >= MaxAllowedFrameCapture())
174    {
175        return;
176    }
177
178    MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager];
179    if (captureManager.isCapturing)
180    {
181        return;
182    }
183
184    gFrameCaptured++;
185
186    if (gFrameCaptured < MinAllowedFrameCapture())
187    {
188        return;
189    }
190
191    auto captureDescriptor                = mtl::adoptObjCObj([[MTLCaptureDescriptor alloc] init]);
192    captureDescriptor.get().captureObject = metalDevice;
193    const std::string filePath            = GetMetalCaptureFile();
194    NSString *frameCapturePath            = nil;
195    if (filePath != "")
196    {
197        frameCapturePath =
198            [NSString stringWithFormat:@"%s%zu.gputrace", filePath.c_str(), gFrameCaptured - 1];
199        captureDescriptor.get().destination = MTLCaptureDestinationGPUTraceDocument;
200        captureDescriptor.get().outputURL   = [NSURL fileURLWithPath:frameCapturePath
201                                                       isDirectory:false];
202    }
203    else
204    {
205        // This will pause execution only if application is being debugged inside Xcode
206        captureDescriptor.get().destination = MTLCaptureDestinationDeveloperTools;
207    }
208
209    NSError *error;
210    if ([captureManager startCaptureWithDescriptor:captureDescriptor.get() error:&error])
211    {
212        ASSERT(!gFrameCapturePath);
213        gFrameCapturePath = frameCapturePath;
214    }
215    else
216    {
217        NSLog(@"Failed to start capture, error %@", error);
218    }
219#endif  // ANGLE_METAL_FRAME_CAPTURE_ENABLED
220}
221
222void StartFrameCapture(ContextMtl *context)
223{
224    StartFrameCapture(context->getMetalDevice(), context->cmdQueue().get());
225}
226
227void StopFrameCapture()
228{
229#if ANGLE_METAL_FRAME_CAPTURE_ENABLED
230    if (!IsFrameCaptureEnabled())
231    {
232        return;
233    }
234    MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager];
235    if (captureManager.isCapturing)
236    {
237        [captureManager stopCapture];
238        if (gFrameCapturePath)
239        {
240            FixGPUTraceBundlePermissions(gFrameCapturePath);
241            [gFrameCapturePath ANGLE_MTL_RELEASE];
242            gFrameCapturePath = nil;
243        }
244    }
245#endif
246}
247
248namespace mtl
249{
250
251constexpr char kANGLEPrintMSLEnv[]        = "ANGLE_METAL_PRINT_MSL_ENABLE";
252constexpr char kANGLEMSLVersionMajorEnv[] = "ANGLE_MSL_VERSION_MAJOR";
253constexpr char kANGLEMSLVersionMinorEnv[] = "ANGLE_MSL_VERSION_MINOR";
254
255namespace
256{
257
258uint32_t GetDeviceVendorIdFromName(id<MTLDevice> metalDevice)
259{
260    struct Vendor
261    {
262        NSString *const trademark;
263        uint32_t vendorId;
264    };
265
266    constexpr Vendor kVendors[] = {
267        {@"AMD", angle::kVendorID_AMD},        {@"Apple", angle::kVendorID_Apple},
268        {@"Radeon", angle::kVendorID_AMD},     {@"Intel", angle::kVendorID_Intel},
269        {@"Geforce", angle::kVendorID_NVIDIA}, {@"Quadro", angle::kVendorID_NVIDIA}};
270    ANGLE_MTL_OBJC_SCOPE
271    {
272        if (metalDevice)
273        {
274            for (const Vendor &it : kVendors)
275            {
276                if ([metalDevice.name rangeOfString:it.trademark].location != NSNotFound)
277                {
278                    return it.vendorId;
279                }
280            }
281        }
282
283        return 0;
284    }
285}
286
287#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
288uint32_t GetDeviceVendorIdFromIOKit(id<MTLDevice> device)
289{
290    return angle::GetVendorIDFromMetalDeviceRegistryID(device.registryID);
291}
292#endif
293
294void GetSliceAndDepth(const ImageNativeIndex &index, GLint *layer, GLint *startDepth)
295{
296    *layer = *startDepth = 0;
297    if (!index.hasLayer())
298    {
299        return;
300    }
301
302    switch (index.getType())
303    {
304        case gl::TextureType::CubeMap:
305            *layer = index.cubeMapFaceIndex();
306            break;
307        case gl::TextureType::_2DArray:
308            *layer = index.getLayerIndex();
309            break;
310        case gl::TextureType::_3D:
311            *startDepth = index.getLayerIndex();
312            break;
313        default:
314            break;
315    }
316}
317GLint GetSliceOrDepth(const ImageNativeIndex &index)
318{
319    GLint layer, startDepth;
320    GetSliceAndDepth(index, &layer, &startDepth);
321
322    return std::max(layer, startDepth);
323}
324
325bool GetCompressedBufferSizeAndRowLengthForTextureWithFormat(const TextureRef &texture,
326                                                             const Format &textureObjFormat,
327                                                             const ImageNativeIndex &index,
328                                                             size_t *bytesPerRowOut,
329                                                             size_t *bytesPerImageOut)
330{
331    gl::Extents size = texture->size(index);
332    ASSERT(size.depth == 1);
333    GLuint bufferRowInBytes;
334    if (!textureObjFormat.intendedInternalFormat().computeCompressedImageRowPitch(
335            size.width, &bufferRowInBytes))
336    {
337        return false;
338    }
339    GLuint bufferSizeInBytes;
340    if (!textureObjFormat.intendedInternalFormat().computeCompressedImageDepthPitch(
341            size.height, bufferRowInBytes, &bufferSizeInBytes))
342    {
343        return false;
344    }
345    *bytesPerRowOut   = bufferRowInBytes;
346    *bytesPerImageOut = bufferSizeInBytes;
347    return true;
348}
349static angle::Result InitializeCompressedTextureContents(const gl::Context *context,
350                                                         const TextureRef &texture,
351                                                         const Format &textureObjFormat,
352                                                         const ImageNativeIndex &index,
353                                                         const uint layer,
354                                                         const uint startDepth)
355{
356    assert(textureObjFormat.actualAngleFormat().isBlock);
357    size_t bytesPerRow   = 0;
358    size_t bytesPerImage = 0;
359    if (!GetCompressedBufferSizeAndRowLengthForTextureWithFormat(texture, textureObjFormat, index,
360                                                                 &bytesPerRow, &bytesPerImage))
361    {
362        return angle::Result::Stop;
363    }
364    ContextMtl *contextMtl = mtl::GetImpl(context);
365    gl::Extents extents    = texture->size(index);
366    if (texture->isCPUAccessible())
367    {
368        if (textureObjFormat.isPVRTC())
369        {
370            // Replace Region Validation: rowBytes must be 0
371            bytesPerRow = 0;
372        }
373
374        angle::MemoryBuffer buffer;
375        if (!buffer.resize(bytesPerImage))
376        {
377            return angle::Result::Stop;
378        }
379        buffer.fill(0);
380        for (NSUInteger d = 0; d < static_cast<NSUInteger>(extents.depth); ++d)
381        {
382            auto mtlTextureRegion     = MTLRegionMake2D(0, 0, extents.width, extents.height);
383            mtlTextureRegion.origin.z = d + startDepth;
384            texture->replaceRegion(contextMtl, mtlTextureRegion, index.getNativeLevel(), layer,
385                                   buffer.data(), bytesPerRow, 0);
386        }
387    }
388    else
389    {
390        mtl::BufferRef zeroBuffer;
391        ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, bytesPerImage, nullptr, &zeroBuffer));
392        mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
393        for (NSUInteger d = 0; d < static_cast<NSUInteger>(extents.depth); ++d)
394        {
395            auto blitOrigin = MTLOriginMake(0, 0, d + startDepth);
396            blitEncoder->copyBufferToTexture(zeroBuffer, 0, bytesPerRow, 0,
397                                             MTLSizeMake(extents.width, extents.height, 1), texture,
398                                             layer, index.getNativeLevel(), blitOrigin, 0);
399        }
400        blitEncoder->endEncoding();
401    }
402    return angle::Result::Continue;
403}
404
405}  // namespace
406
407bool PreferStagedTextureUploads(const gl::Context *context,
408                                const TextureRef &texture,
409                                const Format &textureObjFormat,
410                                StagingPurpose purpose)
411{
412    // The simulator MUST upload all textures as staged.
413    if (TARGET_OS_SIMULATOR)
414    {
415        return true;
416    }
417
418    ContextMtl *contextMtl             = mtl::GetImpl(context);
419    const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures();
420
421    const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat();
422    if (intendedInternalFormat.compressed || textureObjFormat.actualAngleFormat().isBlock)
423    {
424        return false;
425    }
426
427    // If the intended internal format is luminance, we can still
428    // initialize the texture using the GPU. However, if we're
429    // uploading data to it, we avoid using a staging buffer, due to
430    // the (current) need to re-pack the data from L8 -> RGBA8 and LA8
431    // -> RGBA8. This could be better optimized by emulating L8
432    // textures with R8 and LA8 with RG8, and using swizzlig for the
433    // resulting textures.
434    if (intendedInternalFormat.isLUMA())
435    {
436        return (purpose == StagingPurpose::Initialization);
437    }
438
439    if (features.disableStagedInitializationOfPackedTextureFormats.enabled)
440    {
441        switch (intendedInternalFormat.sizedInternalFormat)
442        {
443            case GL_RGB9_E5:
444            case GL_R11F_G11F_B10F:
445                return false;
446
447            default:
448                break;
449        }
450    }
451
452    return (texture->hasIOSurface() && features.uploadDataToIosurfacesWithStagingBuffers.enabled) ||
453           features.alwaysPreferStagedTextureUploads.enabled;
454}
455
456angle::Result InitializeTextureContents(const gl::Context *context,
457                                        const TextureRef &texture,
458                                        const Format &textureObjFormat,
459                                        const ImageNativeIndex &index)
460{
461    ASSERT(texture && texture->valid());
462    // Only one slice can be initialized at a time.
463    ASSERT(!index.isLayered() || index.getType() == gl::TextureType::_3D);
464    ContextMtl *contextMtl = mtl::GetImpl(context);
465
466    const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat();
467
468    bool preferGPUInitialization = PreferStagedTextureUploads(context, texture, textureObjFormat,
469                                                              StagingPurpose::Initialization);
470
471    // This function is called in many places to initialize the content of a texture.
472    // So it's better we do the initial check here instead of let the callers do it themselves:
473    if (!textureObjFormat.valid())
474    {
475        return angle::Result::Continue;
476    }
477
478    if ((textureObjFormat.hasDepthOrStencilBits() && !textureObjFormat.getCaps().depthRenderable) ||
479        !textureObjFormat.getCaps().colorRenderable)
480    {
481        // Texture is not appropriately color- or depth-renderable, so do not attempt
482        // to use GPU initialization (clears for initialization).
483        preferGPUInitialization = false;
484    }
485
486    gl::Extents size = texture->size(index);
487
488    // Intiialize the content to black
489    GLint layer, startDepth;
490    GetSliceAndDepth(index, &layer, &startDepth);
491
492    // Use compressed texture initialization only when both the intended and the actual ANGLE
493    // formats are compressed. Emulated opaque ETC2 formats use uncompressed fallbacks and require
494    // custom initialization.
495    if (intendedInternalFormat.compressed && textureObjFormat.actualAngleFormat().isBlock)
496    {
497        return InitializeCompressedTextureContents(context, texture, textureObjFormat, index, layer,
498                                                   startDepth);
499    }
500    else if (texture->isCPUAccessible() && index.getType() != gl::TextureType::_2DMultisample &&
501             index.getType() != gl::TextureType::_2DMultisampleArray && !preferGPUInitialization)
502    {
503        const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId);
504        const size_t dstRowPitch       = dstFormat.pixelBytes * size.width;
505        angle::MemoryBuffer conversionRow;
506        ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
507
508        if (textureObjFormat.initFunction)
509        {
510            textureObjFormat.initFunction(size.width, 1, 1, conversionRow.data(), dstRowPitch, 0);
511        }
512        else
513        {
514            const angle::Format &srcFormat = angle::Format::Get(
515                intendedInternalFormat.alphaBits > 0 ? angle::FormatID::R8G8B8A8_UNORM
516                                                     : angle::FormatID::R8G8B8_UNORM);
517            const size_t srcRowPitch = srcFormat.pixelBytes * size.width;
518            angle::MemoryBuffer srcRow;
519            ANGLE_CHECK_GL_ALLOC(contextMtl, srcRow.resize(srcRowPitch));
520            memset(srcRow.data(), 0, srcRowPitch);
521
522            CopyImageCHROMIUM(srcRow.data(), srcRowPitch, srcFormat.pixelBytes, 0,
523                              srcFormat.pixelReadFunction, conversionRow.data(), dstRowPitch,
524                              dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction,
525                              intendedInternalFormat.format, dstFormat.componentType, size.width, 1,
526                              1, false, false, false);
527        }
528
529        auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1);
530
531        for (NSUInteger d = 0; d < static_cast<NSUInteger>(size.depth); ++d)
532        {
533            mtlRowRegion.origin.z = d + startDepth;
534            for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r)
535            {
536                mtlRowRegion.origin.y = r;
537
538                // Upload to texture
539                texture->replace2DRegion(contextMtl, mtlRowRegion, index.getNativeLevel(), layer,
540                                         conversionRow.data(), dstRowPitch);
541            }
542        }
543    }
544    else
545    {
546        ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat, index,
547                                               MTLColorWriteMaskAll));
548    }
549
550    return angle::Result::Continue;
551}
552
553angle::Result InitializeTextureContentsGPU(const gl::Context *context,
554                                           const TextureRef &texture,
555                                           const Format &textureObjFormat,
556                                           const ImageNativeIndex &index,
557                                           MTLColorWriteMask channelsToInit)
558{
559    // Only one slice can be initialized at a time.
560    ASSERT(!index.isLayered() || index.getType() == gl::TextureType::_3D);
561    if (index.isLayered() && index.getType() == gl::TextureType::_3D)
562    {
563        ImageNativeIndexIterator ite =
564            index.getLayerIterator(texture->depth(index.getNativeLevel()));
565        while (ite.hasNext())
566        {
567            ImageNativeIndex depthLayerIndex = ite.next();
568            ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat,
569                                                   depthLayerIndex, MTLColorWriteMaskAll));
570        }
571
572        return angle::Result::Continue;
573    }
574
575    if (textureObjFormat.hasDepthOrStencilBits())
576    {
577        // Depth stencil texture needs dedicated function.
578        return InitializeDepthStencilTextureContentsGPU(context, texture, textureObjFormat, index);
579    }
580
581    ContextMtl *contextMtl = mtl::GetImpl(context);
582    GLint sliceOrDepth     = GetSliceOrDepth(index);
583
584    // Use clear render command
585    RenderTargetMtl tempRtt;
586    tempRtt.set(texture, index.getNativeLevel(), sliceOrDepth, textureObjFormat);
587
588    int clearAlpha = 0;
589    if (!textureObjFormat.intendedAngleFormat().alphaBits)
590    {
591        // if intended format doesn't have alpha, set it to 1.0.
592        clearAlpha = kEmulatedAlphaValue;
593    }
594
595    RenderCommandEncoder *encoder;
596    if (channelsToInit == MTLColorWriteMaskAll)
597    {
598        // If all channels will be initialized, use clear loadOp.
599        Optional<MTLClearColor> blackColor = MTLClearColorMake(0, 0, 0, clearAlpha);
600        encoder = contextMtl->getRenderTargetCommandEncoderWithClear(tempRtt, blackColor);
601    }
602    else
603    {
604        // temporarily enable color channels requested via channelsToInit. Some emulated format has
605        // some channels write mask disabled when the texture is created.
606        MTLColorWriteMask oldMask = texture->getColorWritableMask();
607        texture->setColorWritableMask(channelsToInit);
608
609        // If there are some channels don't need to be initialized, we must use clearWithDraw.
610        encoder = contextMtl->getRenderTargetCommandEncoder(tempRtt);
611
612        const angle::Format &angleFormat = textureObjFormat.actualAngleFormat();
613
614        ClearRectParams clearParams;
615        ClearColorValue clearColor;
616        if (angleFormat.isSint())
617        {
618            clearColor.setAsInt(0, 0, 0, clearAlpha);
619        }
620        else if (angleFormat.isUint())
621        {
622            clearColor.setAsUInt(0, 0, 0, clearAlpha);
623        }
624        else
625        {
626            clearColor.setAsFloat(0, 0, 0, clearAlpha);
627        }
628        clearParams.clearColor     = clearColor;
629        clearParams.dstTextureSize = texture->sizeAt0();
630        clearParams.enabledBuffers.set(0);
631        clearParams.clearArea = gl::Rectangle(0, 0, texture->widthAt0(), texture->heightAt0());
632
633        ANGLE_TRY(
634            contextMtl->getDisplay()->getUtils().clearWithDraw(context, encoder, clearParams));
635
636        // Restore texture's intended write mask
637        texture->setColorWritableMask(oldMask);
638    }
639    encoder->setStoreAction(MTLStoreActionStore);
640
641    return angle::Result::Continue;
642}
643
644angle::Result InitializeDepthStencilTextureContentsGPU(const gl::Context *context,
645                                                       const TextureRef &texture,
646                                                       const Format &textureObjFormat,
647                                                       const ImageNativeIndex &index)
648{
649    const MipmapNativeLevel &level = index.getNativeLevel();
650    // Use clear operation
651    ContextMtl *contextMtl           = mtl::GetImpl(context);
652    const angle::Format &angleFormat = textureObjFormat.actualAngleFormat();
653    RenderTargetMtl rtMTL;
654
655    uint32_t layer = index.hasLayer() ? index.getLayerIndex() : 0;
656    rtMTL.set(texture, level, layer, textureObjFormat);
657    mtl::RenderPassDesc rpDesc;
658    if (angleFormat.depthBits)
659    {
660        rtMTL.toRenderPassAttachmentDesc(&rpDesc.depthAttachment);
661        rpDesc.depthAttachment.loadAction = MTLLoadActionClear;
662    }
663    if (angleFormat.stencilBits)
664    {
665        rtMTL.toRenderPassAttachmentDesc(&rpDesc.stencilAttachment);
666        rpDesc.stencilAttachment.loadAction = MTLLoadActionClear;
667    }
668    rpDesc.rasterSampleCount = texture->samples();
669
670    // End current render pass
671    contextMtl->endEncoding(true);
672
673    RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(rpDesc);
674    encoder->setStoreAction(MTLStoreActionStore);
675
676    return angle::Result::Continue;
677}
678
679angle::Result ReadTexturePerSliceBytes(const gl::Context *context,
680                                       const TextureRef &texture,
681                                       size_t bytesPerRow,
682                                       const gl::Rectangle &fromRegion,
683                                       const MipmapNativeLevel &mipLevel,
684                                       uint32_t sliceOrDepth,
685                                       uint8_t *dataOut)
686{
687    ASSERT(texture && texture->valid());
688    ContextMtl *contextMtl = mtl::GetImpl(context);
689    GLint layer            = 0;
690    GLint startDepth       = 0;
691    switch (texture->textureType())
692    {
693        case MTLTextureTypeCube:
694        case MTLTextureType2DArray:
695            layer = sliceOrDepth;
696            break;
697        case MTLTextureType3D:
698            startDepth = sliceOrDepth;
699            break;
700        default:
701            break;
702    }
703
704    MTLRegion mtlRegion = MTLRegionMake3D(fromRegion.x, fromRegion.y, startDepth, fromRegion.width,
705                                          fromRegion.height, 1);
706
707    texture->getBytes(contextMtl, bytesPerRow, 0, mtlRegion, mipLevel, layer, dataOut);
708
709    return angle::Result::Continue;
710}
711
712angle::Result ReadTexturePerSliceBytesToBuffer(const gl::Context *context,
713                                               const TextureRef &texture,
714                                               size_t bytesPerRow,
715                                               const gl::Rectangle &fromRegion,
716                                               const MipmapNativeLevel &mipLevel,
717                                               uint32_t sliceOrDepth,
718                                               uint32_t dstOffset,
719                                               const BufferRef &dstBuffer)
720{
721    ASSERT(texture && texture->valid());
722    ContextMtl *contextMtl = mtl::GetImpl(context);
723    GLint layer            = 0;
724    GLint startDepth       = 0;
725    switch (texture->textureType())
726    {
727        case MTLTextureTypeCube:
728        case MTLTextureType2DArray:
729            layer = sliceOrDepth;
730            break;
731        case MTLTextureType3D:
732            startDepth = sliceOrDepth;
733            break;
734        default:
735            break;
736    }
737
738    MTLRegion mtlRegion = MTLRegionMake3D(fromRegion.x, fromRegion.y, startDepth, fromRegion.width,
739                                          fromRegion.height, 1);
740
741    BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
742    blitEncoder->copyTextureToBuffer(texture, layer, mipLevel, mtlRegion.origin, mtlRegion.size,
743                                     dstBuffer, dstOffset, bytesPerRow, 0, MTLBlitOptionNone);
744
745    return angle::Result::Continue;
746}
747
748MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar)
749{
750    MTLViewport re;
751
752    re.originX = rect.x;
753    re.originY = rect.y;
754    re.width   = rect.width;
755    re.height  = rect.height;
756    re.znear   = znear;
757    re.zfar    = zfar;
758
759    return re;
760}
761
762MTLViewport GetViewportFlipY(const gl::Rectangle &rect,
763                             NSUInteger screenHeight,
764                             double znear,
765                             double zfar)
766{
767    MTLViewport re;
768
769    re.originX = rect.x;
770    re.originY = static_cast<double>(screenHeight) - rect.y1();
771    re.width   = rect.width;
772    re.height  = rect.height;
773    re.znear   = znear;
774    re.zfar    = zfar;
775
776    return re;
777}
778
779MTLViewport GetViewport(const gl::Rectangle &rect,
780                        NSUInteger screenHeight,
781                        bool flipY,
782                        double znear,
783                        double zfar)
784{
785    if (flipY)
786    {
787        return GetViewportFlipY(rect, screenHeight, znear, zfar);
788    }
789
790    return GetViewport(rect, znear, zfar);
791}
792
793MTLScissorRect GetScissorRect(const gl::Rectangle &rect, NSUInteger screenHeight, bool flipY)
794{
795    MTLScissorRect re;
796
797    re.x      = rect.x;
798    re.y      = flipY ? (screenHeight - rect.y1()) : rect.y;
799    re.width  = rect.width;
800    re.height = rect.height;
801
802    return re;
803}
804
805uint32_t GetDeviceVendorId(id<MTLDevice> metalDevice)
806{
807    uint32_t vendorId = 0;
808#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
809    vendorId = GetDeviceVendorIdFromIOKit(metalDevice);
810#endif
811    if (!vendorId)
812    {
813        vendorId = GetDeviceVendorIdFromName(metalDevice);
814    }
815
816    return vendorId;
817}
818
819static MTLLanguageVersion GetUserSetOrHighestMSLVersion(const MTLLanguageVersion currentVersion)
820{
821    const std::string major_str = angle::GetEnvironmentVar(kANGLEMSLVersionMajorEnv);
822    const std::string minor_str = angle::GetEnvironmentVar(kANGLEMSLVersionMinorEnv);
823    if (major_str != "" && minor_str != "")
824    {
825        const int major = std::stoi(major_str);
826        const int minor = std::stoi(minor_str);
827#if !defined(NDEBUG)
828        NSLog(@"Forcing MSL Version: MTLLanguageVersion%d_%d\n", major, minor);
829#endif
830        switch (major)
831        {
832            case 1:
833                switch (minor)
834                {
835                    case 0:
836#if !defined(NDEBUG)
837                        NSLog(@"MSL 1.0 is deprecated, using MSL 1.1 instead\n");
838#endif
839                        return MTLLanguageVersion1_1;
840                    case 1:
841                        return MTLLanguageVersion1_1;
842                    case 2:
843                        return MTLLanguageVersion1_2;
844                    default:
845                        assert(0 && "Unsupported MSL Minor Language Version.");
846                }
847                break;
848            case 2:
849                switch (minor)
850                {
851                    case 0:
852                        return MTLLanguageVersion2_0;
853                    case 1:
854                        return MTLLanguageVersion2_1;
855                    case 2:
856                        return MTLLanguageVersion2_2;
857                    case 3:
858                        return MTLLanguageVersion2_3;
859                    case 4:
860                        if (@available(macOS 12.0, *))
861                        {
862                            return MTLLanguageVersion2_4;
863                        }
864                        assert(0 && "MSL 2.4 requires macOS 12.");
865                        break;
866                    default:
867                        assert(0 && "Unsupported MSL Minor Language Version.");
868                }
869                break;
870            default:
871                assert(0 && "Unsupported MSL Major Language Version.");
872        }
873    }
874    return currentVersion;
875}
876
877AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(
878    id<MTLDevice> metalDevice,
879    std::string_view source,
880    const std::map<std::string, std::string> &substitutionMacros,
881    bool disableFastMath,
882    bool usesInvariance,
883    AutoObjCPtr<NSError *> *errorOut)
884{
885    AutoObjCPtr<id<MTLLibrary>> result;
886    ANGLE_MTL_OBJC_SCOPE
887    {
888        NSError *nsError = nil;
889        AutoObjCPtr nsSource =
890            adoptObjCObj([[NSString alloc] initWithBytesNoCopy:const_cast<char *>(source.data())
891                                                        length:source.length()
892                                                      encoding:NSUTF8StringEncoding
893                                                  freeWhenDone:NO]);
894        AutoObjCPtr options = adoptObjCObj([[MTLCompileOptions alloc] init]);
895
896        // Mark all positions in VS with attribute invariant as non-optimizable
897        options.get().preserveInvariance = usesInvariance;
898
899        if (disableFastMath)
900        {
901            options.get().fastMathEnabled = false;
902        }
903
904        options.get().languageVersion =
905            GetUserSetOrHighestMSLVersion(options.get().languageVersion);
906
907        if (!substitutionMacros.empty())
908        {
909            auto macroDict = [NSMutableDictionary dictionary];
910            for (const auto &macro : substitutionMacros)
911            {
912                [macroDict setObject:@(macro.second.c_str()) forKey:@(macro.first.c_str())];
913            }
914            options.get().preprocessorMacros = macroDict;
915        }
916
917        auto *platform   = ANGLEPlatformCurrent();
918        double startTime = platform->currentTime(platform);
919
920        result = adoptObjCObj([metalDevice newLibraryWithSource:nsSource.get()
921                                                        options:options.get()
922                                                          error:&nsError]);
923        if (angle::GetBoolEnvironmentVar(kANGLEPrintMSLEnv))
924        {
925            NSLog(@"%@\n", nsSource.get());
926        }
927        *errorOut = std::move(nsError);
928
929        int us = static_cast<int>((platform->currentTime(platform) - startTime) * 1e6);
930        ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.MetalShaderCompilationTimeUs", us);
931    }
932    return result;
933}
934
935std::string CompileShaderLibraryToFile(const std::string &source,
936                                       const std::map<std::string, std::string> &macros,
937                                       bool disableFastMath,
938                                       bool usesInvariance)
939{
940    auto tmpDir = angle::GetTempDirectory();
941    if (!tmpDir.valid())
942    {
943        FATAL() << "angle::GetTempDirectory() failed";
944    }
945    // NOTE: metal/metallib seem to require extensions, otherwise they interpret the files
946    // differently.
947    auto metalFileName =
948        angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".metal");
949    auto airFileName = angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".air");
950    auto metallibFileName =
951        angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".metallib");
952    if (!metalFileName.valid() || !airFileName.valid() || !metallibFileName.valid())
953    {
954        FATAL() << "Unable to generate temporary files for compiling metal";
955    }
956    // Save the source.
957    {
958        FILE *fp = fopen(metalFileName.value().c_str(), "wb");
959        ASSERT(fp);
960        fwrite(source.c_str(), sizeof(char), metalFileName.value().length(), fp);
961        fclose(fp);
962    }
963
964    // metal -> air
965    std::vector<std::string> metalToAirArgv{"/usr/bin/xcrun",
966                                            "/usr/bin/xcrun",
967                                            "-sdk",
968                                            "macosx",
969                                            "metal",
970                                            "-std=macos-metal2.0",
971                                            "-mmacosx-version-min=10.13",
972                                            "-c",
973                                            metalFileName.value(),
974                                            "-o",
975                                            airFileName.value()};
976    // Macros are passed using `-D key=value`.
977    for (const auto &macro : macros)
978    {
979        metalToAirArgv.push_back("-D");
980        // TODO: not sure if this needs to escape strings or what (for example, might
981        // a space cause problems)?
982        metalToAirArgv.push_back(macro.first + "=" + macro.second);
983    }
984    // TODO: is this right, not sure if MTLCompileOptions.fastMathEnabled is same as -ffast-math.
985    if (!disableFastMath)
986    {
987        metalToAirArgv.push_back("-ffast-math");
988    }
989    if (usesInvariance)
990    {
991        metalToAirArgv.push_back("-fpreserve-invariance");
992    }
993    Process metalToAirProcess(metalToAirArgv);
994    int exitCode = -1;
995    if (!metalToAirProcess.DidLaunch() || !metalToAirProcess.WaitForExit(exitCode) || exitCode != 0)
996    {
997        FATAL() << "Generating air file failed";
998    }
999
1000    // air -> metallib
1001    const std::vector<std::string> airToMetallibArgv{
1002        "xcrun",    "/usr/bin/xcrun",    "-sdk", "macosx",
1003        "metallib", airFileName.value(), "-o",   metallibFileName.value()};
1004    Process air_to_metallib_process(airToMetallibArgv);
1005    if (!air_to_metallib_process.DidLaunch() || !air_to_metallib_process.WaitForExit(exitCode) ||
1006        exitCode != 0)
1007    {
1008        FATAL() << "Ggenerating metallib file failed";
1009    }
1010    return metallibFileName.value();
1011}
1012
1013AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromBinary(id<MTLDevice> metalDevice,
1014                                                          const uint8_t *data,
1015                                                          size_t length,
1016                                                          AutoObjCPtr<NSError *> *errorOut)
1017{
1018    AutoObjCPtr<id<MTLLibrary>> result;
1019    ANGLE_MTL_OBJC_SCOPE
1020    {
1021        NSError *nsError = nil;
1022        AutoObjCPtr binaryData = adoptObjCObj(
1023            dispatch_data_create(data, length, nullptr, DISPATCH_DATA_DESTRUCTOR_DEFAULT));
1024        result    = adoptObjCObj([metalDevice newLibraryWithData:binaryData.get() error:&nsError]);
1025        *errorOut = std::move(nsError);
1026    }
1027    return result;
1028}
1029
1030AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromStaticBinary(id<MTLDevice> metalDevice,
1031                                                                const uint8_t *data,
1032                                                                size_t length,
1033                                                                AutoObjCPtr<NSError *> *errorOut)
1034{
1035    AutoObjCPtr<id<MTLLibrary>> result;
1036    ANGLE_MTL_OBJC_SCOPE
1037    {
1038        NSError *nsError = nil;
1039        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
1040        AutoObjCPtr binaryData = adoptObjCObj(dispatch_data_create(data, length, queue,
1041                                                                   ^{
1042                                                                   }));
1043        result    = adoptObjCObj([metalDevice newLibraryWithData:binaryData.get() error:&nsError]);
1044        *errorOut = std::move(nsError);
1045    }
1046    return result;
1047}
1048MTLTextureType GetTextureType(gl::TextureType glType)
1049{
1050    switch (glType)
1051    {
1052        case gl::TextureType::_2D:
1053            return MTLTextureType2D;
1054        case gl::TextureType::_2DArray:
1055            return MTLTextureType2DArray;
1056        case gl::TextureType::_3D:
1057            return MTLTextureType3D;
1058        case gl::TextureType::CubeMap:
1059            return MTLTextureTypeCube;
1060        default:
1061            return MTLTextureTypeInvalid;
1062    }
1063}
1064
1065MTLSamplerMinMagFilter GetFilter(GLenum filter)
1066{
1067    switch (filter)
1068    {
1069        case GL_LINEAR_MIPMAP_LINEAR:
1070        case GL_LINEAR_MIPMAP_NEAREST:
1071        case GL_LINEAR:
1072            return MTLSamplerMinMagFilterLinear;
1073        case GL_NEAREST_MIPMAP_LINEAR:
1074        case GL_NEAREST_MIPMAP_NEAREST:
1075        case GL_NEAREST:
1076            return MTLSamplerMinMagFilterNearest;
1077        default:
1078            UNIMPLEMENTED();
1079            return MTLSamplerMinMagFilterNearest;
1080    }
1081}
1082
1083MTLSamplerMipFilter GetMipmapFilter(GLenum filter)
1084{
1085    switch (filter)
1086    {
1087        case GL_LINEAR:
1088        case GL_NEAREST:
1089            return MTLSamplerMipFilterNotMipmapped;
1090        case GL_LINEAR_MIPMAP_LINEAR:
1091        case GL_NEAREST_MIPMAP_LINEAR:
1092            return MTLSamplerMipFilterLinear;
1093        case GL_NEAREST_MIPMAP_NEAREST:
1094        case GL_LINEAR_MIPMAP_NEAREST:
1095            return MTLSamplerMipFilterNearest;
1096        default:
1097            UNIMPLEMENTED();
1098            return MTLSamplerMipFilterNotMipmapped;
1099    }
1100}
1101
1102MTLSamplerAddressMode GetSamplerAddressMode(GLenum wrap)
1103{
1104    switch (wrap)
1105    {
1106        case GL_CLAMP_TO_EDGE:
1107            return MTLSamplerAddressModeClampToEdge;
1108        case GL_MIRROR_CLAMP_TO_EDGE_EXT:
1109            return MTLSamplerAddressModeMirrorClampToEdge;
1110        case GL_REPEAT:
1111            return MTLSamplerAddressModeRepeat;
1112        case GL_MIRRORED_REPEAT:
1113            return MTLSamplerAddressModeMirrorRepeat;
1114        default:
1115            UNIMPLEMENTED();
1116            return MTLSamplerAddressModeClampToEdge;
1117    }
1118}
1119
1120MTLBlendFactor GetBlendFactor(gl::BlendFactorType factor)
1121{
1122    switch (factor)
1123    {
1124        case gl::BlendFactorType::Zero:
1125            return MTLBlendFactorZero;
1126        case gl::BlendFactorType::One:
1127            return MTLBlendFactorOne;
1128        case gl::BlendFactorType::SrcColor:
1129            return MTLBlendFactorSourceColor;
1130        case gl::BlendFactorType::OneMinusSrcColor:
1131            return MTLBlendFactorOneMinusSourceColor;
1132        case gl::BlendFactorType::SrcAlpha:
1133            return MTLBlendFactorSourceAlpha;
1134        case gl::BlendFactorType::OneMinusSrcAlpha:
1135            return MTLBlendFactorOneMinusSourceAlpha;
1136        case gl::BlendFactorType::DstColor:
1137            return MTLBlendFactorDestinationColor;
1138        case gl::BlendFactorType::OneMinusDstColor:
1139            return MTLBlendFactorOneMinusDestinationColor;
1140        case gl::BlendFactorType::DstAlpha:
1141            return MTLBlendFactorDestinationAlpha;
1142        case gl::BlendFactorType::OneMinusDstAlpha:
1143            return MTLBlendFactorOneMinusDestinationAlpha;
1144        case gl::BlendFactorType::SrcAlphaSaturate:
1145            return MTLBlendFactorSourceAlphaSaturated;
1146        case gl::BlendFactorType::ConstantColor:
1147            return MTLBlendFactorBlendColor;
1148        case gl::BlendFactorType::OneMinusConstantColor:
1149            return MTLBlendFactorOneMinusBlendColor;
1150        case gl::BlendFactorType::ConstantAlpha:
1151            return MTLBlendFactorBlendAlpha;
1152        case gl::BlendFactorType::OneMinusConstantAlpha:
1153            return MTLBlendFactorOneMinusBlendAlpha;
1154        case gl::BlendFactorType::Src1Color:
1155            return MTLBlendFactorSource1Color;
1156        case gl::BlendFactorType::OneMinusSrc1Color:
1157            return MTLBlendFactorOneMinusSource1Color;
1158        case gl::BlendFactorType::Src1Alpha:
1159            return MTLBlendFactorSource1Alpha;
1160        case gl::BlendFactorType::OneMinusSrc1Alpha:
1161            return MTLBlendFactorOneMinusSource1Alpha;
1162        default:
1163            UNREACHABLE();
1164            return MTLBlendFactorZero;
1165    }
1166}
1167
1168MTLBlendOperation GetBlendOp(gl::BlendEquationType op)
1169{
1170    switch (op)
1171    {
1172        case gl::BlendEquationType::Add:
1173            return MTLBlendOperationAdd;
1174        case gl::BlendEquationType::Subtract:
1175            return MTLBlendOperationSubtract;
1176        case gl::BlendEquationType::ReverseSubtract:
1177            return MTLBlendOperationReverseSubtract;
1178        case gl::BlendEquationType::Min:
1179            return MTLBlendOperationMin;
1180        case gl::BlendEquationType::Max:
1181            return MTLBlendOperationMax;
1182        default:
1183            UNREACHABLE();
1184            return MTLBlendOperationAdd;
1185    }
1186}
1187
1188MTLCompareFunction GetCompareFunc(GLenum func)
1189{
1190    switch (func)
1191    {
1192        case GL_NEVER:
1193            return MTLCompareFunctionNever;
1194        case GL_ALWAYS:
1195            return MTLCompareFunctionAlways;
1196        case GL_LESS:
1197            return MTLCompareFunctionLess;
1198        case GL_LEQUAL:
1199            return MTLCompareFunctionLessEqual;
1200        case GL_EQUAL:
1201            return MTLCompareFunctionEqual;
1202        case GL_GREATER:
1203            return MTLCompareFunctionGreater;
1204        case GL_GEQUAL:
1205            return MTLCompareFunctionGreaterEqual;
1206        case GL_NOTEQUAL:
1207            return MTLCompareFunctionNotEqual;
1208        default:
1209            UNREACHABLE();
1210            return MTLCompareFunctionAlways;
1211    }
1212}
1213
1214MTLStencilOperation GetStencilOp(GLenum op)
1215{
1216    switch (op)
1217    {
1218        case GL_KEEP:
1219            return MTLStencilOperationKeep;
1220        case GL_ZERO:
1221            return MTLStencilOperationZero;
1222        case GL_REPLACE:
1223            return MTLStencilOperationReplace;
1224        case GL_INCR:
1225            return MTLStencilOperationIncrementClamp;
1226        case GL_DECR:
1227            return MTLStencilOperationDecrementClamp;
1228        case GL_INCR_WRAP:
1229            return MTLStencilOperationIncrementWrap;
1230        case GL_DECR_WRAP:
1231            return MTLStencilOperationDecrementWrap;
1232        case GL_INVERT:
1233            return MTLStencilOperationInvert;
1234        default:
1235            UNREACHABLE();
1236            return MTLStencilOperationKeep;
1237    }
1238}
1239
1240MTLWinding GetFrontfaceWinding(GLenum frontFaceMode, bool invert)
1241{
1242    switch (frontFaceMode)
1243    {
1244        case GL_CW:
1245            return invert ? MTLWindingCounterClockwise : MTLWindingClockwise;
1246        case GL_CCW:
1247            return invert ? MTLWindingClockwise : MTLWindingCounterClockwise;
1248        default:
1249            UNREACHABLE();
1250            return MTLWindingClockwise;
1251    }
1252}
1253
1254MTLPrimitiveTopologyClass GetPrimitiveTopologyClass(gl::PrimitiveMode mode)
1255{
1256    // NOTE(hqle): Support layered renderring in future.
1257    // In non-layered rendering mode, unspecified is enough.
1258    return MTLPrimitiveTopologyClassUnspecified;
1259}
1260
1261MTLPrimitiveType GetPrimitiveType(gl::PrimitiveMode mode)
1262{
1263    switch (mode)
1264    {
1265        case gl::PrimitiveMode::Triangles:
1266            return MTLPrimitiveTypeTriangle;
1267        case gl::PrimitiveMode::Points:
1268            return MTLPrimitiveTypePoint;
1269        case gl::PrimitiveMode::Lines:
1270            return MTLPrimitiveTypeLine;
1271        case gl::PrimitiveMode::LineStrip:
1272        case gl::PrimitiveMode::LineLoop:
1273            return MTLPrimitiveTypeLineStrip;
1274        case gl::PrimitiveMode::TriangleStrip:
1275            return MTLPrimitiveTypeTriangleStrip;
1276        case gl::PrimitiveMode::TriangleFan:
1277            // NOTE(hqle): Emulate triangle fan.
1278        default:
1279            return MTLPrimitiveTypeInvalid;
1280    }
1281}
1282
1283MTLIndexType GetIndexType(gl::DrawElementsType type)
1284{
1285    switch (type)
1286    {
1287        case gl::DrawElementsType::UnsignedShort:
1288            return MTLIndexTypeUInt16;
1289        case gl::DrawElementsType::UnsignedInt:
1290            return MTLIndexTypeUInt32;
1291        case gl::DrawElementsType::UnsignedByte:
1292            // NOTE(hqle): Convert to supported type
1293        default:
1294            return MTLIndexTypeInvalid;
1295    }
1296}
1297
1298MTLTextureSwizzle GetTextureSwizzle(GLenum swizzle)
1299{
1300    switch (swizzle)
1301    {
1302        case GL_RED:
1303            return MTLTextureSwizzleRed;
1304        case GL_GREEN:
1305            return MTLTextureSwizzleGreen;
1306        case GL_BLUE:
1307            return MTLTextureSwizzleBlue;
1308        case GL_ALPHA:
1309            return MTLTextureSwizzleAlpha;
1310        case GL_ZERO:
1311            return MTLTextureSwizzleZero;
1312        case GL_ONE:
1313            return MTLTextureSwizzleOne;
1314        default:
1315            UNREACHABLE();
1316            return MTLTextureSwizzleZero;
1317    }
1318}
1319
1320MTLColorWriteMask GetEmulatedColorWriteMask(const mtl::Format &mtlFormat, bool *isEmulatedOut)
1321{
1322    const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat();
1323    const angle::Format &actualFormat   = mtlFormat.actualAngleFormat();
1324    bool isFormatEmulated               = false;
1325    MTLColorWriteMask colorWritableMask = MTLColorWriteMaskAll;
1326    if (intendedFormat.alphaBits == 0 && actualFormat.alphaBits)
1327    {
1328        isFormatEmulated = true;
1329        // Disable alpha write to this texture
1330        colorWritableMask = colorWritableMask & (~MTLColorWriteMaskAlpha);
1331    }
1332    if (intendedFormat.luminanceBits == 0)
1333    {
1334        if (intendedFormat.redBits == 0 && actualFormat.redBits)
1335        {
1336            isFormatEmulated = true;
1337            // Disable red write to this texture
1338            colorWritableMask = colorWritableMask & (~MTLColorWriteMaskRed);
1339        }
1340        if (intendedFormat.greenBits == 0 && actualFormat.greenBits)
1341        {
1342            isFormatEmulated = true;
1343            // Disable green write to this texture
1344            colorWritableMask = colorWritableMask & (~MTLColorWriteMaskGreen);
1345        }
1346        if (intendedFormat.blueBits == 0 && actualFormat.blueBits)
1347        {
1348            isFormatEmulated = true;
1349            // Disable blue write to this texture
1350            colorWritableMask = colorWritableMask & (~MTLColorWriteMaskBlue);
1351        }
1352    }
1353
1354    *isEmulatedOut = isFormatEmulated;
1355
1356    return colorWritableMask;
1357}
1358
1359MTLColorWriteMask GetEmulatedColorWriteMask(const mtl::Format &mtlFormat)
1360{
1361    // Ignore isFormatEmulated boolean value
1362    bool isFormatEmulated;
1363    return GetEmulatedColorWriteMask(mtlFormat, &isFormatEmulated);
1364}
1365
1366bool IsFormatEmulated(const mtl::Format &mtlFormat)
1367{
1368    bool isFormatEmulated;
1369    (void)GetEmulatedColorWriteMask(mtlFormat, &isFormatEmulated);
1370    return isFormatEmulated;
1371}
1372
1373size_t EstimateTextureSizeInBytes(const mtl::Format &mtlFormat,
1374                                  size_t width,
1375                                  size_t height,
1376                                  size_t depth,
1377                                  size_t sampleCount,
1378                                  size_t numMips)
1379{
1380    size_t textureSizeInBytes;
1381    if (mtlFormat.getCaps().compressed)
1382    {
1383        GLuint textureSize;
1384        gl::Extents size((int)width, (int)height, (int)depth);
1385        if (!mtlFormat.intendedInternalFormat().computeCompressedImageSize(size, &textureSize))
1386        {
1387            return 0;
1388        }
1389        textureSizeInBytes = textureSize;
1390    }
1391    else
1392    {
1393        textureSizeInBytes = mtlFormat.getCaps().pixelBytes * width * height * depth * sampleCount;
1394    }
1395    if (numMips > 1)
1396    {
1397        // Estimate mipmap size.
1398        textureSizeInBytes = textureSizeInBytes * 4 / 3;
1399    }
1400    return textureSizeInBytes;
1401}
1402
1403MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask)
1404{
1405    MTLClearColor re = color;
1406
1407    if (!(colorMask & MTLColorWriteMaskAlpha))
1408    {
1409        re.alpha = kEmulatedAlphaValue;
1410    }
1411
1412    return re;
1413}
1414
1415NSUInteger GetMaxRenderTargetSizeForDeviceInBytes(const mtl::ContextDevice &device)
1416{
1417    if (SupportsAppleGPUFamily(device, 4))
1418    {
1419        return 64;
1420    }
1421    else if (SupportsAppleGPUFamily(device, 2))
1422    {
1423        return 32;
1424    }
1425    else
1426    {
1427        return 16;
1428    }
1429}
1430
1431NSUInteger GetMaxNumberOfRenderTargetsForDevice(const mtl::ContextDevice &device)
1432{
1433    if (SupportsAppleGPUFamily(device, 2) || SupportsMacGPUFamily(device, 1))
1434    {
1435        return 8;
1436    }
1437    else
1438    {
1439        return 4;
1440    }
1441}
1442
1443bool DeviceHasMaximumRenderTargetSize(id<MTLDevice> device)
1444{
1445    return !SupportsMacGPUFamily(device, 1);
1446}
1447
1448bool SupportsAppleGPUFamily(id<MTLDevice> device, uint8_t appleFamily)
1449{
1450    MTLGPUFamily family;
1451    switch (appleFamily)
1452    {
1453        case 1:
1454            family = MTLGPUFamilyApple1;
1455            break;
1456        case 2:
1457            family = MTLGPUFamilyApple2;
1458            break;
1459        case 3:
1460            family = MTLGPUFamilyApple3;
1461            break;
1462        case 4:
1463            family = MTLGPUFamilyApple4;
1464            break;
1465        case 5:
1466            family = MTLGPUFamilyApple5;
1467            break;
1468        case 6:
1469            family = MTLGPUFamilyApple6;
1470            break;
1471        case 7:
1472            family = MTLGPUFamilyApple7;
1473            break;
1474        default:
1475            return false;
1476    }
1477    return [device supportsFamily:family];
1478}
1479
1480bool SupportsMacGPUFamily(id<MTLDevice> device, uint8_t macFamily)
1481{
1482#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1483    switch (macFamily)
1484    {
1485        case 1:
1486#    if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000
1487            return [device supportsFamily:MTLGPUFamilyMacCatalyst1];
1488#    elif TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 130000
1489            return [device supportsFamily:MTLGPUFamilyMac1];
1490#    else
1491            return [device supportsFamily:MTLGPUFamilyMac2];
1492#    endif
1493        case 2:
1494#    if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000
1495            return [device supportsFamily:MTLGPUFamilyMacCatalyst2];
1496#    else
1497            return [device supportsFamily:MTLGPUFamilyMac2];
1498#    endif
1499        default:
1500            break;
1501    }
1502#endif
1503    return false;
1504}
1505
1506static NSUInteger getNextLocationForFormat(const FormatCaps &caps,
1507                                           bool isMSAA,
1508                                           NSUInteger currentRenderTargetSize)
1509{
1510    assert(!caps.compressed);
1511    uint8_t alignment         = caps.alignment;
1512    NSUInteger pixelBytes     = caps.pixelBytes;
1513    NSUInteger pixelBytesMSAA = caps.pixelBytesMSAA;
1514    pixelBytes                = isMSAA ? pixelBytesMSAA : pixelBytes;
1515
1516    currentRenderTargetSize = (currentRenderTargetSize + (alignment - 1)) & ~(alignment - 1);
1517    currentRenderTargetSize += pixelBytes;
1518    return currentRenderTargetSize;
1519}
1520
1521static NSUInteger getNextLocationForAttachment(const mtl::RenderPassAttachmentDesc &attachment,
1522                                               const Context *context,
1523                                               NSUInteger currentRenderTargetSize)
1524{
1525    mtl::TextureRef texture =
1526        attachment.implicitMSTexture ? attachment.implicitMSTexture : attachment.texture;
1527
1528    if (texture)
1529    {
1530        MTLPixelFormat pixelFormat = texture->pixelFormat();
1531        bool isMsaa                = texture->samples();
1532        const FormatCaps &caps     = context->getDisplay()->getNativeFormatCaps(pixelFormat);
1533        currentRenderTargetSize = getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize);
1534    }
1535    return currentRenderTargetSize;
1536}
1537
1538NSUInteger ComputeTotalSizeUsedForMTLRenderPassDescriptor(const mtl::RenderPassDesc &descriptor,
1539                                                          const Context *context,
1540                                                          const mtl::ContextDevice &device)
1541{
1542    NSUInteger currentRenderTargetSize = 0;
1543
1544    for (NSUInteger i = 0; i < GetMaxNumberOfRenderTargetsForDevice(device); i++)
1545    {
1546        currentRenderTargetSize = getNextLocationForAttachment(descriptor.colorAttachments[i],
1547                                                               context, currentRenderTargetSize);
1548    }
1549    if (descriptor.depthAttachment.texture == descriptor.stencilAttachment.texture)
1550    {
1551        currentRenderTargetSize = getNextLocationForAttachment(descriptor.depthAttachment, context,
1552                                                               currentRenderTargetSize);
1553    }
1554    else
1555    {
1556        currentRenderTargetSize = getNextLocationForAttachment(descriptor.depthAttachment, context,
1557                                                               currentRenderTargetSize);
1558        currentRenderTargetSize = getNextLocationForAttachment(descriptor.stencilAttachment,
1559                                                               context, currentRenderTargetSize);
1560    }
1561
1562    return currentRenderTargetSize;
1563}
1564
1565NSUInteger ComputeTotalSizeUsedForMTLRenderPipelineDescriptor(
1566    const MTLRenderPipelineDescriptor *descriptor,
1567    const Context *context,
1568    const mtl::ContextDevice &device)
1569{
1570    NSUInteger currentRenderTargetSize = 0;
1571    bool isMsaa                        = descriptor.rasterSampleCount > 1;
1572    for (NSUInteger i = 0; i < GetMaxNumberOfRenderTargetsForDevice(device); i++)
1573    {
1574        MTLRenderPipelineColorAttachmentDescriptor *color = descriptor.colorAttachments[i];
1575        if (color.pixelFormat != MTLPixelFormatInvalid)
1576        {
1577            const FormatCaps &caps = context->getDisplay()->getNativeFormatCaps(color.pixelFormat);
1578            currentRenderTargetSize =
1579                getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize);
1580        }
1581    }
1582    if (descriptor.depthAttachmentPixelFormat == descriptor.stencilAttachmentPixelFormat)
1583    {
1584        if (descriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid)
1585        {
1586            const FormatCaps &caps =
1587                context->getDisplay()->getNativeFormatCaps(descriptor.depthAttachmentPixelFormat);
1588            currentRenderTargetSize =
1589                getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize);
1590        }
1591    }
1592    else
1593    {
1594        if (descriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid)
1595        {
1596            const FormatCaps &caps =
1597                context->getDisplay()->getNativeFormatCaps(descriptor.depthAttachmentPixelFormat);
1598            currentRenderTargetSize =
1599                getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize);
1600        }
1601        if (descriptor.stencilAttachmentPixelFormat != MTLPixelFormatInvalid)
1602        {
1603            const FormatCaps &caps =
1604                context->getDisplay()->getNativeFormatCaps(descriptor.stencilAttachmentPixelFormat);
1605            currentRenderTargetSize =
1606                getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize);
1607        }
1608    }
1609    return currentRenderTargetSize;
1610}
1611
1612gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion)
1613{
1614    return gl::Box(static_cast<int>(mtlRegion.origin.x), static_cast<int>(mtlRegion.origin.y),
1615                   static_cast<int>(mtlRegion.origin.z), static_cast<int>(mtlRegion.size.width),
1616                   static_cast<int>(mtlRegion.size.height), static_cast<int>(mtlRegion.size.depth));
1617}
1618
1619MipmapNativeLevel GetNativeMipLevel(GLuint level, GLuint base)
1620{
1621    ASSERT(level >= base);
1622    return MipmapNativeLevel(level - base);
1623}
1624
1625GLuint GetGLMipLevel(const MipmapNativeLevel &nativeLevel, GLuint base)
1626{
1627    return nativeLevel.get() + base;
1628}
1629
1630angle::Result TriangleFanBoundCheck(ContextMtl *context, size_t numTris)
1631{
1632    bool indexCheck =
1633        (numTris > std::numeric_limits<unsigned int>::max() / (sizeof(unsigned int) * 3));
1634    ANGLE_CHECK(context, !indexCheck,
1635                "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, "
1636                "too many indices required.",
1637                GL_OUT_OF_MEMORY);
1638    return angle::Result::Continue;
1639}
1640
1641angle::Result GetTriangleFanIndicesCount(ContextMtl *context,
1642                                         GLsizei vetexCount,
1643                                         uint32_t *numElemsOut)
1644{
1645    size_t numTris = vetexCount - 2;
1646    ANGLE_TRY(TriangleFanBoundCheck(context, numTris));
1647    size_t numIndices = numTris * 3;
1648    ANGLE_CHECK(context, numIndices <= std::numeric_limits<uint32_t>::max(),
1649                "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, "
1650                "too many indices required.",
1651                GL_OUT_OF_MEMORY);
1652
1653    *numElemsOut = static_cast<uint32_t>(numIndices);
1654    return angle::Result::Continue;
1655}
1656
1657angle::Result CreateMslShader(mtl::Context *context,
1658                              id<MTLLibrary> shaderLib,
1659                              NSString *shaderName,
1660                              MTLFunctionConstantValues *funcConstants,
1661                              id<MTLFunction> *shaderOut)
1662{
1663    NSError *nsErr = nil;
1664
1665    id<MTLFunction> mtlShader;
1666    if (funcConstants)
1667    {
1668        mtlShader = [shaderLib newFunctionWithName:shaderName
1669                                    constantValues:funcConstants
1670                                             error:&nsErr];
1671    }
1672    else
1673    {
1674        mtlShader = [shaderLib newFunctionWithName:shaderName];
1675    }
1676
1677    [mtlShader ANGLE_MTL_AUTORELEASE];
1678    if (nsErr && !mtlShader)
1679    {
1680        std::ostringstream ss;
1681        ss << "Internal error compiling Metal shader:\n"
1682           << nsErr.localizedDescription.UTF8String << "\n";
1683
1684        ERR() << ss.str();
1685
1686        ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION);
1687    }
1688    *shaderOut = mtlShader;
1689    return angle::Result::Continue;
1690}
1691
1692angle::Result CreateMslShader(Context *context,
1693                              id<MTLLibrary> shaderLib,
1694                              NSString *shaderName,
1695                              MTLFunctionConstantValues *funcConstants,
1696                              AutoObjCPtr<id<MTLFunction>> *shaderOut)
1697{
1698    id<MTLFunction> outFunction;
1699    ANGLE_TRY(CreateMslShader(context, shaderLib, shaderName, funcConstants, &outFunction));
1700    shaderOut->retainAssign(outFunction);
1701    return angle::Result::Continue;
1702}
1703}  // namespace mtl
1704}  // namespace rx
1705