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