• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2//
3// Copyright 2019 The ANGLE Project Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7// TextureMtl.mm:
8//    Implements the class methods for TextureMtl.
9//
10
11#include "libANGLE/renderer/metal/TextureMtl.h"
12
13#include <algorithm>
14#include <initializer_list>
15
16#include "common/Color.h"
17#include "common/MemoryBuffer.h"
18#include "common/debug.h"
19#include "common/mathutil.h"
20#include "image_util/imageformats.h"
21#include "image_util/loadimage.h"
22#include "libANGLE/ErrorStrings.h"
23#include "libANGLE/Surface.h"
24#include "libANGLE/renderer/Format.h"
25#include "libANGLE/renderer/metal/BufferMtl.h"
26#include "libANGLE/renderer/metal/ContextMtl.h"
27#include "libANGLE/renderer/metal/DisplayMtl.h"
28#include "libANGLE/renderer/metal/FrameBufferMtl.h"
29#include "libANGLE/renderer/metal/ImageMtl.h"
30#include "libANGLE/renderer/metal/SamplerMtl.h"
31#include "libANGLE/renderer/metal/SurfaceMtl.h"
32#include "libANGLE/renderer/metal/mtl_common.h"
33#include "libANGLE/renderer/metal/mtl_format_utils.h"
34#include "libANGLE/renderer/metal/mtl_utils.h"
35#include "libANGLE/renderer/renderer_utils.h"
36
37namespace rx
38{
39
40namespace
41{
42
43gl::ImageIndex GetZeroLevelIndex(const mtl::TextureRef &image)
44{
45    switch (image->textureType())
46    {
47        case MTLTextureType2D:
48            return gl::ImageIndex::Make2D(0);
49        case MTLTextureTypeCube:
50            return gl::ImageIndex::MakeFromType(gl::TextureType::CubeMap, 0);
51        case MTLTextureType2DArray:
52            return gl::ImageIndex::Make2DArray(0 /** entire layers */);
53        case MTLTextureType2DMultisample:
54            return gl::ImageIndex::Make2DMultisample();
55        case MTLTextureType3D:
56            return gl::ImageIndex::Make3D(0 /** entire layers */);
57        default:
58            UNREACHABLE();
59            break;
60    }
61
62    return gl::ImageIndex();
63}
64
65// Slice is ignored if texture type is not Cube or 2D array
66gl::ImageIndex GetCubeOrArraySliceMipIndex(const mtl::TextureRef &image,
67                                           uint32_t slice,
68                                           uint32_t level)
69{
70    switch (image->textureType())
71    {
72        case MTLTextureType2D:
73            return gl::ImageIndex::Make2D(level);
74        case MTLTextureTypeCube:
75        {
76            auto cubeFace = static_cast<gl::TextureTarget>(
77                static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + slice);
78            return gl::ImageIndex::MakeCubeMapFace(cubeFace, level);
79        }
80        case MTLTextureType2DArray:
81            return gl::ImageIndex::Make2DArray(level, slice);
82        case MTLTextureType2DMultisample:
83            return gl::ImageIndex::Make2DMultisample();
84        case MTLTextureType3D:
85            return gl::ImageIndex::Make3D(level);
86        default:
87            UNREACHABLE();
88            break;
89    }
90
91    return gl::ImageIndex();
92}
93
94// layer is ignored if texture type is not Cube or 2D array or 3D
95gl::ImageIndex GetLayerMipIndex(const mtl::TextureRef &image, uint32_t layer, uint32_t level)
96{
97    switch (image->textureType())
98    {
99        case MTLTextureType2D:
100            return gl::ImageIndex::Make2D(level);
101        case MTLTextureTypeCube:
102        {
103            auto cubeFace = static_cast<gl::TextureTarget>(
104                static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + layer);
105            return gl::ImageIndex::MakeCubeMapFace(cubeFace, level);
106        }
107        case MTLTextureType2DArray:
108            return gl::ImageIndex::Make2DArray(level, layer);
109        case MTLTextureType2DMultisample:
110            return gl::ImageIndex::Make2DMultisample();
111        case MTLTextureType3D:
112            return gl::ImageIndex::Make3D(level, layer);
113        default:
114            UNREACHABLE();
115            break;
116    }
117
118    return gl::ImageIndex();
119}
120
121GLuint GetImageLayerIndexFrom(const gl::ImageIndex &index)
122{
123    switch (index.getType())
124    {
125        case gl::TextureType::_2D:
126        case gl::TextureType::_2DMultisample:
127        case gl::TextureType::Rectangle:
128            return 0;
129        case gl::TextureType::CubeMap:
130            return index.cubeMapFaceIndex();
131        case gl::TextureType::_2DArray:
132        case gl::TextureType::_3D:
133            return index.getLayerIndex();
134        default:
135            UNREACHABLE();
136    }
137
138    return 0;
139}
140
141GLuint GetImageCubeFaceIndexOrZeroFrom(const gl::ImageIndex &index)
142{
143    switch (index.getType())
144    {
145        case gl::TextureType::CubeMap:
146            return index.cubeMapFaceIndex();
147        default:
148            break;
149    }
150
151    return 0;
152}
153
154// Given texture type, get texture type of one image for a glTexImage call.
155// For example, for texture 2d, one image is also texture 2d.
156// for texture cube, one image is texture 2d.
157gl::TextureType GetTextureImageType(gl::TextureType texType)
158{
159    switch (texType)
160    {
161        case gl::TextureType::CubeMap:
162            return gl::TextureType::_2D;
163        case gl::TextureType::_2D:
164        case gl::TextureType::_2DArray:
165        case gl::TextureType::_2DMultisample:
166        case gl::TextureType::_3D:
167        case gl::TextureType::Rectangle:
168            return texType;
169        default:
170            UNREACHABLE();
171            return gl::TextureType::InvalidEnum;
172    }
173}
174
175// D24X8 by default writes depth data to high 24 bits of 32 bit integers. However, Metal separate
176// depth stencil blitting expects depth data to be in low 24 bits of the data.
177void WriteDepthStencilToDepth24(const uint8_t *srcPtr, uint8_t *dstPtr)
178{
179    auto src = reinterpret_cast<const angle::DepthStencil *>(srcPtr);
180    auto dst = reinterpret_cast<uint32_t *>(dstPtr);
181    *dst     = gl::floatToNormalized<24, uint32_t>(static_cast<float>(src->depth));
182}
183
184void CopyTextureData(const MTLSize &regionSize,
185                     size_t srcRowPitch,
186                     size_t src2DImageSize,
187                     const uint8_t *psrc,
188                     size_t destRowPitch,
189                     size_t dest2DImageSize,
190                     uint8_t *pdst)
191{
192    {
193        size_t rowCopySize = std::min(srcRowPitch, destRowPitch);
194        for (NSUInteger d = 0; d < regionSize.depth; ++d)
195        {
196            for (NSUInteger r = 0; r < regionSize.height; ++r)
197            {
198                const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch;
199                uint8_t *pCopyDst       = pdst + d * dest2DImageSize + r * destRowPitch;
200                memcpy(pCopyDst, pCopySrc, rowCopySize);
201            }
202        }
203    }
204}
205
206void ConvertDepthStencilData(const MTLSize &regionSize,
207                             const angle::Format &srcAngleFormat,
208                             size_t srcRowPitch,
209                             size_t src2DImageSize,
210                             const uint8_t *psrc,
211                             const angle::Format &dstAngleFormat,
212                             rx::PixelWriteFunction pixelWriteFunctionOverride,
213                             size_t destRowPitch,
214                             size_t dest2DImageSize,
215                             uint8_t *pdst)
216{
217    if (srcAngleFormat.id == dstAngleFormat.id)
218    {
219        size_t rowCopySize = std::min(srcRowPitch, destRowPitch);
220        for (NSUInteger d = 0; d < regionSize.depth; ++d)
221        {
222            for (NSUInteger r = 0; r < regionSize.height; ++r)
223            {
224                const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch;
225                uint8_t *pCopyDst       = pdst + d * dest2DImageSize + r * destRowPitch;
226                memcpy(pCopyDst, pCopySrc, rowCopySize);
227            }
228        }
229    }
230    else
231    {
232        rx::PixelWriteFunction pixelWriteFunction = pixelWriteFunctionOverride
233                                                        ? pixelWriteFunctionOverride
234                                                        : dstAngleFormat.pixelWriteFunction;
235        // This is only for depth & stencil case.
236        ASSERT(srcAngleFormat.depthBits || srcAngleFormat.stencilBits);
237        ASSERT(srcAngleFormat.pixelReadFunction && pixelWriteFunction);
238
239        // cache to store read result of source pixel
240        angle::DepthStencil depthStencilData;
241        auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData);
242        ASSERT(srcAngleFormat.pixelBytes <= sizeof(depthStencilData));
243
244        for (NSUInteger d = 0; d < regionSize.depth; ++d)
245        {
246            for (NSUInteger r = 0; r < regionSize.height; ++r)
247            {
248                for (NSUInteger c = 0; c < regionSize.width; ++c)
249                {
250                    const uint8_t *sourcePixelData =
251                        psrc + d * src2DImageSize + r * srcRowPitch + c * srcAngleFormat.pixelBytes;
252
253                    uint8_t *destPixelData = pdst + d * dest2DImageSize + r * destRowPitch +
254                                             c * dstAngleFormat.pixelBytes;
255
256                    srcAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData);
257                    pixelWriteFunction(sourcePixelReadData, destPixelData);
258                }
259            }
260        }
261    }
262}
263
264mtl::BlitCommandEncoder *GetBlitCommandEncoderForResources(
265    ContextMtl *contextMtl,
266    const std::initializer_list<const mtl::Resource *> &resources)
267{
268    if (std::none_of(resources.begin(), resources.end(), [contextMtl](const mtl::Resource *res) {
269            return res->hasPendingRenderWorks(contextMtl);
270        }))
271    {
272        // If no resource has pending render works waiting to be submitted, then it's safe to
273        // create a blit encoder without ending current render pass. The blit commands
274        // will run before any pending render commands.
275        return contextMtl->getBlitCommandEncoderWithoutEndingRenderEncoder();
276    }
277    return contextMtl->getBlitCommandEncoder();
278}
279
280angle::Result CopyDepthStencilTextureContentsToStagingBuffer(
281    ContextMtl *contextMtl,
282    const angle::Format &textureAngleFormat,
283    const angle::Format &stagingAngleFormat,
284    rx::PixelWriteFunction pixelWriteFunctionOverride,
285    const MTLSize &regionSize,
286    const uint8_t *data,
287    size_t bytesPerRow,
288    size_t bytesPer2DImage,
289    size_t *bufferRowPitchOut,
290    size_t *buffer2DImageSizeOut,
291    mtl::BufferRef *bufferOut)
292{
293    size_t stagingBufferRowPitch    = regionSize.width * stagingAngleFormat.pixelBytes;
294    size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height;
295    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSize.depth;
296    mtl::BufferRef stagingBuffer;
297    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
298
299    uint8_t *pdst = stagingBuffer->map(contextMtl);
300
301    ConvertDepthStencilData(regionSize, textureAngleFormat, bytesPerRow, bytesPer2DImage, data,
302                            stagingAngleFormat, pixelWriteFunctionOverride, stagingBufferRowPitch,
303                            stagingBuffer2DImageSize, pdst);
304
305    stagingBuffer->unmap(contextMtl);
306
307    *bufferOut            = stagingBuffer;
308    *bufferRowPitchOut    = stagingBufferRowPitch;
309    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
310
311    return angle::Result::Continue;
312}
313
314angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl,
315                                                 const angle::Format &textureAngleFormat,
316                                                 const MTLSize &regionSize,
317                                                 const uint8_t *data,
318                                                 size_t bytesPerRow,
319                                                 size_t bytesPer2DImage,
320                                                 size_t *bufferRowPitchOut,
321                                                 size_t *buffer2DImageSizeOut,
322                                                 mtl::BufferRef *bufferOut)
323{
324    size_t stagingBufferRowPitch    = regionSize.width * textureAngleFormat.pixelBytes;
325    size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height;
326    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSize.depth;
327    mtl::BufferRef stagingBuffer;
328    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
329
330    uint8_t *pdst = stagingBuffer->map(contextMtl);
331    CopyTextureData(regionSize, bytesPerRow, bytesPer2DImage, data, stagingBufferRowPitch,
332                    stagingBuffer2DImageSize, pdst);
333
334    stagingBuffer->unmap(contextMtl);
335
336    *bufferOut            = stagingBuffer;
337    *bufferRowPitchOut    = stagingBufferRowPitch;
338    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
339
340    return angle::Result::Continue;
341}
342
343angle::Result CopyCompressedTextureContentsToStagingBuffer(ContextMtl *contextMtl,
344                                                           const angle::Format &textureAngleFormat,
345                                                           const MTLSize &regionSizeInBlocks,
346                                                           const uint8_t *data,
347                                                           size_t bytesPerBlockRow,
348                                                           size_t bytesPer2DImage,
349                                                           size_t *bufferRowPitchOut,
350                                                           size_t *buffer2DImageSizeOut,
351                                                           mtl::BufferRef *bufferOut)
352{
353    size_t stagingBufferRowPitch    = bytesPerBlockRow;
354    size_t stagingBuffer2DImageSize = bytesPer2DImage;
355    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSizeInBlocks.depth;
356    mtl::BufferRef stagingBuffer;
357    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
358
359    uint8_t *pdst = stagingBuffer->map(contextMtl);
360    CopyTextureData(regionSizeInBlocks, bytesPerBlockRow, bytesPer2DImage, data,
361                    stagingBufferRowPitch, stagingBuffer2DImageSize, pdst);
362
363    stagingBuffer->unmap(contextMtl);
364
365    *bufferOut            = stagingBuffer;
366    *bufferRowPitchOut    = stagingBufferRowPitch;
367    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
368
369    return angle::Result::Continue;
370}
371
372angle::Result SaturateDepth(ContextMtl *contextMtl,
373                            mtl::BufferRef srcBuffer,
374                            mtl::BufferRef dstBuffer,
375                            uint32_t srcBufferOffset,
376                            uint32_t srcPitch,
377                            MTLSize size)
378{
379    static_assert(gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE <= UINT_MAX);
380    mtl::DepthSaturationParams params;
381    params.srcBuffer       = srcBuffer;
382    params.dstBuffer       = dstBuffer;
383    params.srcBufferOffset = srcBufferOffset;
384    params.dstWidth        = static_cast<uint32_t>(size.width);
385    params.dstHeight       = static_cast<uint32_t>(size.height);
386    params.srcPitch        = srcPitch;
387    ANGLE_TRY(contextMtl->getDisplay()->getUtils().saturateDepth(contextMtl, params));
388
389    return angle::Result::Continue;
390}
391
392// This will copy a buffer to:
393// - the respective level & slice of an original texture if the "dst" texture is a view.
394// - the "dst" texture if it is not a view.
395// Notes:
396// - dstSlice is a slice in the "dst" texture not original texture.
397// - dstLevel is a level in the "dst" texture not original texture.
398// This function is needed because some GPUs such as the ones having AMD Bronze driver
399// have a bug when copying a buffer to a view of a 3D texture.
400void CopyBufferToOriginalTextureIfDstIsAView(ContextMtl *contextMtl,
401                                             mtl::BlitCommandEncoder *blitEncoder,
402                                             const mtl::BufferRef &src,
403                                             size_t srcOffset,
404                                             size_t srcBytesPerRow,
405                                             size_t srcBytesPerImage,
406                                             MTLSize srcSize,
407                                             const mtl::TextureRef &dst,
408                                             const uint32_t dstSlice,
409                                             const mtl::MipmapNativeLevel &dstLevel,
410                                             MTLOrigin dstOrigin,
411                                             MTLBlitOption blitOption)
412{
413    mtl::TextureRef correctedTexture      = dst;
414    mtl::MipmapNativeLevel correctedLevel = dstLevel;
415    uint32_t correctedSlice               = dstSlice;
416    // TODO(b/343734719): Simulator has bug in parentRelativeSlice() so skip this step
417    // on simulator.
418    if (!contextMtl->getDisplay()->isSimulator() && correctedTexture->parentTexture())
419    {
420        correctedLevel = correctedLevel + correctedTexture->parentRelativeLevel().get();
421        correctedSlice += correctedTexture->parentRelativeSlice();
422        correctedTexture = correctedTexture->parentTexture();
423    }
424
425    blitEncoder->copyBufferToTexture(src, srcOffset, srcBytesPerRow, srcBytesPerImage, srcSize,
426                                     correctedTexture, correctedSlice, correctedLevel, dstOrigin,
427                                     blitOption);
428}
429
430angle::Result UploadDepthStencilTextureContentsWithStagingBuffer(
431    ContextMtl *contextMtl,
432    const angle::Format &textureAngleFormat,
433    MTLRegion region,
434    const mtl::MipmapNativeLevel &mipmapLevel,
435    uint32_t slice,
436    const uint8_t *data,
437    size_t bytesPerRow,
438    size_t bytesPer2DImage,
439    const mtl::TextureRef &texture)
440{
441    ASSERT(texture && texture->valid());
442
443    ASSERT(!texture->isCPUAccessible());
444
445    ASSERT(!textureAngleFormat.depthBits || !textureAngleFormat.stencilBits);
446
447    // Depth and stencil textures cannot be of 3D type;
448    // arrays and cube maps must be uploaded per-slice.
449    ASSERT(region.size.depth == 1);
450
451    // Copy data to staging buffer
452    size_t stagingBufferRowPitch;
453    size_t stagingBuffer2DImageSize;
454    mtl::BufferRef stagingBuffer;
455    ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
456        contextMtl, textureAngleFormat, textureAngleFormat, textureAngleFormat.pixelWriteFunction,
457        region.size, data, bytesPerRow, bytesPer2DImage, &stagingBufferRowPitch,
458        &stagingBuffer2DImageSize, &stagingBuffer));
459
460    if (textureAngleFormat.id == angle::FormatID::D32_FLOAT)
461    {
462        ANGLE_TRY(SaturateDepth(contextMtl, stagingBuffer, stagingBuffer, 0,
463                                static_cast<uint32_t>(region.size.width), region.size));
464    }
465
466    // Copy staging buffer to texture.
467    mtl::BlitCommandEncoder *encoder =
468        GetBlitCommandEncoderForResources(contextMtl, {stagingBuffer.get(), texture.get()});
469
470    CopyBufferToOriginalTextureIfDstIsAView(
471        contextMtl, encoder, stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize,
472        region.size, texture, slice, mipmapLevel, region.origin, MTLBlitOptionNone);
473
474    return angle::Result::Continue;
475}
476
477// Packed depth stencil upload using staging buffer
478angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
479    ContextMtl *contextMtl,
480    const angle::Format &textureAngleFormat,
481    MTLRegion region,
482    const mtl::MipmapNativeLevel &mipmapLevel,
483    uint32_t slice,
484    const uint8_t *data,
485    size_t bytesPerRow,
486    size_t bytesPer2DImage,
487    const mtl::TextureRef &texture)
488{
489    ASSERT(texture && texture->valid());
490
491    ASSERT(!texture->isCPUAccessible());
492
493    ASSERT(textureAngleFormat.depthBits && textureAngleFormat.stencilBits);
494
495    // Depth and stencil textures cannot be of 3D type;
496    // arrays and cube maps must be uploaded per-slice.
497    ASSERT(region.size.depth == 1);
498
499    // We have to split the depth & stencil data into 2 buffers.
500    angle::FormatID stagingDepthBufferFormatId;
501    angle::FormatID stagingStencilBufferFormatId;
502    // Custom depth write function. We cannot use those in imageformats.cpp since Metal has some
503    // special cases.
504    rx::PixelWriteFunction stagingDepthBufferWriteFunctionOverride = nullptr;
505
506    switch (textureAngleFormat.id)
507    {
508        case angle::FormatID::D24_UNORM_S8_UINT:
509            // D24_UNORM_X8_UINT writes depth data to high 24 bits. But Metal expects depth data to
510            // be in low 24 bits.
511            stagingDepthBufferFormatId              = angle::FormatID::D24_UNORM_X8_UINT;
512            stagingDepthBufferWriteFunctionOverride = WriteDepthStencilToDepth24;
513            stagingStencilBufferFormatId            = angle::FormatID::S8_UINT;
514            break;
515        case angle::FormatID::D32_FLOAT_S8X24_UINT:
516            stagingDepthBufferFormatId   = angle::FormatID::D32_FLOAT;
517            stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
518            break;
519        default:
520            ANGLE_GL_UNREACHABLE(contextMtl);
521    }
522
523    const angle::Format &angleStagingDepthFormat = angle::Format::Get(stagingDepthBufferFormatId);
524    const angle::Format &angleStagingStencilFormat =
525        angle::Format::Get(stagingStencilBufferFormatId);
526
527    size_t stagingDepthBufferRowPitch, stagingStencilBufferRowPitch;
528    size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize;
529    mtl::BufferRef stagingDepthBuffer, stagingStencilBuffer;
530
531    // Copy depth data to staging depth buffer
532    ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
533        contextMtl, textureAngleFormat, angleStagingDepthFormat,
534        stagingDepthBufferWriteFunctionOverride, region.size, data, bytesPerRow, bytesPer2DImage,
535        &stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthBuffer));
536
537    // Copy stencil data to staging stencil buffer
538    ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
539        contextMtl, textureAngleFormat, angleStagingStencilFormat, nullptr, region.size, data,
540        bytesPerRow, bytesPer2DImage, &stagingStencilBufferRowPitch,
541        &stagingStencilBuffer2DImageSize, &stagingStencilBuffer));
542
543    if (angleStagingDepthFormat.id == angle::FormatID::D32_FLOAT)
544    {
545        ANGLE_TRY(SaturateDepth(contextMtl, stagingDepthBuffer, stagingDepthBuffer, 0,
546                                static_cast<uint32_t>(region.size.width), region.size));
547    }
548
549    mtl::BlitCommandEncoder *encoder = GetBlitCommandEncoderForResources(
550        contextMtl, {stagingDepthBuffer.get(), stagingStencilBuffer.get(), texture.get()});
551
552    CopyBufferToOriginalTextureIfDstIsAView(
553        contextMtl, encoder, stagingDepthBuffer, 0, stagingDepthBufferRowPitch,
554        stagingDepthBuffer2DImageSize, region.size, texture, slice, mipmapLevel, region.origin,
555        MTLBlitOptionDepthFromDepthStencil);
556    CopyBufferToOriginalTextureIfDstIsAView(
557        contextMtl, encoder, stagingStencilBuffer, 0, stagingStencilBufferRowPitch,
558        stagingStencilBuffer2DImageSize, region.size, texture, slice, mipmapLevel, region.origin,
559        MTLBlitOptionStencilFromDepthStencil);
560
561    return angle::Result::Continue;
562}
563
564angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl,
565                                                     const angle::Format &textureAngleFormat,
566                                                     MTLRegion region,
567                                                     const mtl::MipmapNativeLevel &mipmapLevel,
568                                                     uint32_t slice,
569                                                     const uint8_t *data,
570                                                     size_t bytesPerRow,
571                                                     size_t bytesPer2DImage,
572                                                     const mtl::TextureRef &texture)
573{
574    ASSERT(texture && texture->valid());
575
576    angle::FormatID stagingBufferFormatID   = textureAngleFormat.id;
577    const angle::Format &angleStagingFormat = angle::Format::Get(stagingBufferFormatID);
578
579    size_t stagingBufferRowPitch;
580    size_t stagingBuffer2DImageSize;
581    mtl::BufferRef stagingBuffer;
582
583    // Block-compressed formats need a bit of massaging for copy.
584    if (textureAngleFormat.isBlock)
585    {
586        GLenum internalFormat         = textureAngleFormat.glInternalFormat;
587        const gl::InternalFormat &fmt = gl::GetSizedInternalFormatInfo(internalFormat);
588        MTLRegion newRegion           = region;
589        bytesPerRow =
590            (region.size.width + fmt.compressedBlockWidth - 1) / fmt.compressedBlockWidth * 16;
591        bytesPer2DImage = (region.size.height + fmt.compressedBlockHeight - 1) /
592                          fmt.compressedBlockHeight * bytesPerRow;
593        newRegion.size.width =
594            (region.size.width + fmt.compressedBlockWidth - 1) / fmt.compressedBlockWidth;
595        newRegion.size.height =
596            (region.size.height + fmt.compressedBlockHeight - 1) / fmt.compressedBlockHeight;
597        ANGLE_TRY(CopyCompressedTextureContentsToStagingBuffer(
598            contextMtl, angleStagingFormat, newRegion.size, data, bytesPerRow, bytesPer2DImage,
599            &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer));
600    }
601    // Copy to staging buffer before uploading to texture.
602    else
603    {
604        ANGLE_TRY(CopyTextureContentsToStagingBuffer(
605            contextMtl, angleStagingFormat, region.size, data, bytesPerRow, bytesPer2DImage,
606            &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer));
607    }
608    mtl::BlitCommandEncoder *encoder =
609        GetBlitCommandEncoderForResources(contextMtl, {stagingBuffer.get(), texture.get()});
610
611    CopyBufferToOriginalTextureIfDstIsAView(
612        contextMtl, encoder, stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize,
613        region.size, texture, slice, mipmapLevel, region.origin, 0);
614
615    return angle::Result::Continue;
616}
617
618angle::Result UploadTextureContents(const gl::Context *context,
619                                    const angle::Format &textureAngleFormat,
620                                    const MTLRegion &region,
621                                    const mtl::MipmapNativeLevel &mipmapLevel,
622                                    uint32_t slice,
623                                    const uint8_t *data,
624                                    size_t bytesPerRow,
625                                    size_t bytesPer2DImage,
626                                    bool avoidStagingBuffers,
627                                    const mtl::TextureRef &texture)
628
629{
630    ASSERT(texture && texture->valid());
631    ContextMtl *contextMtl       = mtl::GetImpl(context);
632    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(textureAngleFormat.id);
633
634    bool preferGPUInitialization =
635        !avoidStagingBuffers &&
636        PreferStagedTextureUploads(context, texture, mtlFormat, mtl::StagingPurpose::Upload);
637    if (texture->isCPUAccessible() && !preferGPUInitialization)
638    {
639        if (mtlFormat.isPVRTC())
640        {
641            // Replace Region Validation: rowBytes must be 0
642            bytesPerRow = 0;
643        }
644
645        // If texture is CPU accessible, just call replaceRegion() directly.
646        texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow,
647                               bytesPer2DImage);
648
649        return angle::Result::Continue;
650    }
651
652    // Texture is not CPU accessible or staging is forced due to a workaround
653    if (!textureAngleFormat.depthBits && !textureAngleFormat.stencilBits)
654    {
655        // Upload color data
656        ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, textureAngleFormat, region,
657                                                         mipmapLevel, slice, data, bytesPerRow,
658                                                         bytesPer2DImage, texture));
659    }
660    else if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits)
661    {
662        // Packed depth-stencil
663        ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer(
664            contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow,
665            bytesPer2DImage, texture));
666    }
667    else
668    {
669        // Depth or stencil
670        ANGLE_TRY(UploadDepthStencilTextureContentsWithStagingBuffer(
671            contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow,
672            bytesPer2DImage, texture));
673    }
674
675    return angle::Result::Continue;
676}
677
678// This might be unused on platform not supporting swizzle.
679ANGLE_APPLE_UNUSED
680GLenum OverrideSwizzleValue(const gl::Context *context,
681                            GLenum swizzle,
682                            const mtl::Format &format,
683                            const gl::InternalFormat &glInternalFormat)
684{
685    if (format.actualAngleFormat().hasDepthOrStencilBits())
686    {
687        ASSERT(!format.swizzled);
688        if (context->getState().getClientMajorVersion() >= 3 && glInternalFormat.sized)
689        {
690            // ES 3.1 spec: treat depth and stencil textures as red textures during sampling.
691            if (swizzle == GL_GREEN || swizzle == GL_BLUE)
692            {
693                return GL_NONE;
694            }
695            else if (swizzle == GL_ALPHA)
696            {
697                return GL_ONE;
698            }
699        }
700        else
701        {
702            // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_depth_texture.txt
703            // Treat depth texture as luminance texture during sampling.
704            if (swizzle == GL_GREEN || swizzle == GL_BLUE)
705            {
706                return GL_RED;
707            }
708            else if (swizzle == GL_ALPHA)
709            {
710                return GL_ONE;
711            }
712        }
713    }
714    else if (format.swizzled)
715    {
716        // Combine the swizzles
717        switch (swizzle)
718        {
719            case GL_RED:
720                return format.swizzle[0];
721            case GL_GREEN:
722                return format.swizzle[1];
723            case GL_BLUE:
724                return format.swizzle[2];
725            case GL_ALPHA:
726                return format.swizzle[3];
727            default:
728                break;
729        }
730    }
731
732    return swizzle;
733}
734
735mtl::TextureRef &GetLayerLevelTextureView(
736    TextureMtl::LayerLevelTextureViewVector *layerLevelTextureViews,
737    uint32_t layer,
738    uint32_t level,
739    uint32_t layerCount,
740    uint32_t levelCount)
741{
742    // Lazily allocate the full layer and level count to not trigger any std::vector reallocations.
743    if (layerLevelTextureViews->empty())
744    {
745        layerLevelTextureViews->resize(layerCount);
746    }
747    ASSERT(layerLevelTextureViews->size() > layer);
748
749    TextureMtl::TextureViewVector &levelTextureViews = (*layerLevelTextureViews)[layer];
750
751    if (levelTextureViews.empty())
752    {
753        levelTextureViews.resize(levelCount);
754    }
755    ASSERT(levelTextureViews.size() > level);
756
757    return levelTextureViews[level];
758}
759
760}  // namespace
761
762// TextureMtl::NativeTextureWrapper implementation.
763// This class uses GL level instead of mtl::MipmapNativeLevel.
764// It seamlessly translates GL level to native level based on the base GL information passed in the
765// constructor. The base GL level is unchanged thoughout the lifetime of this object.
766// Note that NativeTextureWrapper's base GL level doesn't necessarily mean it's the same as a GL
767// texture's real base level.
768// - If NativeTextureWrapper holds a native storage of a non-immutable texture,
769// its base GL level is indeed equal to the GL texture's base level.
770// - If NativeTextureWrapper holds a native storage of an immutable texture,
771// it base GL level is actually 0.
772// - If NativeTextureWrapper holds a view from base level to max level of a GL texture,
773// then its base GL level is equal to the GL texture's base level.
774class TextureMtl::NativeTextureWrapper : angle::NonCopyable
775{
776  public:
777    NativeTextureWrapper(mtl::TextureRef texture, GLuint baseGLLevel)
778        : mNativeTexture(std::move(texture)), mBaseGLLevel(baseGLLevel)
779    {
780        ASSERT(mNativeTexture && mNativeTexture->valid());
781    }
782
783    operator const mtl::TextureRef &() const { return mNativeTexture; }
784    const mtl::TextureRef &getNativeTexture() const { return mNativeTexture; }
785
786    void replaceRegion(ContextMtl *context,
787                       const MTLRegion &region,
788                       GLuint glLevel,
789                       uint32_t slice,
790                       const uint8_t *data,
791                       size_t bytesPerRow,
792                       size_t bytesPer2DImage)
793    {
794        mNativeTexture->replaceRegion(context, region, getNativeLevel(glLevel), slice, data,
795                                      bytesPerRow, bytesPer2DImage);
796    }
797
798    void getBytes(ContextMtl *context,
799                  size_t bytesPerRow,
800                  size_t bytesPer2DInage,
801                  const MTLRegion &region,
802                  GLuint glLevel,
803                  uint32_t slice,
804                  uint8_t *dataOut)
805    {
806        mNativeTexture->getBytes(context, bytesPerRow, bytesPer2DInage, region,
807                                 getNativeLevel(glLevel), slice, dataOut);
808    }
809
810    GLuint getBaseGLLevel() const { return mBaseGLLevel; }
811    // Get max addressable GL level that this texture supports.
812    GLuint getMaxSupportedGLLevel() const { return mBaseGLLevel + mipmapLevels() - 1; }
813    // Check whether a GL level refers to a valid mip in this texture.
814    bool isGLLevelSupported(GLuint glLevel)
815    {
816        return glLevel >= mBaseGLLevel && glLevel <= getMaxSupportedGLLevel();
817    }
818    mtl::MipmapNativeLevel getNativeLevel(GLuint glLevel) const
819    {
820        return mtl::GetNativeMipLevel(glLevel, mBaseGLLevel);
821    }
822    GLuint getGLLevel(const mtl::MipmapNativeLevel &nativeLevel) const
823    {
824        return mtl::GetGLMipLevel(nativeLevel, mBaseGLLevel);
825    }
826
827    mtl::TextureRef getStencilView() { return mNativeTexture->getStencilView(); }
828
829    MTLTextureType textureType() const { return mNativeTexture->textureType(); }
830    MTLPixelFormat pixelFormat() const { return mNativeTexture->pixelFormat(); }
831
832    uint32_t mipmapLevels() const { return mNativeTexture->mipmapLevels(); }
833    uint32_t arrayLength() const { return mNativeTexture->arrayLength(); }
834    uint32_t cubeFaces() const { return mNativeTexture->cubeFaces(); }
835    uint32_t cubeFacesOrArrayLength() const { return mNativeTexture->cubeFacesOrArrayLength(); }
836
837    uint32_t width(GLuint glLevel) const { return mNativeTexture->width(getNativeLevel(glLevel)); }
838    uint32_t height(GLuint glLevel) const
839    {
840        return mNativeTexture->height(getNativeLevel(glLevel));
841    }
842    uint32_t depth(GLuint glLevel) const { return mNativeTexture->depth(getNativeLevel(glLevel)); }
843
844    gl::Extents size(GLuint glLevel) const { return mNativeTexture->size(getNativeLevel(glLevel)); }
845
846    // Get width, height, depth, size at base level.
847    uint32_t widthAt0() const { return width(mBaseGLLevel); }
848    uint32_t heightAt0() const { return height(mBaseGLLevel); }
849    uint32_t depthAt0() const { return depth(mBaseGLLevel); }
850    gl::Extents sizeAt0() const { return size(mBaseGLLevel); }
851
852  protected:
853    mtl::TextureRef mNativeTexture;
854    const GLuint mBaseGLLevel;
855};
856
857// This class extends NativeTextureWrapper with support for view creation
858class TextureMtl::NativeTextureWrapperWithViewSupport : public NativeTextureWrapper
859{
860  public:
861    NativeTextureWrapperWithViewSupport(mtl::TextureRef texture, GLuint baseGLLevel)
862        : NativeTextureWrapper(std::move(texture), baseGLLevel)
863    {}
864
865    // Create a view of one slice at a level.
866    mtl::TextureRef createSliceMipView(uint32_t slice, GLuint glLevel)
867    {
868        return mNativeTexture->createSliceMipView(slice, getNativeLevel(glLevel));
869    }
870    // Create a levels range view
871    mtl::TextureRef createMipsView(GLuint glLevel, uint32_t levels)
872    {
873        return mNativeTexture->createMipsView(getNativeLevel(glLevel), levels);
874    }
875    // Create a view of a level.
876    mtl::TextureRef createMipView(GLuint glLevel)
877    {
878        return mNativeTexture->createMipView(getNativeLevel(glLevel));
879    }
880    // Create a view for a shader image binding.
881    mtl::TextureRef createShaderImageView2D(GLuint glLevel, int layer, MTLPixelFormat format)
882    {
883        return mNativeTexture->createShaderImageView2D(getNativeLevel(glLevel), layer, format);
884    }
885
886    // Create a swizzled view
887    mtl::TextureRef createMipsSwizzleView(GLuint glLevel,
888                                          uint32_t levels,
889                                          MTLPixelFormat format,
890                                          const MTLTextureSwizzleChannels &swizzle)
891    {
892        return mNativeTexture->createMipsSwizzleView(getNativeLevel(glLevel), levels, format,
893                                                     swizzle);
894    }
895};
896
897// TextureMtl implementation
898TextureMtl::TextureMtl(const gl::TextureState &state) : TextureImpl(state) {}
899
900TextureMtl::~TextureMtl() = default;
901
902void TextureMtl::onDestroy(const gl::Context *context)
903{
904    deallocateNativeStorage(/*keepImages=*/false);
905    mBoundSurface = nullptr;
906}
907
908void TextureMtl::deallocateNativeStorage(bool keepImages, bool keepSamplerStateAndFormat)
909{
910
911    if (!keepImages)
912    {
913        mTexImageDefs.clear();
914        mShaderImageViews.clear();
915    }
916    else if (mNativeTextureStorage)
917    {
918        // Release native texture but keep its image definitions.
919        retainImageDefinitions();
920    }
921
922    mNativeTextureStorage       = nullptr;
923    mViewFromBaseToMaxLevel     = nullptr;
924    mSwizzleStencilSamplingView = nullptr;
925
926    // Clear render target cache for each texture's image. We don't erase them because they
927    // might still be referenced by a framebuffer.
928    for (auto &samplesMapRenderTargets : mRenderTargets)
929    {
930        for (RenderTargetMtl &perSampleCountRenderTarget : samplesMapRenderTargets.second)
931        {
932            perSampleCountRenderTarget.reset();
933        }
934    }
935
936    for (auto &samplesMapMSTextures : mImplicitMSTextures)
937    {
938        for (mtl::TextureRef &perSampleCountMSTexture : samplesMapMSTextures.second)
939        {
940            perSampleCountMSTexture.reset();
941        }
942    }
943
944    for (mtl::TextureRef &view : mLevelViewsWithinBaseMax)
945    {
946        view.reset();
947    }
948
949    if (!keepSamplerStateAndFormat)
950    {
951        mMetalSamplerState = nil;
952        mFormat            = mtl::Format();
953    }
954}
955
956angle::Result TextureMtl::ensureNativeStorageCreated(const gl::Context *context)
957{
958    if (mNativeTextureStorage)
959    {
960        return angle::Result::Continue;
961    }
962
963    // This should not be called from immutable texture.
964    ASSERT(!isImmutableOrPBuffer());
965    ASSERT(mState.getType() != gl::TextureType::_2DMultisample);
966    ASSERT(mState.getType() != gl::TextureType::_2DMultisampleArray);
967
968    ContextMtl *contextMtl = mtl::GetImpl(context);
969
970    // Create actual texture object:
971    GLuint mips        = mState.getMipmapMaxLevel() - mState.getEffectiveBaseLevel() + 1;
972    gl::ImageDesc desc = mState.getBaseLevelDesc();
973    ANGLE_CHECK(contextMtl, desc.format.valid(), gl::err::kInternalError, GL_INVALID_OPERATION);
974    angle::FormatID angleFormatId =
975        angle::Format::InternalFormatToID(desc.format.info->sizedInternalFormat);
976    mFormat = contextMtl->getPixelFormat(angleFormatId);
977
978    ANGLE_TRY(createNativeStorage(context, mState.getType(), mips, 0, desc.size));
979
980    // Transfer data from defined images to actual texture object
981    int numCubeFaces = static_cast<int>(mNativeTextureStorage->cubeFaces());
982    for (int face = 0; face < numCubeFaces; ++face)
983    {
984        for (mtl::MipmapNativeLevel actualMip = mtl::kZeroNativeMipLevel; actualMip.get() < mips;
985             ++actualMip)
986        {
987            GLuint imageMipLevel             = mNativeTextureStorage->getGLLevel(actualMip);
988            mtl::TextureRef &imageToTransfer = mTexImageDefs[face][imageMipLevel].image;
989
990            // Only transfer if this mip & slice image has been defined and in correct size &
991            // format.
992            gl::Extents actualMipSize = mNativeTextureStorage->size(imageMipLevel);
993            if (imageToTransfer && imageToTransfer->sizeAt0() == actualMipSize &&
994                imageToTransfer->arrayLength() == mNativeTextureStorage->arrayLength() &&
995                imageToTransfer->pixelFormat() == mNativeTextureStorage->pixelFormat())
996            {
997                mtl::BlitCommandEncoder *encoder = GetBlitCommandEncoderForResources(
998                    contextMtl,
999                    {imageToTransfer.get(), mNativeTextureStorage->getNativeTexture().get()});
1000
1001                encoder->copyTexture(imageToTransfer, 0, mtl::kZeroNativeMipLevel,
1002                                     *mNativeTextureStorage, face, actualMip,
1003                                     imageToTransfer->arrayLength(), 1);
1004
1005                // Invalidate texture image definition at this index so that we can make it a
1006                // view of the native texture at this index later.
1007                imageToTransfer = nullptr;
1008            }
1009        }
1010    }
1011
1012    return angle::Result::Continue;
1013}
1014
1015angle::Result TextureMtl::createNativeStorage(const gl::Context *context,
1016                                              gl::TextureType type,
1017                                              GLuint mips,
1018                                              GLuint samples,
1019                                              const gl::Extents &size)
1020{
1021    ASSERT(samples == 0 || mips == 0);
1022    ContextMtl *contextMtl = mtl::GetImpl(context);
1023
1024    // Create actual texture object:
1025    mSlices              = 1;
1026    bool allowFormatView = mFormat.hasDepthAndStencilBits() ||
1027                           needsFormatViewForPixelLocalStorage(
1028                               contextMtl->getDisplay()->getNativePixelLocalStorageOptions());
1029    mtl::TextureRef nativeTextureStorage;
1030    switch (type)
1031    {
1032        case gl::TextureType::_2D:
1033            ANGLE_TRY(mtl::Texture::Make2DTexture(
1034                contextMtl, mFormat, size.width, size.height, mips,
1035                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1036            break;
1037        case gl::TextureType::CubeMap:
1038            mSlices = 6;
1039            ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, size.width, mips,
1040                                                    /** renderTargetOnly */ false, allowFormatView,
1041                                                    &nativeTextureStorage));
1042            break;
1043        case gl::TextureType::_3D:
1044            ANGLE_TRY(mtl::Texture::Make3DTexture(
1045                contextMtl, mFormat, size.width, size.height, size.depth, mips,
1046                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1047            break;
1048        case gl::TextureType::_2DArray:
1049            mSlices = size.depth;
1050            ANGLE_TRY(mtl::Texture::Make2DArrayTexture(
1051                contextMtl, mFormat, size.width, size.height, mips, mSlices,
1052                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1053            break;
1054        case gl::TextureType::_2DMultisample:
1055            ANGLE_TRY(mtl::Texture::Make2DMSTexture(
1056                contextMtl, mFormat, size.width, size.height, samples,
1057                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1058            break;
1059        default:
1060            UNREACHABLE();
1061    }
1062
1063    if (mState.getImmutableFormat())
1064    {
1065        mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1066            std::move(nativeTextureStorage), /*baseGLLevel=*/0);
1067    }
1068    else
1069    {
1070        mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1071            std::move(nativeTextureStorage), /*baseGLLevel=*/mState.getEffectiveBaseLevel());
1072    }
1073
1074    ANGLE_TRY(checkForEmulatedChannels(context, mFormat, *mNativeTextureStorage));
1075
1076    ANGLE_TRY(createViewFromBaseToMaxLevel());
1077
1078    // Create sampler state
1079    ANGLE_TRY(ensureSamplerStateCreated(context));
1080
1081    return angle::Result::Continue;
1082}
1083
1084angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context)
1085{
1086    if (mMetalSamplerState)
1087    {
1088        return angle::Result::Continue;
1089    }
1090
1091    ContextMtl *contextMtl = mtl::GetImpl(context);
1092
1093    mtl::SamplerDesc samplerDesc(mState.getSamplerState());
1094
1095    if (mFormat.actualAngleFormat().depthBits && !mFormat.getCaps().filterable)
1096    {
1097        // On devices not supporting filtering for depth textures, we need to convert to nearest
1098        // here.
1099        samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
1100        samplerDesc.magFilter = MTLSamplerMinMagFilterNearest;
1101        if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped)
1102        {
1103            samplerDesc.mipFilter = MTLSamplerMipFilterNearest;
1104        }
1105
1106        samplerDesc.maxAnisotropy = 1;
1107    }
1108
1109    // OpenGL ES 3.x: The rules for texel selection are modified
1110    // for cube maps so that texture wrap modes are ignored.
1111    if ((mState.getType() == gl::TextureType::CubeMap ||
1112         mState.getType() == gl::TextureType::CubeMapArray) &&
1113        context->getState().getClientMajorVersion() >= 3)
1114    {
1115        samplerDesc.rAddressMode = MTLSamplerAddressModeClampToEdge;
1116        samplerDesc.sAddressMode = MTLSamplerAddressModeClampToEdge;
1117        samplerDesc.tAddressMode = MTLSamplerAddressModeClampToEdge;
1118    }
1119    mMetalSamplerState = contextMtl->getDisplay()->getStateCache().getSamplerState(
1120        contextMtl->getMetalDevice(), samplerDesc);
1121
1122    return angle::Result::Continue;
1123}
1124
1125angle::Result TextureMtl::createViewFromBaseToMaxLevel()
1126{
1127    ASSERT(mNativeTextureStorage);
1128    uint32_t maxLevel =
1129        std::min(mNativeTextureStorage->getMaxSupportedGLLevel(), mState.getEffectiveMaxLevel());
1130
1131    mtl::TextureRef nativeViewFromBaseToMaxLevelRef;
1132    if (maxLevel == mNativeTextureStorage->getMaxSupportedGLLevel() &&
1133        mState.getEffectiveBaseLevel() == mNativeTextureStorage->getBaseGLLevel())
1134    {
1135        // If base & max level are the same in mNativeTextureStorage, we don't need
1136        // a dedicated view. Furthermore, Intel driver has some bugs when sampling a view
1137        // of a stencil texture.
1138        nativeViewFromBaseToMaxLevelRef = mNativeTextureStorage->getNativeTexture();
1139    }
1140    else
1141    {
1142        uint32_t baseToMaxLevels = maxLevel - mState.getEffectiveBaseLevel() + 1;
1143        nativeViewFromBaseToMaxLevelRef =
1144            mNativeTextureStorage->createMipsView(mState.getEffectiveBaseLevel(), baseToMaxLevels);
1145    }
1146
1147    mViewFromBaseToMaxLevel = std::make_unique<NativeTextureWrapper>(
1148        nativeViewFromBaseToMaxLevelRef, mState.getEffectiveBaseLevel());
1149
1150    // Recreate in bindToShader()
1151    mSwizzleStencilSamplingView = nullptr;
1152    return angle::Result::Continue;
1153}
1154
1155angle::Result TextureMtl::onBaseMaxLevelsChanged(const gl::Context *context)
1156{
1157    if (!mNativeTextureStorage)
1158    {
1159        return angle::Result::Continue;
1160    }
1161
1162    if (isImmutableOrPBuffer())
1163    {
1164        // For immutable texture, only recreate base-max view.
1165        ANGLE_TRY(createViewFromBaseToMaxLevel());
1166        // Invalidate base-max per level views so that they can be recreated
1167        // in generateMipmap()
1168        for (mtl::TextureRef &view : mLevelViewsWithinBaseMax)
1169        {
1170            view.reset();
1171        }
1172        return angle::Result::Continue;
1173    }
1174
1175    if (mState.getEffectiveBaseLevel() == mNativeTextureStorage->getBaseGLLevel() &&
1176        mState.getMipmapMaxLevel() == mNativeTextureStorage->getMaxSupportedGLLevel())
1177    {
1178        ASSERT(mState.getBaseLevelDesc().size == mNativeTextureStorage->sizeAt0());
1179        // If level range remain the same, don't recreate the texture storage.
1180        // This might feel unnecessary at first since the front-end might prevent redundant base/max
1181        // level change already. However, there are cases that cause native storage to be created
1182        // before base/max level dirty bit is passed to Metal backend and lead to unwanted problems.
1183        // Example:
1184        // 1. texture with a non-default base/max level state is set.
1185        // 2. The texture is used first as a framebuffer attachment. This operation does not fully
1186        //    sync the texture state and therefore does not unset base/max level dirty bits.
1187        // 3. The same texture is then used for sampling; this operation fully syncs the texture
1188        //    state. Base/max level dirty bits may lead to recreating the texture storage thus
1189        //    invalidating native render target references created in step 2.
1190        // 4. If the framebuffer created in step 2 is used again, its native render target
1191        //    references will not be updated to point to the new storage because everything is in
1192        //    sync from the frontend point of view.
1193        // 5. Note: if the new range is different, it is expected that native render target
1194        //    references will be updated during draw framebuffer sync.
1195        return angle::Result::Continue;
1196    }
1197
1198    ContextMtl *contextMtl = mtl::GetImpl(context);
1199
1200    // We need to recreate a new native texture storage with number of levels = max level - base
1201    // level + 1. This can be achieved by simply deleting the old storage. The storage will be
1202    // lazily recreated later via ensureNativeStorageCreated().
1203    // Note: We release the native texture storage but keep old image definitions. So that when the
1204    // storage is recreated, its levels can be recreated with data from the old image definitions
1205    // respectively.
1206    deallocateNativeStorage(/*keepImages=*/true, /*keepSamplerStateAndFormat=*/true);
1207
1208    // Tell context to rebind textures
1209    contextMtl->invalidateCurrentTextures();
1210
1211    return angle::Result::Continue;
1212}
1213
1214angle::Result TextureMtl::ensureImageCreated(const gl::Context *context,
1215                                             const gl::ImageIndex &index)
1216{
1217    mtl::TextureRef &image = getImage(index);
1218    if (!image)
1219    {
1220        // Image at this level hasn't been defined yet. We need to define it:
1221        const gl::ImageDesc &desc = mState.getImageDesc(index);
1222        ANGLE_TRY(redefineImage(context, index, mFormat, desc.size));
1223    }
1224    return angle::Result::Continue;
1225}
1226
1227angle::Result TextureMtl::ensureLevelViewsWithinBaseMaxCreated()
1228{
1229    ASSERT(mViewFromBaseToMaxLevel);
1230    for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel;
1231         mip.get() < mViewFromBaseToMaxLevel->mipmapLevels(); ++mip)
1232    {
1233        if (mLevelViewsWithinBaseMax[mip])
1234        {
1235            continue;
1236        }
1237
1238        GLuint mipGLLevel = mViewFromBaseToMaxLevel->getGLLevel(mip);
1239
1240        if (mViewFromBaseToMaxLevel->textureType() != MTLTextureTypeCube &&
1241            mTexImageDefs[0][mipGLLevel].image)
1242        {
1243            // Reuse texture image view.
1244            mLevelViewsWithinBaseMax[mip] = mTexImageDefs[0][mipGLLevel].image;
1245        }
1246        else
1247        {
1248            mLevelViewsWithinBaseMax[mip] = mNativeTextureStorage->createMipView(mipGLLevel);
1249        }
1250    }
1251    return angle::Result::Continue;
1252}
1253
1254mtl::TextureRef TextureMtl::createImageViewFromTextureStorage(GLuint cubeFaceOrZero, GLuint glLevel)
1255{
1256    mtl::TextureRef image;
1257    if (mNativeTextureStorage->textureType() == MTLTextureTypeCube)
1258    {
1259        // Cube texture's image is per face.
1260        image = mNativeTextureStorage->createSliceMipView(cubeFaceOrZero, glLevel);
1261    }
1262    else
1263    {
1264        if (mViewFromBaseToMaxLevel->isGLLevelSupported(glLevel))
1265        {
1266            mtl::MipmapNativeLevel nativeLevel = mViewFromBaseToMaxLevel->getNativeLevel(glLevel);
1267            if (mLevelViewsWithinBaseMax[nativeLevel])
1268            {
1269                // Reuse the native level view
1270                image = mLevelViewsWithinBaseMax[nativeLevel];
1271            }
1272        }
1273
1274        if (!image)
1275        {
1276            image = mNativeTextureStorage->createMipView(glLevel);
1277        }
1278    }
1279
1280    return image;
1281}
1282
1283void TextureMtl::retainImageDefinitions()
1284{
1285    if (!mNativeTextureStorage)
1286    {
1287        return;
1288    }
1289    const GLuint mips = mNativeTextureStorage->mipmapLevels();
1290
1291    int numCubeFaces = 1;
1292    switch (mState.getType())
1293    {
1294        case gl::TextureType::CubeMap:
1295            numCubeFaces = 6;
1296            break;
1297        default:
1298            break;
1299    }
1300
1301    // Create image view per cube face, per mip level
1302    for (int face = 0; face < numCubeFaces; ++face)
1303    {
1304        for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel; mip.get() < mips; ++mip)
1305        {
1306            GLuint imageMipLevel         = mNativeTextureStorage->getGLLevel(mip);
1307            ImageDefinitionMtl &imageDef = mTexImageDefs[face][imageMipLevel];
1308            if (imageDef.image)
1309            {
1310                continue;
1311            }
1312            imageDef.image    = createImageViewFromTextureStorage(face, imageMipLevel);
1313            imageDef.formatID = mFormat.intendedFormatId;
1314        }
1315    }
1316}
1317
1318mtl::TextureRef &TextureMtl::getImage(const gl::ImageIndex &imageIndex)
1319{
1320    return getImageDefinition(imageIndex).image;
1321}
1322
1323ImageDefinitionMtl &TextureMtl::getImageDefinition(const gl::ImageIndex &imageIndex)
1324{
1325    GLuint cubeFaceOrZero        = GetImageCubeFaceIndexOrZeroFrom(imageIndex);
1326    ImageDefinitionMtl &imageDef = mTexImageDefs[cubeFaceOrZero][imageIndex.getLevelIndex()];
1327
1328    if (!imageDef.image && mNativeTextureStorage)
1329    {
1330        // If native texture is already created, and the image at this index is not available,
1331        // then create a view of native texture at this index, so that modifications of the image
1332        // are reflected back to native texture's respective index.
1333        if (!mNativeTextureStorage->isGLLevelSupported(imageIndex.getLevelIndex()))
1334        {
1335            // Image outside native texture's mip levels is skipped.
1336            return imageDef;
1337        }
1338
1339        imageDef.image =
1340            createImageViewFromTextureStorage(cubeFaceOrZero, imageIndex.getLevelIndex());
1341        imageDef.formatID = mFormat.intendedFormatId;
1342    }
1343
1344    return imageDef;
1345}
1346angle::Result TextureMtl::getRenderTarget(ContextMtl *context,
1347                                          const gl::ImageIndex &imageIndex,
1348                                          GLsizei implicitSamples,
1349                                          RenderTargetMtl **renderTargetOut)
1350{
1351    ASSERT(imageIndex.getType() == gl::TextureType::_2D ||
1352           imageIndex.getType() == gl::TextureType::Rectangle ||
1353           imageIndex.getType() == gl::TextureType::_2DMultisample || imageIndex.hasLayer());
1354
1355    const gl::RenderToTextureImageIndex renderToTextureIndex =
1356        implicitSamples <= 1
1357            ? gl::RenderToTextureImageIndex::Default
1358            : static_cast<gl::RenderToTextureImageIndex>(PackSampleCount(implicitSamples));
1359
1360    GLuint layer         = GetImageLayerIndexFrom(imageIndex);
1361    RenderTargetMtl &rtt = mRenderTargets[imageIndex][renderToTextureIndex];
1362    if (!rtt.getTexture())
1363    {
1364        // Lazy initialization of render target:
1365        mtl::TextureRef &image = getImage(imageIndex);
1366        if (image)
1367        {
1368            if (imageIndex.getType() == gl::TextureType::CubeMap)
1369            {
1370                // Cube map is special, the image is already the view of its layer
1371                rtt.set(image, mtl::kZeroNativeMipLevel, 0, mFormat);
1372            }
1373            else
1374            {
1375                rtt.set(image, mtl::kZeroNativeMipLevel, layer, mFormat);
1376            }
1377        }
1378    }
1379
1380    if (implicitSamples > 1 && !rtt.getImplicitMSTexture())
1381    {
1382        // This format must supports implicit resolve
1383        ANGLE_CHECK(context, mFormat.getCaps().resolve, gl::err::kInternalError, GL_INVALID_VALUE);
1384        mtl::TextureRef &msTexture = mImplicitMSTextures[imageIndex][renderToTextureIndex];
1385        if (!msTexture)
1386        {
1387            const gl::ImageDesc &desc = mState.getImageDesc(imageIndex);
1388            ANGLE_TRY(mtl::Texture::MakeMemoryLess2DMSTexture(
1389                context, mFormat, desc.size.width, desc.size.height, implicitSamples, &msTexture));
1390        }
1391        rtt.setImplicitMSTexture(msTexture);
1392    }
1393
1394    *renderTargetOut = &rtt;
1395
1396    return angle::Result::Continue;
1397}
1398
1399angle::Result TextureMtl::setImage(const gl::Context *context,
1400                                   const gl::ImageIndex &index,
1401                                   GLenum internalFormat,
1402                                   const gl::Extents &size,
1403                                   GLenum format,
1404                                   GLenum type,
1405                                   const gl::PixelUnpackState &unpack,
1406                                   gl::Buffer *unpackBuffer,
1407                                   const uint8_t *pixels)
1408{
1409    const gl::InternalFormat &dstFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
1410
1411    return setImageImpl(context, index, dstFormatInfo, size, format, type, unpack, unpackBuffer,
1412                        pixels);
1413}
1414
1415angle::Result TextureMtl::setSubImage(const gl::Context *context,
1416                                      const gl::ImageIndex &index,
1417                                      const gl::Box &area,
1418                                      GLenum format,
1419                                      GLenum type,
1420                                      const gl::PixelUnpackState &unpack,
1421                                      gl::Buffer *unpackBuffer,
1422                                      const uint8_t *pixels)
1423{
1424    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
1425
1426    return setSubImageImpl(context, index, area, formatInfo, type, unpack, unpackBuffer, pixels);
1427}
1428
1429angle::Result TextureMtl::setCompressedImage(const gl::Context *context,
1430                                             const gl::ImageIndex &index,
1431                                             GLenum internalFormat,
1432                                             const gl::Extents &size,
1433                                             const gl::PixelUnpackState &unpack,
1434                                             size_t imageSize,
1435                                             const uint8_t *pixels)
1436{
1437    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1438    const gl::State &glState             = context->getState();
1439    gl::Buffer *unpackBuffer             = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
1440
1441    return setImageImpl(context, index, formatInfo, size, internalFormat, GL_UNSIGNED_BYTE, unpack,
1442                        unpackBuffer, pixels);
1443}
1444
1445angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context,
1446                                                const gl::ImageIndex &index,
1447                                                const gl::Box &area,
1448                                                GLenum format,
1449                                                const gl::PixelUnpackState &unpack,
1450                                                size_t imageSize,
1451                                                const uint8_t *pixels)
1452{
1453
1454    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE);
1455
1456    const gl::State &glState = context->getState();
1457    gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
1458
1459    return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, unpackBuffer,
1460                           pixels);
1461}
1462
1463angle::Result TextureMtl::copyImage(const gl::Context *context,
1464                                    const gl::ImageIndex &index,
1465                                    const gl::Rectangle &sourceArea,
1466                                    GLenum internalFormat,
1467                                    gl::Framebuffer *source)
1468{
1469    gl::Extents newImageSize(sourceArea.width, sourceArea.height, 1);
1470    const gl::InternalFormat &internalFormatInfo =
1471        gl::GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE);
1472
1473    ContextMtl *contextMtl = mtl::GetImpl(context);
1474    angle::FormatID angleFormatId =
1475        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
1476    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1477
1478    FramebufferMtl *srcFramebufferMtl = mtl::GetImpl(source);
1479    RenderTargetMtl *srcReadRT        = srcFramebufferMtl->getColorReadRenderTarget(context);
1480    RenderTargetMtl colorReadRT;
1481    if (srcReadRT)
1482    {
1483        // Need to duplicate RenderTargetMtl since the srcReadRT would be invalidated in
1484        // redefineImage(). This can happen if the source and this texture are the same texture.
1485        // Duplication ensures the copyImage() will be able to proceed even if the source texture
1486        // will be redefined.
1487        colorReadRT.duplicateFrom(*srcReadRT);
1488    }
1489
1490    ANGLE_TRY(redefineImage(context, index, mtlFormat, newImageSize));
1491
1492    gl::Extents fbSize = source->getReadColorAttachment()->getSize();
1493    gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
1494    if (context->isWebGL() && !fbRect.encloses(sourceArea))
1495    {
1496        ANGLE_TRY(initializeContents(context, GL_NONE, index));
1497    }
1498
1499    return copySubImageImpl(context, index, gl::Offset(0, 0, 0), sourceArea, internalFormatInfo,
1500                            srcFramebufferMtl, &colorReadRT);
1501}
1502
1503angle::Result TextureMtl::copySubImage(const gl::Context *context,
1504                                       const gl::ImageIndex &index,
1505                                       const gl::Offset &destOffset,
1506                                       const gl::Rectangle &sourceArea,
1507                                       gl::Framebuffer *source)
1508{
1509    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
1510    FramebufferMtl *srcFramebufferMtl       = mtl::GetImpl(source);
1511    RenderTargetMtl *colorReadRT            = srcFramebufferMtl->getColorReadRenderTarget(context);
1512    return copySubImageImpl(context, index, destOffset, sourceArea, currentFormat,
1513                            srcFramebufferMtl, colorReadRT);
1514}
1515
1516angle::Result TextureMtl::copyTexture(const gl::Context *context,
1517                                      const gl::ImageIndex &index,
1518                                      GLenum internalFormat,
1519                                      GLenum type,
1520                                      GLint sourceLevel,
1521                                      bool unpackFlipY,
1522                                      bool unpackPremultiplyAlpha,
1523                                      bool unpackUnmultiplyAlpha,
1524                                      const gl::Texture *source)
1525{
1526    const gl::ImageDesc &sourceImageDesc = source->getTextureState().getImageDesc(
1527        NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
1528    const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
1529
1530    // Only 2D textures are supported.
1531    ASSERT(sourceImageDesc.size.depth == 1);
1532
1533    ContextMtl *contextMtl = mtl::GetImpl(context);
1534    angle::FormatID angleFormatId =
1535        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
1536    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1537
1538    ANGLE_TRY(redefineImage(context, index, mtlFormat, sourceImageDesc.size));
1539
1540    return copySubTextureImpl(
1541        context, index, gl::Offset(0, 0, 0), internalFormatInfo, sourceLevel,
1542        gl::Box(0, 0, 0, sourceImageDesc.size.width, sourceImageDesc.size.height, 1), unpackFlipY,
1543        unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
1544}
1545
1546angle::Result TextureMtl::copySubTexture(const gl::Context *context,
1547                                         const gl::ImageIndex &index,
1548                                         const gl::Offset &destOffset,
1549                                         GLint sourceLevel,
1550                                         const gl::Box &sourceBox,
1551                                         bool unpackFlipY,
1552                                         bool unpackPremultiplyAlpha,
1553                                         bool unpackUnmultiplyAlpha,
1554                                         const gl::Texture *source)
1555{
1556    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
1557
1558    return copySubTextureImpl(context, index, destOffset, currentFormat, sourceLevel, sourceBox,
1559                              unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
1560}
1561
1562angle::Result TextureMtl::copyCompressedTexture(const gl::Context *context,
1563                                                const gl::Texture *source)
1564{
1565    UNIMPLEMENTED();
1566
1567    return angle::Result::Stop;
1568}
1569
1570angle::Result TextureMtl::setStorage(const gl::Context *context,
1571                                     gl::TextureType type,
1572                                     size_t mipmaps,
1573                                     GLenum internalFormat,
1574                                     const gl::Extents &size)
1575{
1576    ContextMtl *contextMtl               = mtl::GetImpl(context);
1577    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1578    angle::FormatID angleFormatId =
1579        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
1580    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1581
1582    return setStorageImpl(context, type, mState.getImmutableLevels(), 0, mtlFormat, size);
1583}
1584
1585angle::Result TextureMtl::setStorageExternalMemory(const gl::Context *context,
1586                                                   gl::TextureType type,
1587                                                   size_t levels,
1588                                                   GLenum internalFormat,
1589                                                   const gl::Extents &size,
1590                                                   gl::MemoryObject *memoryObject,
1591                                                   GLuint64 offset,
1592                                                   GLbitfield createFlags,
1593                                                   GLbitfield usageFlags,
1594                                                   const void *imageCreateInfoPNext)
1595{
1596    UNIMPLEMENTED();
1597
1598    return angle::Result::Stop;
1599}
1600
1601angle::Result TextureMtl::setStorageMultisample(const gl::Context *context,
1602                                                gl::TextureType type,
1603                                                GLsizei samples,
1604                                                GLint internalFormat,
1605                                                const gl::Extents &size,
1606                                                bool fixedSampleLocations)
1607{
1608    ContextMtl *contextMtl               = mtl::GetImpl(context);
1609    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1610    angle::FormatID angleFormatId =
1611        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
1612    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1613
1614    return setStorageImpl(context, type, 0, mState.getLevelZeroDesc().samples, mtlFormat, size);
1615}
1616
1617angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context,
1618                                            gl::TextureType type,
1619                                            egl::Image *image)
1620{
1621    deallocateNativeStorage(/*keepImages=*/false);
1622
1623    ContextMtl *contextMtl = mtl::GetImpl(context);
1624
1625    ImageMtl *imageMtl = mtl::GetImpl(image);
1626    if (type != imageMtl->getImageTextureType())
1627    {
1628        return angle::Result::Stop;
1629    }
1630
1631    mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1632        imageMtl->getTexture(), /*baseGLLevel=*/0);
1633
1634    const angle::FormatID angleFormatId =
1635        angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat);
1636    mFormat = contextMtl->getPixelFormat(angleFormatId);
1637
1638    mSlices = mNativeTextureStorage->cubeFacesOrArrayLength();
1639
1640    ANGLE_TRY(ensureSamplerStateCreated(context));
1641    ANGLE_TRY(createViewFromBaseToMaxLevel());
1642
1643    // Tell context to rebind textures
1644    contextMtl->invalidateCurrentTextures();
1645
1646    return angle::Result::Continue;
1647}
1648
1649angle::Result TextureMtl::setImageExternal(const gl::Context *context,
1650                                           gl::TextureType type,
1651                                           egl::Stream *stream,
1652                                           const egl::Stream::GLTextureDescription &desc)
1653{
1654    UNIMPLEMENTED();
1655    return angle::Result::Stop;
1656}
1657
1658angle::Result TextureMtl::generateMipmap(const gl::Context *context)
1659{
1660    ANGLE_TRY(ensureNativeStorageCreated(context));
1661
1662    ContextMtl *contextMtl = mtl::GetImpl(context);
1663    if (!mViewFromBaseToMaxLevel)
1664    {
1665        return angle::Result::Continue;
1666    }
1667
1668    const mtl::FormatCaps &caps = mFormat.getCaps();
1669    //
1670    bool sRGB = mFormat.actualInternalFormat().colorEncoding == GL_SRGB;
1671
1672    bool avoidGPUPath =
1673        contextMtl->getDisplay()->getFeatures().forceNonCSBaseMipmapGeneration.enabled &&
1674        mViewFromBaseToMaxLevel->widthAt0() < 5;
1675
1676    if (!avoidGPUPath && caps.writable && mState.getType() == gl::TextureType::_3D)
1677    {
1678        // http://anglebug.com/42263496.
1679        // Use compute for 3D mipmap generation.
1680        ANGLE_TRY(ensureLevelViewsWithinBaseMaxCreated());
1681        ANGLE_TRY(contextMtl->getDisplay()->getUtils().generateMipmapCS(
1682            contextMtl, *mViewFromBaseToMaxLevel, sRGB, &mLevelViewsWithinBaseMax));
1683    }
1684    else if (!avoidGPUPath && caps.filterable && caps.colorRenderable)
1685    {
1686        mtl::BlitCommandEncoder *blitEncoder = GetBlitCommandEncoderForResources(
1687            contextMtl, {mViewFromBaseToMaxLevel->getNativeTexture().get()});
1688        blitEncoder->generateMipmapsForTexture(*mViewFromBaseToMaxLevel);
1689    }
1690    else
1691    {
1692        ANGLE_TRY(generateMipmapCPU(context));
1693    }
1694
1695    return angle::Result::Continue;
1696}
1697
1698angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
1699{
1700    ASSERT(mViewFromBaseToMaxLevel);
1701
1702    ContextMtl *contextMtl           = mtl::GetImpl(context);
1703    const angle::Format &angleFormat = mFormat.actualAngleFormat();
1704    // This format must have mip generation function.
1705    ANGLE_CHECK(contextMtl, angleFormat.mipGenerationFunction, gl::err::kInternalError,
1706                GL_INVALID_OPERATION);
1707
1708    for (uint32_t slice = 0; slice < mSlices; ++slice)
1709    {
1710        GLuint baseGLLevel = mViewFromBaseToMaxLevel->getBaseGLLevel();
1711
1712        uint32_t prevLevelWidth    = mViewFromBaseToMaxLevel->widthAt0();
1713        uint32_t prevLevelHeight   = mViewFromBaseToMaxLevel->heightAt0();
1714        uint32_t prevLevelDepth    = mViewFromBaseToMaxLevel->depthAt0();
1715        size_t prevLevelRowPitch   = angleFormat.pixelBytes * prevLevelWidth;
1716        size_t prevLevelDepthPitch = prevLevelRowPitch * prevLevelHeight;
1717        std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow)
1718                                                     uint8_t[prevLevelDepthPitch * prevLevelDepth]);
1719        ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData);
1720        std::unique_ptr<uint8_t[]> dstLevelData;
1721
1722        // Download base level data
1723        mViewFromBaseToMaxLevel->getBytes(
1724            contextMtl, prevLevelRowPitch, prevLevelDepthPitch,
1725            MTLRegionMake3D(0, 0, 0, prevLevelWidth, prevLevelHeight, prevLevelDepth), baseGLLevel,
1726            slice, prevLevelData.get());
1727
1728        for (GLuint mip = 1; mip < mViewFromBaseToMaxLevel->mipmapLevels(); ++mip)
1729        {
1730            GLuint glLevel     = baseGLLevel + mip;
1731            uint32_t dstWidth  = mViewFromBaseToMaxLevel->width(glLevel);
1732            uint32_t dstHeight = mViewFromBaseToMaxLevel->height(glLevel);
1733            uint32_t dstDepth  = mViewFromBaseToMaxLevel->depth(glLevel);
1734
1735            size_t dstRowPitch   = angleFormat.pixelBytes * dstWidth;
1736            size_t dstDepthPitch = dstRowPitch * dstHeight;
1737            size_t dstDataSize   = dstDepthPitch * dstDepth;
1738            if (!dstLevelData)
1739            {
1740                // Allocate once and reuse the buffer
1741                dstLevelData.reset(new (std::nothrow) uint8_t[dstDataSize]);
1742                ANGLE_CHECK_GL_ALLOC(contextMtl, dstLevelData);
1743            }
1744
1745            // Generate mip level
1746            angleFormat.mipGenerationFunction(
1747                prevLevelWidth, prevLevelHeight, 1, prevLevelData.get(), prevLevelRowPitch,
1748                prevLevelDepthPitch, dstLevelData.get(), dstRowPitch, dstDepthPitch);
1749
1750            mtl::MipmapNativeLevel nativeLevel = mViewFromBaseToMaxLevel->getNativeLevel(glLevel);
1751
1752            // Upload to texture
1753            ANGLE_TRY(UploadTextureContents(context, angleFormat,
1754                                            MTLRegionMake3D(0, 0, 0, dstWidth, dstHeight, dstDepth),
1755                                            nativeLevel, slice, dstLevelData.get(), dstRowPitch,
1756                                            dstDepthPitch, false, *mViewFromBaseToMaxLevel));
1757
1758            prevLevelWidth      = dstWidth;
1759            prevLevelHeight     = dstHeight;
1760            prevLevelDepth      = dstDepth;
1761            prevLevelRowPitch   = dstRowPitch;
1762            prevLevelDepthPitch = dstDepthPitch;
1763            std::swap(prevLevelData, dstLevelData);
1764        }  // for mip level
1765
1766    }  // For layers
1767
1768    return angle::Result::Continue;
1769}
1770
1771bool TextureMtl::needsFormatViewForPixelLocalStorage(
1772    const ShPixelLocalStorageOptions &plsOptions) const
1773{
1774    // On iOS devices with GPU family 5 and later, Metal doesn't apply lossless compression to
1775    // the texture if we set MTLTextureUsagePixelFormatView. This shouldn't be a problem though
1776    // because iOS devices implement pixel local storage with framebuffer fetch instead of shader
1777    // images.
1778    if (plsOptions.type == ShPixelLocalStorageType::ImageLoadStore)
1779    {
1780        switch (mFormat.metalFormat)
1781        {
1782            case MTLPixelFormatRGBA8Unorm:
1783            case MTLPixelFormatRGBA8Uint:
1784            case MTLPixelFormatRGBA8Sint:
1785                return !plsOptions.supportsNativeRGBA8ImageFormats;
1786            default:
1787                break;
1788        }
1789    }
1790    return false;
1791}
1792
1793bool TextureMtl::isImmutableOrPBuffer() const
1794{
1795    return mState.getImmutableFormat() || mBoundSurface;
1796}
1797
1798angle::Result TextureMtl::setBaseLevel(const gl::Context *context, GLuint baseLevel)
1799{
1800    return onBaseMaxLevelsChanged(context);
1801}
1802
1803angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface *surface)
1804{
1805    deallocateNativeStorage(/*keepImages=*/false);
1806
1807    mBoundSurface         = surface;
1808    auto pBuffer          = GetImplAs<OffscreenSurfaceMtl>(surface);
1809    mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1810        pBuffer->getColorTexture(), /*baseGLLevel=*/0);
1811    mFormat = pBuffer->getColorFormat();
1812    mSlices = mNativeTextureStorage->cubeFacesOrArrayLength();
1813
1814    ANGLE_TRY(ensureSamplerStateCreated(context));
1815    ANGLE_TRY(createViewFromBaseToMaxLevel());
1816
1817    // Tell context to rebind textures
1818    ContextMtl *contextMtl = mtl::GetImpl(context);
1819    contextMtl->invalidateCurrentTextures();
1820
1821    return angle::Result::Continue;
1822}
1823
1824angle::Result TextureMtl::releaseTexImage(const gl::Context *context)
1825{
1826    deallocateNativeStorage(/*keepImages=*/false);
1827    mBoundSurface = nullptr;
1828    return angle::Result::Continue;
1829}
1830
1831angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context,
1832                                                    GLenum binding,
1833                                                    const gl::ImageIndex &imageIndex,
1834                                                    GLsizei samples,
1835                                                    FramebufferAttachmentRenderTarget **rtOut)
1836{
1837    ANGLE_TRY(ensureNativeStorageCreated(context));
1838
1839    ContextMtl *contextMtl = mtl::GetImpl(context);
1840    ANGLE_CHECK(contextMtl, mNativeTextureStorage, gl::err::kInternalError, GL_INVALID_OPERATION);
1841
1842    RenderTargetMtl *rtt;
1843    ANGLE_TRY(getRenderTarget(contextMtl, imageIndex, samples, &rtt));
1844
1845    *rtOut = rtt;
1846
1847    return angle::Result::Continue;
1848}
1849
1850angle::Result TextureMtl::syncState(const gl::Context *context,
1851                                    const gl::Texture::DirtyBits &dirtyBits,
1852                                    gl::Command source)
1853{
1854    ContextMtl *contextMtl = mtl::GetImpl(context);
1855    for (size_t dirtyBit : dirtyBits)
1856    {
1857        switch (dirtyBit)
1858        {
1859            case gl::Texture::DIRTY_BIT_COMPARE_MODE:
1860            case gl::Texture::DIRTY_BIT_COMPARE_FUNC:
1861                // Tell context to rebind textures so that ProgramMtl has a chance to verify
1862                // depth texture compare mode.
1863                contextMtl->invalidateCurrentTextures();
1864                // fall through
1865                OS_FALLTHROUGH;
1866            case gl::Texture::DIRTY_BIT_MIN_FILTER:
1867            case gl::Texture::DIRTY_BIT_MAG_FILTER:
1868            case gl::Texture::DIRTY_BIT_WRAP_S:
1869            case gl::Texture::DIRTY_BIT_WRAP_T:
1870            case gl::Texture::DIRTY_BIT_WRAP_R:
1871            case gl::Texture::DIRTY_BIT_MAX_ANISOTROPY:
1872            case gl::Texture::DIRTY_BIT_MIN_LOD:
1873            case gl::Texture::DIRTY_BIT_MAX_LOD:
1874            case gl::Texture::DIRTY_BIT_SRGB_DECODE:
1875            case gl::Texture::DIRTY_BIT_BORDER_COLOR:
1876                // Recreate sampler state
1877                mMetalSamplerState = nil;
1878                break;
1879            case gl::Texture::DIRTY_BIT_MAX_LEVEL:
1880            case gl::Texture::DIRTY_BIT_BASE_LEVEL:
1881                ANGLE_TRY(onBaseMaxLevelsChanged(context));
1882                break;
1883            case gl::Texture::DIRTY_BIT_SWIZZLE_RED:
1884            case gl::Texture::DIRTY_BIT_SWIZZLE_GREEN:
1885            case gl::Texture::DIRTY_BIT_SWIZZLE_BLUE:
1886            case gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA:
1887            case gl::Texture::DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE:
1888            {
1889                // Recreate swizzle/stencil view.
1890                mSwizzleStencilSamplingView = nullptr;
1891            }
1892            break;
1893            default:
1894                break;
1895        }
1896    }
1897
1898    ANGLE_TRY(ensureNativeStorageCreated(context));
1899    ANGLE_TRY(ensureSamplerStateCreated(context));
1900
1901    return angle::Result::Continue;
1902}
1903
1904angle::Result TextureMtl::bindToShader(const gl::Context *context,
1905                                       mtl::RenderCommandEncoder *cmdEncoder,
1906                                       gl::ShaderType shaderType,
1907                                       gl::Sampler *sampler,
1908                                       int textureSlotIndex,
1909                                       int samplerSlotIndex)
1910{
1911    ASSERT(mNativeTextureStorage);
1912    ASSERT(mViewFromBaseToMaxLevel);
1913
1914    float minLodClamp;
1915    float maxLodClamp;
1916    id<MTLSamplerState> samplerState;
1917
1918    if (!mSwizzleStencilSamplingView)
1919    {
1920        ContextMtl *contextMtl             = mtl::GetImpl(context);
1921        const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures();
1922
1923        // Sampling from unused channels of depth and stencil textures is undefined in Metal.
1924        // ANGLE relies on swizzled views to enforce values required by OpenGL ES specs. Some
1925        // drivers fail to sample from a swizzled view of a stencil texture so skip this step.
1926        const bool skipStencilSwizzle = mFormat.actualFormatId == angle::FormatID::S8_UINT &&
1927                                        features.avoidStencilTextureSwizzle.enabled;
1928        if (!skipStencilSwizzle &&
1929            (mState.getSwizzleState().swizzleRequired() ||
1930             mFormat.actualAngleFormat().hasDepthOrStencilBits() || mFormat.swizzled) &&
1931            features.hasTextureSwizzle.enabled)
1932        {
1933            const gl::InternalFormat &glInternalFormat = *mState.getBaseLevelDesc().format.info;
1934
1935            MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake(
1936                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1937                    context, mState.getSwizzleState().swizzleRed, mFormat, glInternalFormat)),
1938                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1939                    context, mState.getSwizzleState().swizzleGreen, mFormat, glInternalFormat)),
1940                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1941                    context, mState.getSwizzleState().swizzleBlue, mFormat, glInternalFormat)),
1942                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1943                    context, mState.getSwizzleState().swizzleAlpha, mFormat, glInternalFormat)));
1944
1945            MTLPixelFormat format = mViewFromBaseToMaxLevel->pixelFormat();
1946            if (mState.isStencilMode())
1947            {
1948                if (format == MTLPixelFormatDepth32Float_Stencil8)
1949                {
1950                    format = MTLPixelFormatX32_Stencil8;
1951                }
1952#    if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1953                else if (format == MTLPixelFormatDepth24Unorm_Stencil8)
1954                {
1955                    format = MTLPixelFormatX24_Stencil8;
1956                }
1957#    endif
1958            }
1959
1960            mSwizzleStencilSamplingView = mNativeTextureStorage->createMipsSwizzleView(
1961                mViewFromBaseToMaxLevel->getBaseGLLevel(), mViewFromBaseToMaxLevel->mipmapLevels(),
1962                format, swizzle);
1963        }
1964        else
1965        {
1966            mSwizzleStencilSamplingView = mState.isStencilMode()
1967                                              ? mViewFromBaseToMaxLevel->getStencilView()
1968                                              : mViewFromBaseToMaxLevel->getNativeTexture();
1969        }
1970    }
1971
1972    if (!sampler)
1973    {
1974        samplerState = mMetalSamplerState;
1975        minLodClamp  = mState.getSamplerState().getMinLod();
1976        maxLodClamp  = mState.getSamplerState().getMaxLod();
1977    }
1978    else
1979    {
1980        SamplerMtl *samplerMtl = mtl::GetImpl(sampler);
1981        samplerState           = samplerMtl->getSampler(mtl::GetImpl(context));
1982        minLodClamp            = sampler->getSamplerState().getMinLod();
1983        maxLodClamp            = sampler->getSamplerState().getMaxLod();
1984    }
1985
1986    minLodClamp = std::max(minLodClamp, 0.f);
1987
1988    cmdEncoder->setTexture(shaderType, mSwizzleStencilSamplingView, textureSlotIndex);
1989    cmdEncoder->setSamplerState(shaderType, samplerState, minLodClamp, maxLodClamp,
1990                                samplerSlotIndex);
1991
1992    return angle::Result::Continue;
1993}
1994
1995angle::Result TextureMtl::bindToShaderImage(const gl::Context *context,
1996                                            mtl::RenderCommandEncoder *cmdEncoder,
1997                                            gl::ShaderType shaderType,
1998                                            int textureSlotIndex,
1999                                            int level,
2000                                            int layer,
2001                                            GLenum format)
2002{
2003    ASSERT(mNativeTextureStorage);
2004    ASSERT(mState.getImmutableFormat());
2005    ASSERT(0 <= level && static_cast<uint32_t>(level) < mState.getImmutableLevels());
2006    ASSERT(0 <= layer && static_cast<uint32_t>(layer) < mSlices);
2007
2008    ContextMtl *contextMtl        = mtl::GetImpl(context);
2009    angle::FormatID angleFormatId = angle::Format::InternalFormatToID(format);
2010    mtl::Format imageAccessFormat = contextMtl->getPixelFormat(angleFormatId);
2011    LayerLevelTextureViewVector &textureViewVector =
2012        mShaderImageViews[imageAccessFormat.metalFormat];
2013    mtl::TextureRef &textureRef = GetLayerLevelTextureView(&textureViewVector, layer, level,
2014                                                           mSlices, mState.getImmutableLevels());
2015
2016    if (textureRef == nullptr)
2017    {
2018        textureRef = mNativeTextureStorage->createShaderImageView2D(level, layer,
2019                                                                    imageAccessFormat.metalFormat);
2020    }
2021
2022    cmdEncoder->setRWTexture(shaderType, textureRef, textureSlotIndex);
2023    return angle::Result::Continue;
2024}
2025
2026angle::Result TextureMtl::redefineImage(const gl::Context *context,
2027                                        const gl::ImageIndex &index,
2028                                        const mtl::Format &mtlFormat,
2029                                        const gl::Extents &size)
2030{
2031    bool imageWithinNativeStorageLevels = false;
2032    if (mNativeTextureStorage && mNativeTextureStorage->isGLLevelSupported(index.getLevelIndex()))
2033    {
2034        imageWithinNativeStorageLevels = true;
2035        GLuint glLevel                 = index.getLevelIndex();
2036        // Calculate the expected size for the index we are defining. If the size is different
2037        // from the given size, or the format is different, we are redefining the image so we
2038        // must release it.
2039        ASSERT(mNativeTextureStorage->textureType() == mtl::GetTextureType(index.getType()));
2040        if (mFormat != mtlFormat || size != mNativeTextureStorage->size(glLevel))
2041        {
2042            // Keep other images
2043            deallocateNativeStorage(/*keepImages=*/true);
2044        }
2045    }
2046
2047    // Early-out on empty textures, don't create a zero-sized storage.
2048    if (size.empty())
2049    {
2050        return angle::Result::Continue;
2051    }
2052
2053    ContextMtl *contextMtl = mtl::GetImpl(context);
2054    // Cache last defined image format:
2055    mFormat                      = mtlFormat;
2056    ImageDefinitionMtl &imageDef = getImageDefinition(index);
2057
2058    // If native texture still exists, it means the size hasn't been changed, no need to create new
2059    // image
2060    if (mNativeTextureStorage && imageDef.image && imageWithinNativeStorageLevels)
2061    {
2062        ASSERT(imageDef.image->textureType() ==
2063                   mtl::GetTextureType(GetTextureImageType(index.getType())) &&
2064               imageDef.formatID == mFormat.intendedFormatId && imageDef.image->sizeAt0() == size);
2065    }
2066    else
2067    {
2068        imageDef.formatID    = mtlFormat.intendedFormatId;
2069        bool allowFormatView = mFormat.hasDepthAndStencilBits() ||
2070                               needsFormatViewForPixelLocalStorage(
2071                                   contextMtl->getDisplay()->getNativePixelLocalStorageOptions());
2072        // Create image to hold texture's data at this level & slice:
2073        switch (index.getType())
2074        {
2075            case gl::TextureType::_2D:
2076            case gl::TextureType::CubeMap:
2077                ANGLE_TRY(mtl::Texture::Make2DTexture(
2078                    contextMtl, mtlFormat, size.width, size.height, 1,
2079                    /** renderTargetOnly */ false, allowFormatView, &imageDef.image));
2080                break;
2081            case gl::TextureType::_3D:
2082                ANGLE_TRY(mtl::Texture::Make3DTexture(
2083                    contextMtl, mtlFormat, size.width, size.height, size.depth, 1,
2084                    /** renderTargetOnly */ false, allowFormatView, &imageDef.image));
2085                break;
2086            case gl::TextureType::_2DArray:
2087                ANGLE_TRY(mtl::Texture::Make2DArrayTexture(
2088                    contextMtl, mtlFormat, size.width, size.height, 1, size.depth,
2089                    /** renderTargetOnly */ false, allowFormatView, &imageDef.image));
2090                break;
2091            default:
2092                UNREACHABLE();
2093        }
2094
2095        // Make sure emulated channels are properly initialized in this newly allocated texture.
2096        ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, imageDef.image));
2097    }
2098
2099    // Tell context to rebind textures
2100    contextMtl->invalidateCurrentTextures();
2101
2102    return angle::Result::Continue;
2103}
2104
2105angle::Result TextureMtl::setStorageImpl(const gl::Context *context,
2106                                         gl::TextureType type,
2107                                         GLuint mips,
2108                                         GLuint samples,
2109                                         const mtl::Format &mtlFormat,
2110                                         const gl::Extents &size)
2111{
2112    // Don't need to hold old images data.
2113    deallocateNativeStorage(/*keepImages=*/false);
2114
2115    ContextMtl *contextMtl = mtl::GetImpl(context);
2116
2117    // Tell context to rebind textures
2118    contextMtl->invalidateCurrentTextures();
2119
2120    mFormat = mtlFormat;
2121
2122    ANGLE_TRY(createNativeStorage(context, type, mips, samples, size));
2123    ANGLE_TRY(createViewFromBaseToMaxLevel());
2124
2125    return angle::Result::Continue;
2126}
2127
2128angle::Result TextureMtl::setImageImpl(const gl::Context *context,
2129                                       const gl::ImageIndex &index,
2130                                       const gl::InternalFormat &dstFormatInfo,
2131                                       const gl::Extents &size,
2132                                       GLenum srcFormat,
2133                                       GLenum srcType,
2134                                       const gl::PixelUnpackState &unpack,
2135                                       gl::Buffer *unpackBuffer,
2136                                       const uint8_t *pixels)
2137{
2138    ContextMtl *contextMtl = mtl::GetImpl(context);
2139    angle::FormatID angleFormatId =
2140        angle::Format::InternalFormatToID(dstFormatInfo.sizedInternalFormat);
2141    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
2142
2143    ANGLE_TRY(redefineImage(context, index, mtlFormat, size));
2144
2145    // Early-out on empty textures, don't create a zero-sized storage.
2146    if (size.empty())
2147    {
2148        return angle::Result::Continue;
2149    }
2150
2151    // Format of the supplied pixels.
2152    const gl::InternalFormat *srcFormatInfo;
2153    if (srcFormat != dstFormatInfo.format || srcType != dstFormatInfo.type)
2154    {
2155        srcFormatInfo = &gl::GetInternalFormatInfo(srcFormat, srcType);
2156    }
2157    else
2158    {
2159        srcFormatInfo = &dstFormatInfo;
2160    }
2161    return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth),
2162                           *srcFormatInfo, srcType, unpack, unpackBuffer, pixels);
2163}
2164
2165angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
2166                                          const gl::ImageIndex &index,
2167                                          const gl::Box &area,
2168                                          const gl::InternalFormat &formatInfo,
2169                                          GLenum type,
2170                                          const gl::PixelUnpackState &unpack,
2171                                          gl::Buffer *unpackBuffer,
2172                                          const uint8_t *oriPixels)
2173{
2174    if (!oriPixels && !unpackBuffer)
2175    {
2176        return angle::Result::Continue;
2177    }
2178
2179    ContextMtl *contextMtl = mtl::GetImpl(context);
2180
2181    ANGLE_TRY(ensureImageCreated(context, index));
2182    mtl::TextureRef &image = getImage(index);
2183
2184    GLuint sourceRowPitch   = 0;
2185    GLuint sourceDepthPitch = 0;
2186    GLuint sourceSkipBytes  = 0;
2187    ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment,
2188                                                               unpack.rowLength, &sourceRowPitch));
2189    ANGLE_CHECK_GL_MATH(
2190        contextMtl, formatInfo.computeDepthPitch(area.height, unpack.imageHeight, sourceRowPitch,
2191                                                 &sourceDepthPitch));
2192    ANGLE_CHECK_GL_MATH(contextMtl,
2193                        formatInfo.computeSkipBytes(type, sourceRowPitch, sourceDepthPitch, unpack,
2194                                                    index.usesTex3D(), &sourceSkipBytes));
2195
2196    // Get corresponding source data's ANGLE format
2197    angle::FormatID srcAngleFormatId;
2198    if (formatInfo.sizedInternalFormat == GL_DEPTH_COMPONENT24)
2199    {
2200        // GL_DEPTH_COMPONENT24 is special case, its supplied data is 32 bit depth.
2201        srcAngleFormatId = angle::FormatID::D32_UNORM;
2202    }
2203    else
2204    {
2205        srcAngleFormatId = angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
2206    }
2207    const angle::Format &srcAngleFormat = angle::Format::Get(srcAngleFormatId);
2208
2209    const uint8_t *usablePixels = oriPixels + sourceSkipBytes;
2210
2211    // Upload to texture
2212    if (index.getType() == gl::TextureType::_2DArray)
2213    {
2214        // OpenGL unifies texture array and texture 3d's box area by using z & depth as array start
2215        // index & length for texture array. However, Metal treats them differently. We need to
2216        // handle them in separate code.
2217        MTLRegion mtlRegion = MTLRegionMake3D(area.x, area.y, 0, area.width, area.height, 1);
2218
2219        for (int slice = 0; slice < area.depth; ++slice)
2220        {
2221            int sliceIndex           = slice + area.z;
2222            const uint8_t *srcPixels = usablePixels + slice * sourceDepthPitch;
2223            ANGLE_TRY(setPerSliceSubImage(context, sliceIndex, mtlRegion, formatInfo, type,
2224                                          srcAngleFormat, sourceRowPitch, sourceDepthPitch,
2225                                          unpackBuffer, srcPixels, image));
2226        }
2227    }
2228    else
2229    {
2230        MTLRegion mtlRegion =
2231            MTLRegionMake3D(area.x, area.y, area.z, area.width, area.height, area.depth);
2232
2233        ANGLE_TRY(setPerSliceSubImage(context, 0, mtlRegion, formatInfo, type, srcAngleFormat,
2234                                      sourceRowPitch, sourceDepthPitch, unpackBuffer, usablePixels,
2235                                      image));
2236    }
2237
2238    return angle::Result::Continue;
2239}
2240
2241angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context,
2242                                              int slice,
2243                                              const MTLRegion &mtlArea,
2244                                              const gl::InternalFormat &internalFormat,
2245                                              GLenum type,
2246                                              const angle::Format &pixelsAngleFormat,
2247                                              size_t pixelsRowPitch,
2248                                              size_t pixelsDepthPitch,
2249                                              gl::Buffer *unpackBuffer,
2250                                              const uint8_t *pixels,
2251                                              const mtl::TextureRef &image)
2252{
2253    // If source pixels are luminance or RGB8, we need to convert them to RGBA
2254
2255    if (mFormat.needConversion(pixelsAngleFormat.id))
2256    {
2257        return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
2258                                             pixelsAngleFormat, pixelsRowPitch, pixelsDepthPitch,
2259                                             unpackBuffer, pixels, image);
2260    }
2261
2262    // No conversion needed.
2263    ContextMtl *contextMtl = mtl::GetImpl(context);
2264
2265    if (unpackBuffer)
2266    {
2267        uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
2268        GLuint minRowPitch;
2269        ANGLE_CHECK_GL_MATH(contextMtl, internalFormat.computeRowPitch(
2270                                            type, static_cast<GLint>(mtlArea.size.width),
2271                                            /** aligment */ 1, /** rowLength */ 0, &minRowPitch));
2272        if (offset % mFormat.actualAngleFormat().pixelBytes || pixelsRowPitch < minRowPitch)
2273        {
2274            // offset is not divisible by pixelByte or the source row pitch is smaller than minimum
2275            // row pitch, use convertAndSetPerSliceSubImage() function.
2276            return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
2277                                                 pixelsAngleFormat, pixelsRowPitch,
2278                                                 pixelsDepthPitch, unpackBuffer, pixels, image);
2279        }
2280
2281        BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
2282
2283        if (mFormat.hasDepthAndStencilBits())
2284        {
2285            // NOTE(hqle): packed depth & stencil texture cannot copy from buffer directly, needs
2286            // to split its depth & stencil data and copy separately.
2287            const uint8_t *clientData = unpackBufferMtl->getBufferDataReadOnly(contextMtl);
2288            clientData += offset;
2289            ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea,
2290                                            mtl::kZeroNativeMipLevel, slice, clientData,
2291                                            pixelsRowPitch, pixelsDepthPitch, false, image));
2292        }
2293        else
2294        {
2295            mtl::BufferRef sourceBuffer = unpackBufferMtl->getCurrentBuffer();
2296            // PVRTC1 blocks are stored in a reflected Morton order
2297            // and need to be linearized for buffer uploads in Metal.
2298            // This step is skipped for textures that have only one block.
2299            if (mFormat.isPVRTC() && mtlArea.size.height > 4)
2300            {
2301                // PVRTC1 inherent requirement.
2302                ASSERT(gl::isPow2(mtlArea.size.width) && gl::isPow2(mtlArea.size.height));
2303                // Metal-specific limitation enforced by ANGLE validation.
2304                ASSERT(mtlArea.size.width == mtlArea.size.height);
2305                static_assert(gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE <= 262144,
2306                              "The current kernel can handle up to 65536 blocks per dimension.");
2307
2308                // Current command buffer implementation does not support 64-bit offsets.
2309                ANGLE_CHECK_GL_MATH(contextMtl, offset <= std::numeric_limits<uint32_t>::max());
2310
2311                mtl::BufferRef stagingBuffer;
2312                ANGLE_TRY(
2313                    mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer));
2314
2315                mtl::BlockLinearizationParams params;
2316                params.srcBuffer       = sourceBuffer;
2317                params.dstBuffer       = stagingBuffer;
2318                params.srcBufferOffset = static_cast<uint32_t>(offset);
2319                params.blocksWide =
2320                    static_cast<GLuint>(mtlArea.size.width) / internalFormat.compressedBlockWidth;
2321                params.blocksHigh =
2322                    static_cast<GLuint>(mtlArea.size.height) / internalFormat.compressedBlockHeight;
2323
2324                // PVRTC1 textures always have at least 2 blocks in each dimension.
2325                // Enforce correct block layout for 8x8 textures that use 8x4 blocks.
2326                params.blocksWide = std::max(params.blocksWide, 2u);
2327
2328                ANGLE_TRY(contextMtl->getDisplay()->getUtils().linearizeBlocks(contextMtl, params));
2329
2330                sourceBuffer = stagingBuffer;
2331                offset       = 0;
2332            }
2333            else if (pixelsAngleFormat.id == angle::FormatID::D32_FLOAT)
2334            {
2335                // Current command buffer implementation does not support 64-bit offsets.
2336                ANGLE_CHECK_GL_MATH(contextMtl, offset <= std::numeric_limits<uint32_t>::max());
2337                mtl::BufferRef stagingBuffer;
2338                ANGLE_TRY(
2339                    mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer));
2340
2341                ASSERT(pixelsAngleFormat.pixelBytes == 4 && offset % 4 == 0);
2342                ANGLE_TRY(SaturateDepth(contextMtl, sourceBuffer, stagingBuffer,
2343                                        static_cast<uint32_t>(offset),
2344                                        static_cast<uint32_t>(pixelsRowPitch) / 4, mtlArea.size));
2345
2346                sourceBuffer = stagingBuffer;
2347                offset       = 0;
2348            }
2349
2350            // Use blit encoder to copy
2351            mtl::BlitCommandEncoder *blitEncoder =
2352                GetBlitCommandEncoderForResources(contextMtl, {sourceBuffer.get(), image.get()});
2353            CopyBufferToOriginalTextureIfDstIsAView(
2354                contextMtl, blitEncoder, sourceBuffer, offset, pixelsRowPitch, pixelsDepthPitch,
2355                mtlArea.size, image, slice, mtl::kZeroNativeMipLevel, mtlArea.origin,
2356                mFormat.isPVRTC() ? MTLBlitOptionRowLinearPVRTC : MTLBlitOptionNone);
2357        }
2358    }
2359    else
2360    {
2361        ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea,
2362                                        mtl::kZeroNativeMipLevel, slice, pixels, pixelsRowPitch,
2363                                        pixelsDepthPitch, false, image));
2364    }
2365    return angle::Result::Continue;
2366}
2367
2368angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *context,
2369                                                        int slice,
2370                                                        const MTLRegion &mtlArea,
2371                                                        const gl::InternalFormat &internalFormat,
2372                                                        GLenum type,
2373                                                        const angle::Format &pixelsAngleFormat,
2374                                                        size_t pixelsRowPitch,
2375                                                        size_t pixelsDepthPitch,
2376                                                        gl::Buffer *unpackBuffer,
2377                                                        const uint8_t *pixels,
2378                                                        const mtl::TextureRef &image)
2379{
2380    ASSERT(image && image->valid());
2381
2382    ContextMtl *contextMtl = mtl::GetImpl(context);
2383
2384    if (unpackBuffer)
2385    {
2386        ANGLE_CHECK_GL_MATH(contextMtl, reinterpret_cast<uintptr_t>(pixels) <=
2387                                            std::numeric_limits<uint32_t>::max());
2388
2389        uint32_t offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pixels));
2390
2391        BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
2392        if (!mFormat.getCaps().writable || mFormat.hasDepthOrStencilBits() ||
2393            mFormat.intendedAngleFormat().isBlock)
2394        {
2395            // Unsupported format, use CPU path.
2396            const uint8_t *clientData = unpackBufferMtl->getBufferDataReadOnly(contextMtl);
2397            clientData += offset;
2398            ANGLE_TRY(convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
2399                                                    pixelsAngleFormat, pixelsRowPitch,
2400                                                    pixelsDepthPitch, nullptr, clientData, image));
2401        }
2402        else
2403        {
2404            // Use compute shader
2405            mtl::CopyPixelsFromBufferParams params;
2406            params.buffer            = unpackBufferMtl->getCurrentBuffer();
2407            params.bufferStartOffset = offset;
2408            params.bufferRowPitch    = static_cast<uint32_t>(pixelsRowPitch);
2409            params.bufferDepthPitch  = static_cast<uint32_t>(pixelsDepthPitch);
2410            params.texture           = image;
2411            params.textureArea       = mtl::MTLRegionToGLBox(mtlArea);
2412
2413            // If texture is not array, slice must be zero, if texture is array, mtlArea.origin.z
2414            // must be zero.
2415            // This is because this function uses Metal convention: where slice is only used for
2416            // array textures, and z layer of mtlArea.origin is only used for 3D textures.
2417            ASSERT(slice == 0 || params.textureArea.z == 0);
2418
2419            // For mtl::RenderUtils we convert to OpenGL convention: z layer is used as either array
2420            // texture's slice or 3D texture's layer index.
2421            params.textureArea.z += slice;
2422
2423            ANGLE_TRY(contextMtl->getDisplay()->getUtils().unpackPixelsFromBufferToTexture(
2424                contextMtl, pixelsAngleFormat, params));
2425        }
2426    }  // if (unpackBuffer)
2427    else
2428    {
2429        LoadImageFunctionInfo loadFunctionInfo = mFormat.textureLoadFunctions
2430                                                     ? mFormat.textureLoadFunctions(type)
2431                                                     : LoadImageFunctionInfo();
2432        const angle::Format &dstFormat         = angle::Format::Get(mFormat.actualFormatId);
2433        const size_t dstRowPitch               = dstFormat.pixelBytes * mtlArea.size.width;
2434
2435        // It is very important to avoid allocating a new buffer for each row during these
2436        // uploads.
2437        const bool kAvoidStagingBuffers = true;
2438
2439        // Check if original image data is compressed:
2440        if (mFormat.intendedAngleFormat().isBlock)
2441        {
2442            if (mFormat.intendedFormatId != mFormat.actualFormatId)
2443            {
2444                ASSERT(loadFunctionInfo.loadFunction);
2445
2446                // Need to create a buffer to hold entire decompressed image.
2447                const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
2448                angle::MemoryBuffer decompressBuf;
2449                ANGLE_CHECK_GL_ALLOC(contextMtl,
2450                                     decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
2451
2452                // Decompress
2453                loadFunctionInfo.loadFunction(contextMtl->getImageLoadContext(), mtlArea.size.width,
2454                                              mtlArea.size.height, mtlArea.size.depth, pixels,
2455                                              pixelsRowPitch, pixelsDepthPitch,
2456                                              decompressBuf.data(), dstRowPitch, dstDepthPitch);
2457
2458                // Upload to texture
2459                ANGLE_TRY(UploadTextureContents(
2460                    context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice,
2461                    decompressBuf.data(), dstRowPitch, dstDepthPitch, kAvoidStagingBuffers, image));
2462            }
2463            else
2464            {
2465                // Assert that we're filling the level in it's entierety.
2466                ASSERT(mtlArea.size.width == static_cast<unsigned int>(image->sizeAt0().width));
2467                ASSERT(mtlArea.size.height == static_cast<unsigned int>(image->sizeAt0().height));
2468                const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
2469                ANGLE_TRY(UploadTextureContents(
2470                    context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice, pixels,
2471                    dstRowPitch, dstDepthPitch, kAvoidStagingBuffers, image));
2472            }
2473        }  // if (mFormat.intendedAngleFormat().isBlock)
2474        else
2475        {
2476            // Create scratch row buffer
2477            angle::MemoryBuffer conversionRow;
2478            ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
2479
2480            // Convert row by row:
2481            MTLRegion mtlRow   = mtlArea;
2482            mtlRow.size.height = mtlRow.size.depth = 1;
2483            for (NSUInteger d = 0; d < mtlArea.size.depth; ++d)
2484            {
2485                mtlRow.origin.z = mtlArea.origin.z + d;
2486                for (NSUInteger r = 0; r < mtlArea.size.height; ++r)
2487                {
2488                    const uint8_t *psrc = pixels + d * pixelsDepthPitch + r * pixelsRowPitch;
2489                    mtlRow.origin.y     = mtlArea.origin.y + r;
2490
2491                    // Convert pixels
2492                    if (loadFunctionInfo.loadFunction)
2493                    {
2494                        loadFunctionInfo.loadFunction(contextMtl->getImageLoadContext(),
2495                                                      mtlRow.size.width, 1, 1, psrc, pixelsRowPitch,
2496                                                      0, conversionRow.data(), dstRowPitch, 0);
2497                    }
2498                    else if (mFormat.hasDepthOrStencilBits())
2499                    {
2500                        ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0,
2501                                                psrc, dstFormat, nullptr, dstRowPitch, 0,
2502                                                conversionRow.data());
2503                    }
2504                    else
2505                    {
2506                        CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsAngleFormat.pixelBytes, 0,
2507                                          pixelsAngleFormat.pixelReadFunction, conversionRow.data(),
2508                                          dstRowPitch, dstFormat.pixelBytes, 0,
2509                                          dstFormat.pixelWriteFunction, internalFormat.format,
2510                                          dstFormat.componentType, mtlRow.size.width, 1, 1, false,
2511                                          false, false);
2512                    }
2513
2514                    // Upload to texture
2515                    ANGLE_TRY(UploadTextureContents(
2516                        context, dstFormat, mtlRow, mtl::kZeroNativeMipLevel, slice,
2517                        conversionRow.data(), dstRowPitch, 0, kAvoidStagingBuffers, image));
2518                }
2519            }
2520        }  // if (mFormat.intendedAngleFormat().isBlock)
2521    }      // if (unpackBuffer)
2522
2523    return angle::Result::Continue;
2524}
2525
2526angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
2527                                                   const mtl::Format &mtlFormat,
2528                                                   const mtl::TextureRef &texture)
2529{
2530    bool emulatedChannels = mtl::IsFormatEmulated(mtlFormat);
2531
2532    // For emulated channels that GL texture intends to not have,
2533    // we need to initialize their content.
2534    if (emulatedChannels)
2535    {
2536        uint32_t mipmaps = texture->mipmapLevels();
2537
2538        uint32_t layers = texture->cubeFacesOrArrayLength();
2539        for (uint32_t layer = 0; layer < layers; ++layer)
2540        {
2541            for (uint32_t mip = 0; mip < mipmaps; ++mip)
2542            {
2543                auto index = mtl::ImageNativeIndex::FromBaseZeroGLIndex(
2544                    GetCubeOrArraySliceMipIndex(texture, layer, mip));
2545
2546                ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mtlFormat, index));
2547            }
2548        }
2549    }
2550    return angle::Result::Continue;
2551}
2552
2553angle::Result TextureMtl::initializeContents(const gl::Context *context,
2554                                             GLenum binding,
2555                                             const gl::ImageIndex &index)
2556{
2557    if (index.isLayered())
2558    {
2559        // InitializeTextureContents is only able to initialize one layer at a time.
2560        const gl::ImageDesc &desc = mState.getImageDesc(index);
2561        uint32_t layerCount;
2562        if (index.isEntireLevelCubeMap())
2563        {
2564            layerCount = 6;
2565        }
2566        else
2567        {
2568            layerCount = desc.size.depth;
2569        }
2570
2571        gl::ImageIndexIterator ite = index.getLayerIterator(layerCount);
2572        while (ite.hasNext())
2573        {
2574            gl::ImageIndex layerIndex = ite.next();
2575            ANGLE_TRY(initializeContents(context, GL_NONE, layerIndex));
2576        }
2577        return angle::Result::Continue;
2578    }
2579    else if (index.getLayerCount() > 1)
2580    {
2581        for (int layer = 0; layer < index.getLayerCount(); ++layer)
2582        {
2583            int layerIdx = layer + index.getLayerIndex();
2584            gl::ImageIndex layerIndex =
2585                gl::ImageIndex::MakeFromType(index.getType(), index.getLevelIndex(), layerIdx);
2586            ANGLE_TRY(initializeContents(context, GL_NONE, layerIndex));
2587        }
2588        return angle::Result::Continue;
2589    }
2590
2591    ASSERT(index.getLayerCount() == 1 && !index.isLayered());
2592    ANGLE_TRY(ensureImageCreated(context, index));
2593    ContextMtl *contextMtl       = mtl::GetImpl(context);
2594    ImageDefinitionMtl &imageDef = getImageDefinition(index);
2595    const mtl::TextureRef &image = imageDef.image;
2596    const mtl::Format &format    = contextMtl->getPixelFormat(imageDef.formatID);
2597    // For Texture's image definition, we always use zero mip level.
2598    if (format.metalFormat == MTLPixelFormatInvalid)
2599    {
2600        return angle::Result::Stop;
2601    }
2602    return mtl::InitializeTextureContents(
2603        context, image, format,
2604        mtl::ImageNativeIndex::FromBaseZeroGLIndex(
2605            GetLayerMipIndex(image, GetImageLayerIndexFrom(index), /** level */ 0)));
2606}
2607
2608angle::Result TextureMtl::copySubImageImpl(const gl::Context *context,
2609                                           const gl::ImageIndex &index,
2610                                           const gl::Offset &destOffset,
2611                                           const gl::Rectangle &sourceArea,
2612                                           const gl::InternalFormat &internalFormat,
2613                                           const FramebufferMtl *source,
2614                                           const RenderTargetMtl *colorReadRT)
2615{
2616    if (!colorReadRT || !colorReadRT->getTexture())
2617    {
2618        // Is this an error?
2619        return angle::Result::Continue;
2620    }
2621
2622    gl::Extents fbSize = colorReadRT->getTexture()->size(colorReadRT->getLevelIndex());
2623    gl::Rectangle clippedSourceArea;
2624    if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height),
2625                       &clippedSourceArea))
2626    {
2627        return angle::Result::Continue;
2628    }
2629
2630    // If negative offsets are given, clippedSourceArea ensures we don't read from those offsets.
2631    // However, that changes the sourceOffset->destOffset mapping.  Here, destOffset is shifted by
2632    // the same amount as clipped to correct the error.
2633    const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x,
2634                                        destOffset.y + clippedSourceArea.y - sourceArea.y, 0);
2635
2636    ANGLE_TRY(ensureImageCreated(context, index));
2637
2638    if (!mFormat.getCaps().isRenderable())
2639    {
2640        return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea,
2641                               internalFormat, source, colorReadRT);
2642    }
2643
2644    // NOTE(hqle): Use compute shader.
2645    return copySubImageWithDraw(context, index, modifiedDestOffset, clippedSourceArea,
2646                                internalFormat, source, colorReadRT);
2647}
2648
2649angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
2650                                               const gl::ImageIndex &index,
2651                                               const gl::Offset &modifiedDestOffset,
2652                                               const gl::Rectangle &clippedSourceArea,
2653                                               const gl::InternalFormat &internalFormat,
2654                                               const FramebufferMtl *source,
2655                                               const RenderTargetMtl *colorReadRT)
2656{
2657    ContextMtl *contextMtl = mtl::GetImpl(context);
2658    DisplayMtl *displayMtl = contextMtl->getDisplay();
2659
2660    RenderTargetMtl *imageRtt;
2661    ANGLE_TRY(getRenderTarget(contextMtl, index, /*implicitSamples=*/0, &imageRtt));
2662
2663    mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(*imageRtt);
2664    mtl::ColorBlitParams blitParams;
2665
2666    blitParams.dstTextureSize = imageRtt->getTexture()->size(imageRtt->getLevelIndex());
2667    blitParams.dstRect        = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y,
2668                                              clippedSourceArea.width, clippedSourceArea.height);
2669    blitParams.dstScissorRect = blitParams.dstRect;
2670
2671    blitParams.enabledBuffers.set(0);
2672
2673    blitParams.src      = colorReadRT->getTexture();
2674    blitParams.srcLevel = colorReadRT->getLevelIndex();
2675    blitParams.srcLayer = colorReadRT->getLayerIndex();
2676
2677    blitParams.srcNormalizedCoords = mtl::NormalizedCoords(
2678        clippedSourceArea, colorReadRT->getTexture()->size(blitParams.srcLevel));
2679    blitParams.srcYFlipped  = source->flipY();
2680    blitParams.dstLuminance = internalFormat.isLUMA();
2681
2682    return displayMtl->getUtils().blitColorWithDraw(
2683        context, cmdEncoder, colorReadRT->getFormat().actualAngleFormat(), blitParams);
2684}
2685
2686angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
2687                                          const gl::ImageIndex &index,
2688                                          const gl::Offset &modifiedDestOffset,
2689                                          const gl::Rectangle &clippedSourceArea,
2690                                          const gl::InternalFormat &internalFormat,
2691                                          const FramebufferMtl *source,
2692                                          const RenderTargetMtl *colorReadRT)
2693{
2694    mtl::TextureRef &image = getImage(index);
2695    ASSERT(image && image->valid());
2696
2697    ContextMtl *contextMtl = mtl::GetImpl(context);
2698
2699    const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
2700    const int dstRowPitch          = dstFormat.pixelBytes * clippedSourceArea.width;
2701    angle::MemoryBuffer conversionRow;
2702    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
2703
2704    gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1);
2705    MTLRegion mtlDstRowArea  = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1);
2706    uint32_t dstSlice        = 0;
2707    switch (index.getType())
2708    {
2709        case gl::TextureType::_2D:
2710        case gl::TextureType::CubeMap:
2711            dstSlice = 0;
2712            break;
2713        case gl::TextureType::_2DArray:
2714            ASSERT(index.hasLayer());
2715            dstSlice = index.getLayerIndex();
2716            break;
2717        case gl::TextureType::_3D:
2718            ASSERT(index.hasLayer());
2719            dstSlice               = 0;
2720            mtlDstRowArea.origin.z = index.getLayerIndex();
2721            break;
2722        default:
2723            UNREACHABLE();
2724    }
2725
2726    // It is very important to avoid allocating a new buffer for each row during these
2727    // uploads.
2728    const bool kAvoidStagingBuffers = true;
2729
2730    // Copy row by row:
2731    for (int r = 0; r < clippedSourceArea.height; ++r)
2732    {
2733        mtlDstRowArea.origin.y = modifiedDestOffset.y + r;
2734        srcRowArea.y           = clippedSourceArea.y + r;
2735
2736        PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0);
2737
2738        // Read pixels from framebuffer to memory:
2739        gl::Rectangle flippedSrcRowArea = source->getCorrectFlippedReadArea(context, srcRowArea);
2740        ANGLE_TRY(source->readPixelsImpl(context, flippedSrcRowArea, packParams, colorReadRT,
2741                                         conversionRow.data()));
2742
2743        // Upload to texture
2744        ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlDstRowArea, mtl::kZeroNativeMipLevel,
2745                                        dstSlice, conversionRow.data(), dstRowPitch, 0,
2746                                        kAvoidStagingBuffers, image));
2747    }
2748
2749    return angle::Result::Continue;
2750}
2751
2752angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context,
2753                                             const gl::ImageIndex &index,
2754                                             const gl::Offset &destOffset,
2755                                             const gl::InternalFormat &internalFormat,
2756                                             GLint sourceLevel,
2757                                             const gl::Box &sourceBox,
2758                                             bool unpackFlipY,
2759                                             bool unpackPremultiplyAlpha,
2760                                             bool unpackUnmultiplyAlpha,
2761                                             const gl::Texture *source)
2762{
2763    // Only 2D textures are supported.
2764    ASSERT(sourceBox.depth == 1);
2765    ASSERT(source->getType() == gl::TextureType::_2D);
2766    gl::ImageIndex sourceIndex = gl::ImageIndex::Make2D(sourceLevel);
2767
2768    ContextMtl *contextMtl = mtl::GetImpl(context);
2769    TextureMtl *sourceMtl  = mtl::GetImpl(source);
2770
2771    ANGLE_TRY(ensureImageCreated(context, index));
2772
2773    ANGLE_TRY(sourceMtl->ensureImageCreated(context, sourceIndex));
2774
2775    const ImageDefinitionMtl &srcImageDef  = sourceMtl->getImageDefinition(sourceIndex);
2776    const mtl::TextureRef &sourceImage     = srcImageDef.image;
2777    const mtl::Format &sourceFormat        = contextMtl->getPixelFormat(srcImageDef.formatID);
2778    const angle::Format &sourceAngleFormat = sourceFormat.actualAngleFormat();
2779
2780    if (!mFormat.getCaps().isRenderable())
2781    {
2782        return copySubTextureCPU(context, index, destOffset, internalFormat,
2783                                 mtl::kZeroNativeMipLevel, sourceBox, sourceAngleFormat,
2784                                 unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
2785                                 sourceImage);
2786    }
2787    return copySubTextureWithDraw(
2788        context, index, destOffset, internalFormat, mtl::kZeroNativeMipLevel, sourceBox,
2789        sourceAngleFormat, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, sourceImage);
2790}
2791
2792angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
2793                                                 const gl::ImageIndex &index,
2794                                                 const gl::Offset &destOffset,
2795                                                 const gl::InternalFormat &internalFormat,
2796                                                 const mtl::MipmapNativeLevel &sourceNativeLevel,
2797                                                 const gl::Box &sourceBox,
2798                                                 const angle::Format &sourceAngleFormat,
2799                                                 bool unpackFlipY,
2800                                                 bool unpackPremultiplyAlpha,
2801                                                 bool unpackUnmultiplyAlpha,
2802                                                 const mtl::TextureRef &sourceTexture)
2803{
2804    ContextMtl *contextMtl = mtl::GetImpl(context);
2805    DisplayMtl *displayMtl = contextMtl->getDisplay();
2806
2807    mtl::TextureRef image = getImage(index);
2808    ASSERT(image && image->valid());
2809
2810    if (internalFormat.colorEncoding == GL_SRGB)
2811    {
2812        image = image->getLinearColorView();
2813    }
2814
2815    mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getTextureRenderCommandEncoder(
2816        image, mtl::ImageNativeIndex::FromBaseZeroGLIndex(GetZeroLevelIndex(image)));
2817    mtl::ColorBlitParams blitParams;
2818
2819    blitParams.dstTextureSize = image->sizeAt0();
2820    blitParams.dstRect =
2821        gl::Rectangle(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
2822    blitParams.dstScissorRect = blitParams.dstRect;
2823
2824    blitParams.enabledBuffers.set(0);
2825
2826    blitParams.src      = sourceTexture;
2827    blitParams.srcLevel = sourceNativeLevel;
2828    blitParams.srcLayer = 0;
2829    blitParams.srcNormalizedCoords =
2830        mtl::NormalizedCoords(sourceBox.toRect(), sourceTexture->size(sourceNativeLevel));
2831    blitParams.srcYFlipped            = false;
2832    blitParams.dstLuminance           = internalFormat.isLUMA();
2833    blitParams.unpackFlipY            = unpackFlipY;
2834    blitParams.unpackPremultiplyAlpha = unpackPremultiplyAlpha;
2835    blitParams.unpackUnmultiplyAlpha  = unpackUnmultiplyAlpha;
2836    blitParams.transformLinearToSrgb  = sourceAngleFormat.isSRGB;
2837
2838    return displayMtl->getUtils().copyTextureWithDraw(context, cmdEncoder, sourceAngleFormat,
2839                                                      mFormat.actualAngleFormat(), blitParams);
2840}
2841
2842angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
2843                                            const gl::ImageIndex &index,
2844                                            const gl::Offset &destOffset,
2845                                            const gl::InternalFormat &internalFormat,
2846                                            const mtl::MipmapNativeLevel &sourceNativeLevel,
2847                                            const gl::Box &sourceBox,
2848                                            const angle::Format &sourceAngleFormat,
2849                                            bool unpackFlipY,
2850                                            bool unpackPremultiplyAlpha,
2851                                            bool unpackUnmultiplyAlpha,
2852                                            const mtl::TextureRef &sourceTexture)
2853{
2854    mtl::TextureRef &image = getImage(index);
2855    ASSERT(image && image->valid());
2856
2857    ContextMtl *contextMtl = mtl::GetImpl(context);
2858
2859    const angle::Format &dstAngleFormat = mFormat.actualAngleFormat();
2860    const int srcRowPitch               = sourceAngleFormat.pixelBytes * sourceBox.width;
2861    const int srcImageSize              = srcRowPitch * sourceBox.height;
2862    const int convRowPitch              = dstAngleFormat.pixelBytes * sourceBox.width;
2863    const int convImageSize             = convRowPitch * sourceBox.height;
2864    angle::MemoryBuffer conversionSrc, conversionDst;
2865    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionSrc.resize(srcImageSize));
2866    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionDst.resize(convImageSize));
2867
2868    MTLRegion mtlSrcArea =
2869        MTLRegionMake2D(sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height);
2870    MTLRegion mtlDstArea =
2871        MTLRegionMake2D(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
2872
2873    // Read pixels from source to memory:
2874    sourceTexture->getBytes(contextMtl, srcRowPitch, 0, mtlSrcArea, sourceNativeLevel, 0,
2875                            conversionSrc.data());
2876
2877    // Convert to destination format
2878    CopyImageCHROMIUM(conversionSrc.data(), srcRowPitch, sourceAngleFormat.pixelBytes, 0,
2879                      sourceAngleFormat.pixelReadFunction, conversionDst.data(), convRowPitch,
2880                      dstAngleFormat.pixelBytes, 0, dstAngleFormat.pixelWriteFunction,
2881                      internalFormat.format, internalFormat.componentType, sourceBox.width,
2882                      sourceBox.height, 1, unpackFlipY, unpackPremultiplyAlpha,
2883                      unpackUnmultiplyAlpha);
2884
2885    // Upload to texture
2886    ANGLE_TRY(UploadTextureContents(context, dstAngleFormat, mtlDstArea, mtl::kZeroNativeMipLevel,
2887                                    0, conversionDst.data(), convRowPitch, 0, false, image));
2888
2889    return angle::Result::Continue;
2890}
2891
2892}  // namespace rx
2893