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