• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2//
3// Copyright 2019 The ANGLE Project Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7// TextureMtl.mm:
8//    Implements the class methods for TextureMtl.
9//
10
11#include "libANGLE/renderer/metal/TextureMtl.h"
12
13#include <algorithm>
14#include <initializer_list>
15
16#include "common/Color.h"
17#include "common/MemoryBuffer.h"
18#include "common/debug.h"
19#include "common/mathutil.h"
20#include "image_util/imageformats.h"
21#include "image_util/loadimage.h"
22#include "libANGLE/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 mtl::TextureSwizzleChannels &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    releaseTexture(true);
904    mBoundSurface = nullptr;
905}
906
907void TextureMtl::releaseTexture(bool releaseImages)
908{
909    releaseTexture(releaseImages, false);
910}
911
912void TextureMtl::releaseTexture(bool releaseImages, bool releaseTextureObjectsOnly)
913{
914
915    if (releaseImages)
916    {
917        mTexImageDefs.clear();
918        mShaderImageViews.clear();
919    }
920    else if (mNativeTextureStorage)
921    {
922        // Release native texture but keep its old per face per mipmap level image views.
923        retainImageDefinitions();
924    }
925
926    mNativeTextureStorage       = nullptr;
927    mViewFromBaseToMaxLevel     = nullptr;
928    mSwizzleStencilSamplingView = nullptr;
929
930    // Clear render target cache for each texture's image. We don't erase them because they
931    // might still be referenced by a framebuffer.
932    for (auto &sliceRenderTargets : mPerLayerRenderTargets)
933    {
934        for (RenderTargetMtl &mipRenderTarget : sliceRenderTargets.second)
935        {
936            mipRenderTarget.reset();
937        }
938    }
939
940    for (mtl::TextureRef &view : mLevelViewsWithinBaseMax)
941    {
942        view.reset();
943    }
944
945    if (!releaseTextureObjectsOnly)
946    {
947        mMetalSamplerState = nil;
948        mFormat            = mtl::Format();
949    }
950}
951
952angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
953{
954    if (mNativeTextureStorage)
955    {
956        return angle::Result::Continue;
957    }
958
959    // This should not be called from immutable texture.
960    ASSERT(!isImmutableOrPBuffer());
961
962    ContextMtl *contextMtl = mtl::GetImpl(context);
963
964    // Create actual texture object:
965    GLuint mips        = mState.getMipmapMaxLevel() - mState.getEffectiveBaseLevel() + 1;
966    gl::ImageDesc desc = mState.getBaseLevelDesc();
967    ANGLE_MTL_CHECK(contextMtl, desc.format.valid(), GL_INVALID_OPERATION);
968    angle::FormatID angleFormatId =
969        angle::Format::InternalFormatToID(desc.format.info->sizedInternalFormat);
970    mFormat = contextMtl->getPixelFormat(angleFormatId);
971
972    ANGLE_TRY(createNativeTexture(context, mState.getType(), mips, desc.size));
973
974    // Transfer data from defined images to actual texture object
975    int numCubeFaces = static_cast<int>(mNativeTextureStorage->cubeFaces());
976    for (int face = 0; face < numCubeFaces; ++face)
977    {
978        for (mtl::MipmapNativeLevel actualMip = mtl::kZeroNativeMipLevel; actualMip.get() < mips;
979             ++actualMip)
980        {
981            GLuint imageMipLevel             = mNativeTextureStorage->getGLLevel(actualMip);
982            mtl::TextureRef &imageToTransfer = mTexImageDefs[face][imageMipLevel].image;
983
984            // Only transfer if this mip & slice image has been defined and in correct size &
985            // format.
986            gl::Extents actualMipSize = mNativeTextureStorage->size(imageMipLevel);
987            if (imageToTransfer && imageToTransfer->sizeAt0() == actualMipSize &&
988                imageToTransfer->arrayLength() == mNativeTextureStorage->arrayLength() &&
989                imageToTransfer->pixelFormat() == mNativeTextureStorage->pixelFormat())
990            {
991                mtl::BlitCommandEncoder *encoder = GetBlitCommandEncoderForResources(
992                    contextMtl,
993                    {imageToTransfer.get(), mNativeTextureStorage->getNativeTexture().get()});
994
995                encoder->copyTexture(imageToTransfer, 0, mtl::kZeroNativeMipLevel,
996                                     *mNativeTextureStorage, face, actualMip,
997                                     imageToTransfer->arrayLength(), 1);
998
999                // Invalidate texture image definition at this index so that we can make it a
1000                // view of the native texture at this index later.
1001                imageToTransfer = nullptr;
1002            }
1003        }
1004    }
1005
1006    return angle::Result::Continue;
1007}
1008
1009angle::Result TextureMtl::createNativeTexture(const gl::Context *context,
1010                                              gl::TextureType type,
1011                                              GLuint mips,
1012                                              const gl::Extents &size)
1013{
1014    ContextMtl *contextMtl = mtl::GetImpl(context);
1015
1016    // Create actual texture object:
1017    mSlices              = 1;
1018    bool allowFormatView = mFormat.hasDepthAndStencilBits() ||
1019                           needsFormatViewForPixelLocalStorage(
1020                               contextMtl->getDisplay()->getNativePixelLocalStorageOptions());
1021    mtl::TextureRef nativeTextureStorage;
1022    switch (type)
1023    {
1024        case gl::TextureType::_2D:
1025            ANGLE_TRY(mtl::Texture::Make2DTexture(
1026                contextMtl, mFormat, size.width, size.height, mips,
1027                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1028            break;
1029        case gl::TextureType::CubeMap:
1030            mSlices = 6;
1031            ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, size.width, mips,
1032                                                    /** renderTargetOnly */ false, allowFormatView,
1033                                                    &nativeTextureStorage));
1034            break;
1035        case gl::TextureType::_3D:
1036            ANGLE_TRY(mtl::Texture::Make3DTexture(
1037                contextMtl, mFormat, size.width, size.height, size.depth, mips,
1038                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1039            break;
1040        case gl::TextureType::_2DArray:
1041            mSlices = size.depth;
1042            ANGLE_TRY(mtl::Texture::Make2DArrayTexture(
1043                contextMtl, mFormat, size.width, size.height, mips, mSlices,
1044                /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage));
1045            break;
1046        default:
1047            UNREACHABLE();
1048    }
1049
1050    if (mState.getImmutableFormat())
1051    {
1052        mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1053            std::move(nativeTextureStorage), /*baseGLLevel=*/0);
1054    }
1055    else
1056    {
1057        mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1058            std::move(nativeTextureStorage), /*baseGLLevel=*/mState.getEffectiveBaseLevel());
1059    }
1060
1061    ANGLE_TRY(checkForEmulatedChannels(context, mFormat, *mNativeTextureStorage));
1062
1063    ANGLE_TRY(createViewFromBaseToMaxLevel());
1064
1065    // Create sampler state
1066    ANGLE_TRY(ensureSamplerStateCreated(context));
1067
1068    return angle::Result::Continue;
1069}
1070
1071angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context)
1072{
1073    if (mMetalSamplerState)
1074    {
1075        return angle::Result::Continue;
1076    }
1077
1078    ContextMtl *contextMtl = mtl::GetImpl(context);
1079
1080    mtl::SamplerDesc samplerDesc(mState.getSamplerState());
1081
1082    if (mFormat.actualAngleFormat().depthBits && !mFormat.getCaps().filterable)
1083    {
1084        // On devices not supporting filtering for depth textures, we need to convert to nearest
1085        // here.
1086        samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
1087        samplerDesc.magFilter = MTLSamplerMinMagFilterNearest;
1088        if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped)
1089        {
1090            samplerDesc.mipFilter = MTLSamplerMipFilterNearest;
1091        }
1092
1093        samplerDesc.maxAnisotropy = 1;
1094    }
1095
1096    // OpenGL ES 3.x: The rules for texel selection are modified
1097    // for cube maps so that texture wrap modes are ignored.
1098    if ((mState.getType() == gl::TextureType::CubeMap ||
1099         mState.getType() == gl::TextureType::CubeMapArray) &&
1100        context->getState().getClientMajorVersion() >= 3)
1101    {
1102        samplerDesc.rAddressMode = MTLSamplerAddressModeClampToEdge;
1103        samplerDesc.sAddressMode = MTLSamplerAddressModeClampToEdge;
1104        samplerDesc.tAddressMode = MTLSamplerAddressModeClampToEdge;
1105    }
1106    mMetalSamplerState = contextMtl->getDisplay()->getStateCache().getSamplerState(
1107        contextMtl->getMetalDevice(), samplerDesc);
1108
1109    return angle::Result::Continue;
1110}
1111
1112angle::Result TextureMtl::createViewFromBaseToMaxLevel()
1113{
1114    ASSERT(mNativeTextureStorage);
1115    uint32_t maxLevel =
1116        std::min(mNativeTextureStorage->getMaxSupportedGLLevel(), mState.getEffectiveMaxLevel());
1117
1118    mtl::TextureRef nativeViewFromBaseToMaxLevelRef;
1119    if (maxLevel == mNativeTextureStorage->getMaxSupportedGLLevel() &&
1120        mState.getEffectiveBaseLevel() == mNativeTextureStorage->getBaseGLLevel())
1121    {
1122        // If base & max level are the same in mNativeTextureStorage, we don't need
1123        // a dedicated view. Furthermore, Intel driver has some bugs when sampling a view
1124        // of a stencil texture.
1125        nativeViewFromBaseToMaxLevelRef = mNativeTextureStorage->getNativeTexture();
1126    }
1127    else
1128    {
1129        uint32_t baseToMaxLevels = maxLevel - mState.getEffectiveBaseLevel() + 1;
1130        nativeViewFromBaseToMaxLevelRef =
1131            mNativeTextureStorage->createMipsView(mState.getEffectiveBaseLevel(), baseToMaxLevels);
1132    }
1133
1134    mViewFromBaseToMaxLevel = std::make_unique<NativeTextureWrapper>(
1135        nativeViewFromBaseToMaxLevelRef, mState.getEffectiveBaseLevel());
1136
1137    // Recreate in bindToShader()
1138    mSwizzleStencilSamplingView = nullptr;
1139    return angle::Result::Continue;
1140}
1141
1142angle::Result TextureMtl::onBaseMaxLevelsChanged(const gl::Context *context)
1143{
1144    if (!mNativeTextureStorage)
1145    {
1146        return angle::Result::Continue;
1147    }
1148
1149    if (isImmutableOrPBuffer())
1150    {
1151        // For immutable texture, only recreate base-max view.
1152        ANGLE_TRY(createViewFromBaseToMaxLevel());
1153        // Invalidate base-max per level views so that they can be recreated
1154        // in generateMipmap()
1155        for (mtl::TextureRef &view : mLevelViewsWithinBaseMax)
1156        {
1157            view.reset();
1158        }
1159        return angle::Result::Continue;
1160    }
1161
1162    ContextMtl *contextMtl = mtl::GetImpl(context);
1163
1164    // Release native texture but keep old image definitions so that it can be recreated from old
1165    // image definitions with different base level
1166    releaseTexture(false, true);
1167
1168    // Tell context to rebind textures
1169    contextMtl->invalidateCurrentTextures();
1170
1171    return angle::Result::Continue;
1172}
1173
1174angle::Result TextureMtl::ensureImageCreated(const gl::Context *context,
1175                                             const gl::ImageIndex &index)
1176{
1177    mtl::TextureRef &image = getImage(index);
1178    if (!image)
1179    {
1180        // Image at this level hasn't been defined yet. We need to define it:
1181        const gl::ImageDesc &desc = mState.getImageDesc(index);
1182        ANGLE_TRY(redefineImage(context, index, mFormat, desc.size));
1183    }
1184    return angle::Result::Continue;
1185}
1186
1187angle::Result TextureMtl::ensureLevelViewsWithinBaseMaxCreated()
1188{
1189    ASSERT(mViewFromBaseToMaxLevel);
1190    for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel;
1191         mip.get() < mViewFromBaseToMaxLevel->mipmapLevels(); ++mip)
1192    {
1193        if (mLevelViewsWithinBaseMax[mip])
1194        {
1195            continue;
1196        }
1197
1198        GLuint mipGLLevel = mViewFromBaseToMaxLevel->getGLLevel(mip);
1199
1200        if (mViewFromBaseToMaxLevel->textureType() != MTLTextureTypeCube &&
1201            mTexImageDefs[0][mipGLLevel].image)
1202        {
1203            // Reuse texture image view.
1204            mLevelViewsWithinBaseMax[mip] = mTexImageDefs[0][mipGLLevel].image;
1205        }
1206        else
1207        {
1208            mLevelViewsWithinBaseMax[mip] = mNativeTextureStorage->createMipView(mipGLLevel);
1209        }
1210    }
1211    return angle::Result::Continue;
1212}
1213
1214mtl::TextureRef TextureMtl::createImageViewFromTextureStorage(GLuint cubeFaceOrZero, GLuint glLevel)
1215{
1216    mtl::TextureRef image;
1217    if (mNativeTextureStorage->textureType() == MTLTextureTypeCube)
1218    {
1219        // Cube texture's image is per face.
1220        image = mNativeTextureStorage->createSliceMipView(cubeFaceOrZero, glLevel);
1221    }
1222    else
1223    {
1224        if (mViewFromBaseToMaxLevel->isGLLevelSupported(glLevel))
1225        {
1226            mtl::MipmapNativeLevel nativeLevel = mViewFromBaseToMaxLevel->getNativeLevel(glLevel);
1227            if (mLevelViewsWithinBaseMax[nativeLevel])
1228            {
1229                // Reuse the native level view
1230                image = mLevelViewsWithinBaseMax[nativeLevel];
1231            }
1232        }
1233
1234        if (!image)
1235        {
1236            image = mNativeTextureStorage->createMipView(glLevel);
1237        }
1238    }
1239
1240    return image;
1241}
1242
1243void TextureMtl::retainImageDefinitions()
1244{
1245    if (!mNativeTextureStorage)
1246    {
1247        return;
1248    }
1249    const GLuint mips = mNativeTextureStorage->mipmapLevels();
1250
1251    int numCubeFaces = 1;
1252    switch (mState.getType())
1253    {
1254        case gl::TextureType::CubeMap:
1255            numCubeFaces = 6;
1256            break;
1257        default:
1258            break;
1259    }
1260
1261    // Create image view per cube face, per mip level
1262    for (int face = 0; face < numCubeFaces; ++face)
1263    {
1264        for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel; mip.get() < mips; ++mip)
1265        {
1266            GLuint imageMipLevel         = mNativeTextureStorage->getGLLevel(mip);
1267            ImageDefinitionMtl &imageDef = mTexImageDefs[face][imageMipLevel];
1268            if (imageDef.image)
1269            {
1270                continue;
1271            }
1272            imageDef.image    = createImageViewFromTextureStorage(face, imageMipLevel);
1273            imageDef.formatID = mFormat.intendedFormatId;
1274        }
1275    }
1276}
1277
1278mtl::TextureRef &TextureMtl::getImage(const gl::ImageIndex &imageIndex)
1279{
1280    return getImageDefinition(imageIndex).image;
1281}
1282
1283ImageDefinitionMtl &TextureMtl::getImageDefinition(const gl::ImageIndex &imageIndex)
1284{
1285    GLuint cubeFaceOrZero        = GetImageCubeFaceIndexOrZeroFrom(imageIndex);
1286    ImageDefinitionMtl &imageDef = mTexImageDefs[cubeFaceOrZero][imageIndex.getLevelIndex()];
1287
1288    if (!imageDef.image && mNativeTextureStorage)
1289    {
1290        // If native texture is already created, and the image at this index is not available,
1291        // then create a view of native texture at this index, so that modifications of the image
1292        // are reflected back to native texture's respective index.
1293        if (!mNativeTextureStorage->isGLLevelSupported(imageIndex.getLevelIndex()))
1294        {
1295            // Image outside native texture's mip levels is skipped.
1296            return imageDef;
1297        }
1298
1299        imageDef.image =
1300            createImageViewFromTextureStorage(cubeFaceOrZero, imageIndex.getLevelIndex());
1301        imageDef.formatID = mFormat.intendedFormatId;
1302    }
1303
1304    return imageDef;
1305}
1306RenderTargetMtl &TextureMtl::getRenderTarget(const gl::ImageIndex &imageIndex)
1307{
1308    ASSERT(imageIndex.getType() == gl::TextureType::_2D ||
1309           imageIndex.getType() == gl::TextureType::Rectangle ||
1310           imageIndex.getType() == gl::TextureType::_2DMultisample || imageIndex.hasLayer());
1311    GLuint layer         = GetImageLayerIndexFrom(imageIndex);
1312    RenderTargetMtl &rtt = mPerLayerRenderTargets[layer][imageIndex.getLevelIndex()];
1313    if (!rtt.getTexture())
1314    {
1315        // Lazy initialization of render target:
1316        mtl::TextureRef &image = getImage(imageIndex);
1317        if (image)
1318        {
1319            if (imageIndex.getType() == gl::TextureType::CubeMap)
1320            {
1321                // Cube map is special, the image is already the view of its layer
1322                rtt.set(image, mtl::kZeroNativeMipLevel, 0, mFormat);
1323            }
1324            else
1325            {
1326                rtt.set(image, mtl::kZeroNativeMipLevel, layer, mFormat);
1327            }
1328        }
1329    }
1330    return rtt;
1331}
1332
1333angle::Result TextureMtl::setImage(const gl::Context *context,
1334                                   const gl::ImageIndex &index,
1335                                   GLenum internalFormat,
1336                                   const gl::Extents &size,
1337                                   GLenum format,
1338                                   GLenum type,
1339                                   const gl::PixelUnpackState &unpack,
1340                                   gl::Buffer *unpackBuffer,
1341                                   const uint8_t *pixels)
1342{
1343    const gl::InternalFormat &dstFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
1344
1345    return setImageImpl(context, index, dstFormatInfo, size, format, type, unpack, unpackBuffer,
1346                        pixels);
1347}
1348
1349angle::Result TextureMtl::setSubImage(const gl::Context *context,
1350                                      const gl::ImageIndex &index,
1351                                      const gl::Box &area,
1352                                      GLenum format,
1353                                      GLenum type,
1354                                      const gl::PixelUnpackState &unpack,
1355                                      gl::Buffer *unpackBuffer,
1356                                      const uint8_t *pixels)
1357{
1358    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
1359
1360    return setSubImageImpl(context, index, area, formatInfo, type, unpack, unpackBuffer, pixels);
1361}
1362
1363angle::Result TextureMtl::setCompressedImage(const gl::Context *context,
1364                                             const gl::ImageIndex &index,
1365                                             GLenum internalFormat,
1366                                             const gl::Extents &size,
1367                                             const gl::PixelUnpackState &unpack,
1368                                             size_t imageSize,
1369                                             const uint8_t *pixels)
1370{
1371    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1372    const gl::State &glState             = context->getState();
1373    gl::Buffer *unpackBuffer             = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
1374
1375    return setImageImpl(context, index, formatInfo, size, internalFormat, GL_UNSIGNED_BYTE, unpack,
1376                        unpackBuffer, pixels);
1377}
1378
1379angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context,
1380                                                const gl::ImageIndex &index,
1381                                                const gl::Box &area,
1382                                                GLenum format,
1383                                                const gl::PixelUnpackState &unpack,
1384                                                size_t imageSize,
1385                                                const uint8_t *pixels)
1386{
1387
1388    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE);
1389
1390    const gl::State &glState = context->getState();
1391    gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
1392
1393    return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, unpackBuffer,
1394                           pixels);
1395}
1396
1397angle::Result TextureMtl::copyImage(const gl::Context *context,
1398                                    const gl::ImageIndex &index,
1399                                    const gl::Rectangle &sourceArea,
1400                                    GLenum internalFormat,
1401                                    gl::Framebuffer *source)
1402{
1403    gl::Extents newImageSize(sourceArea.width, sourceArea.height, 1);
1404    const gl::InternalFormat &internalFormatInfo =
1405        gl::GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE);
1406
1407    ContextMtl *contextMtl = mtl::GetImpl(context);
1408    angle::FormatID angleFormatId =
1409        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
1410    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1411
1412    FramebufferMtl *srcFramebufferMtl = mtl::GetImpl(source);
1413    RenderTargetMtl *srcReadRT        = srcFramebufferMtl->getColorReadRenderTarget(context);
1414    RenderTargetMtl colorReadRT;
1415    if (srcReadRT)
1416    {
1417        // Need to duplicate RenderTargetMtl since the srcReadRT would be invalidated in
1418        // redefineImage(). This can happen if the source and this texture are the same texture.
1419        // Duplication ensures the copyImage() will be able to proceed even if the source texture
1420        // will be redefined.
1421        colorReadRT.duplicateFrom(*srcReadRT);
1422    }
1423
1424    ANGLE_TRY(redefineImage(context, index, mtlFormat, newImageSize));
1425
1426    gl::Extents fbSize = source->getReadColorAttachment()->getSize();
1427    gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
1428    if (context->isWebGL() && !fbRect.encloses(sourceArea))
1429    {
1430        ANGLE_TRY(initializeContents(context, GL_NONE, index));
1431    }
1432
1433    return copySubImageImpl(context, index, gl::Offset(0, 0, 0), sourceArea, internalFormatInfo,
1434                            srcFramebufferMtl, &colorReadRT);
1435}
1436
1437angle::Result TextureMtl::copySubImage(const gl::Context *context,
1438                                       const gl::ImageIndex &index,
1439                                       const gl::Offset &destOffset,
1440                                       const gl::Rectangle &sourceArea,
1441                                       gl::Framebuffer *source)
1442{
1443    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
1444    FramebufferMtl *srcFramebufferMtl       = mtl::GetImpl(source);
1445    RenderTargetMtl *colorReadRT            = srcFramebufferMtl->getColorReadRenderTarget(context);
1446    return copySubImageImpl(context, index, destOffset, sourceArea, currentFormat,
1447                            srcFramebufferMtl, colorReadRT);
1448}
1449
1450angle::Result TextureMtl::copyTexture(const gl::Context *context,
1451                                      const gl::ImageIndex &index,
1452                                      GLenum internalFormat,
1453                                      GLenum type,
1454                                      GLint sourceLevel,
1455                                      bool unpackFlipY,
1456                                      bool unpackPremultiplyAlpha,
1457                                      bool unpackUnmultiplyAlpha,
1458                                      const gl::Texture *source)
1459{
1460    const gl::ImageDesc &sourceImageDesc = source->getTextureState().getImageDesc(
1461        NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
1462    const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
1463
1464    // Only 2D textures are supported.
1465    ASSERT(sourceImageDesc.size.depth == 1);
1466
1467    ContextMtl *contextMtl = mtl::GetImpl(context);
1468    angle::FormatID angleFormatId =
1469        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
1470    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1471
1472    ANGLE_TRY(redefineImage(context, index, mtlFormat, sourceImageDesc.size));
1473
1474    return copySubTextureImpl(
1475        context, index, gl::Offset(0, 0, 0), internalFormatInfo, sourceLevel,
1476        gl::Box(0, 0, 0, sourceImageDesc.size.width, sourceImageDesc.size.height, 1), unpackFlipY,
1477        unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
1478}
1479
1480angle::Result TextureMtl::copySubTexture(const gl::Context *context,
1481                                         const gl::ImageIndex &index,
1482                                         const gl::Offset &destOffset,
1483                                         GLint sourceLevel,
1484                                         const gl::Box &sourceBox,
1485                                         bool unpackFlipY,
1486                                         bool unpackPremultiplyAlpha,
1487                                         bool unpackUnmultiplyAlpha,
1488                                         const gl::Texture *source)
1489{
1490    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
1491
1492    return copySubTextureImpl(context, index, destOffset, currentFormat, sourceLevel, sourceBox,
1493                              unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
1494}
1495
1496angle::Result TextureMtl::copyCompressedTexture(const gl::Context *context,
1497                                                const gl::Texture *source)
1498{
1499    UNIMPLEMENTED();
1500
1501    return angle::Result::Stop;
1502}
1503
1504angle::Result TextureMtl::setStorage(const gl::Context *context,
1505                                     gl::TextureType type,
1506                                     size_t mipmaps,
1507                                     GLenum internalFormat,
1508                                     const gl::Extents &size)
1509{
1510    ContextMtl *contextMtl               = mtl::GetImpl(context);
1511    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1512    angle::FormatID angleFormatId =
1513        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
1514    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1515
1516    return setStorageImpl(context, type, mipmaps, mtlFormat, size);
1517}
1518
1519angle::Result TextureMtl::setStorageExternalMemory(const gl::Context *context,
1520                                                   gl::TextureType type,
1521                                                   size_t levels,
1522                                                   GLenum internalFormat,
1523                                                   const gl::Extents &size,
1524                                                   gl::MemoryObject *memoryObject,
1525                                                   GLuint64 offset,
1526                                                   GLbitfield createFlags,
1527                                                   GLbitfield usageFlags,
1528                                                   const void *imageCreateInfoPNext)
1529{
1530    UNIMPLEMENTED();
1531
1532    return angle::Result::Stop;
1533}
1534
1535angle::Result TextureMtl::setStorageMultisample(const gl::Context *context,
1536                                                gl::TextureType type,
1537                                                GLsizei samples,
1538                                                GLint internalformat,
1539                                                const gl::Extents &size,
1540                                                bool fixedSampleLocations)
1541{
1542    UNIMPLEMENTED();
1543
1544    return angle::Result::Stop;
1545}
1546
1547angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context,
1548                                            gl::TextureType type,
1549                                            egl::Image *image)
1550{
1551    releaseTexture(true);
1552
1553    ContextMtl *contextMtl = mtl::GetImpl(context);
1554
1555    ImageMtl *imageMtl = mtl::GetImpl(image);
1556    if (type != imageMtl->getImageTextureType())
1557    {
1558        return angle::Result::Stop;
1559    }
1560
1561    mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1562        imageMtl->getTexture(), /*baseGLLevel=*/0);
1563
1564    const angle::FormatID angleFormatId =
1565        angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat);
1566    mFormat = contextMtl->getPixelFormat(angleFormatId);
1567
1568    mSlices = mNativeTextureStorage->cubeFacesOrArrayLength();
1569
1570    ANGLE_TRY(ensureSamplerStateCreated(context));
1571    ANGLE_TRY(createViewFromBaseToMaxLevel());
1572
1573    // Tell context to rebind textures
1574    contextMtl->invalidateCurrentTextures();
1575
1576    return angle::Result::Continue;
1577}
1578
1579angle::Result TextureMtl::setImageExternal(const gl::Context *context,
1580                                           gl::TextureType type,
1581                                           egl::Stream *stream,
1582                                           const egl::Stream::GLTextureDescription &desc)
1583{
1584    UNIMPLEMENTED();
1585    return angle::Result::Stop;
1586}
1587
1588angle::Result TextureMtl::generateMipmap(const gl::Context *context)
1589{
1590    ANGLE_TRY(ensureTextureCreated(context));
1591
1592    ContextMtl *contextMtl = mtl::GetImpl(context);
1593    if (!mViewFromBaseToMaxLevel)
1594    {
1595        return angle::Result::Continue;
1596    }
1597
1598    const mtl::FormatCaps &caps = mFormat.getCaps();
1599    //
1600    bool sRGB = mFormat.actualInternalFormat().colorEncoding == GL_SRGB;
1601
1602    bool avoidGPUPath =
1603        contextMtl->getDisplay()->getFeatures().forceNonCSBaseMipmapGeneration.enabled &&
1604        mViewFromBaseToMaxLevel->widthAt0() < 5;
1605
1606    if (!avoidGPUPath && caps.writable && mState.getType() == gl::TextureType::_3D)
1607    {
1608        // http://anglebug.com/4921.
1609        // Use compute for 3D mipmap generation.
1610        ANGLE_TRY(ensureLevelViewsWithinBaseMaxCreated());
1611        ANGLE_TRY(contextMtl->getDisplay()->getUtils().generateMipmapCS(
1612            contextMtl, *mViewFromBaseToMaxLevel, sRGB, &mLevelViewsWithinBaseMax));
1613    }
1614    else if (!avoidGPUPath && caps.filterable && caps.colorRenderable)
1615    {
1616        mtl::BlitCommandEncoder *blitEncoder = GetBlitCommandEncoderForResources(
1617            contextMtl, {mViewFromBaseToMaxLevel->getNativeTexture().get()});
1618        blitEncoder->generateMipmapsForTexture(*mViewFromBaseToMaxLevel);
1619    }
1620    else
1621    {
1622        ANGLE_TRY(generateMipmapCPU(context));
1623    }
1624
1625    return angle::Result::Continue;
1626}
1627
1628angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
1629{
1630    ASSERT(mViewFromBaseToMaxLevel);
1631
1632    ContextMtl *contextMtl           = mtl::GetImpl(context);
1633    const angle::Format &angleFormat = mFormat.actualAngleFormat();
1634    // This format must have mip generation function.
1635    ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction);
1636
1637    for (uint32_t slice = 0; slice < mSlices; ++slice)
1638    {
1639        GLuint baseGLLevel = mViewFromBaseToMaxLevel->getBaseGLLevel();
1640
1641        uint32_t prevLevelWidth    = mViewFromBaseToMaxLevel->widthAt0();
1642        uint32_t prevLevelHeight   = mViewFromBaseToMaxLevel->heightAt0();
1643        uint32_t prevLevelDepth    = mViewFromBaseToMaxLevel->depthAt0();
1644        size_t prevLevelRowPitch   = angleFormat.pixelBytes * prevLevelWidth;
1645        size_t prevLevelDepthPitch = prevLevelRowPitch * prevLevelHeight;
1646        std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow)
1647                                                     uint8_t[prevLevelDepthPitch * prevLevelDepth]);
1648        ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData);
1649        std::unique_ptr<uint8_t[]> dstLevelData;
1650
1651        // Download base level data
1652        mViewFromBaseToMaxLevel->getBytes(
1653            contextMtl, prevLevelRowPitch, prevLevelDepthPitch,
1654            MTLRegionMake3D(0, 0, 0, prevLevelWidth, prevLevelHeight, prevLevelDepth), baseGLLevel,
1655            slice, prevLevelData.get());
1656
1657        for (GLuint mip = 1; mip < mViewFromBaseToMaxLevel->mipmapLevels(); ++mip)
1658        {
1659            GLuint glLevel     = baseGLLevel + mip;
1660            uint32_t dstWidth  = mViewFromBaseToMaxLevel->width(glLevel);
1661            uint32_t dstHeight = mViewFromBaseToMaxLevel->height(glLevel);
1662            uint32_t dstDepth  = mViewFromBaseToMaxLevel->depth(glLevel);
1663
1664            size_t dstRowPitch   = angleFormat.pixelBytes * dstWidth;
1665            size_t dstDepthPitch = dstRowPitch * dstHeight;
1666            size_t dstDataSize   = dstDepthPitch * dstDepth;
1667            if (!dstLevelData)
1668            {
1669                // Allocate once and reuse the buffer
1670                dstLevelData.reset(new (std::nothrow) uint8_t[dstDataSize]);
1671                ANGLE_CHECK_GL_ALLOC(contextMtl, dstLevelData);
1672            }
1673
1674            // Generate mip level
1675            angleFormat.mipGenerationFunction(
1676                prevLevelWidth, prevLevelHeight, 1, prevLevelData.get(), prevLevelRowPitch,
1677                prevLevelDepthPitch, dstLevelData.get(), dstRowPitch, dstDepthPitch);
1678
1679            mtl::MipmapNativeLevel nativeLevel = mViewFromBaseToMaxLevel->getNativeLevel(glLevel);
1680
1681            // Upload to texture
1682            ANGLE_TRY(UploadTextureContents(context, angleFormat,
1683                                            MTLRegionMake3D(0, 0, 0, dstWidth, dstHeight, dstDepth),
1684                                            nativeLevel, slice, dstLevelData.get(), dstRowPitch,
1685                                            dstDepthPitch, false, *mViewFromBaseToMaxLevel));
1686
1687            prevLevelWidth      = dstWidth;
1688            prevLevelHeight     = dstHeight;
1689            prevLevelDepth      = dstDepth;
1690            prevLevelRowPitch   = dstRowPitch;
1691            prevLevelDepthPitch = dstDepthPitch;
1692            std::swap(prevLevelData, dstLevelData);
1693        }  // for mip level
1694
1695    }  // For layers
1696
1697    return angle::Result::Continue;
1698}
1699
1700bool TextureMtl::needsFormatViewForPixelLocalStorage(
1701    const ShPixelLocalStorageOptions &plsOptions) const
1702{
1703    // On iOS devices with GPU family 5 and later, Metal doesn't apply lossless compression to
1704    // the texture if we set MTLTextureUsagePixelFormatView. This shouldn't be a problem though
1705    // because iOS devices implement pixel local storage with framebuffer fetch instead of shader
1706    // images.
1707    if (plsOptions.type == ShPixelLocalStorageType::ImageLoadStore)
1708    {
1709        switch (mFormat.metalFormat)
1710        {
1711            case MTLPixelFormatRGBA8Unorm:
1712            case MTLPixelFormatRGBA8Uint:
1713            case MTLPixelFormatRGBA8Sint:
1714                return !plsOptions.supportsNativeRGBA8ImageFormats;
1715            default:
1716                break;
1717        }
1718    }
1719    return false;
1720}
1721
1722bool TextureMtl::isImmutableOrPBuffer() const
1723{
1724    return mState.getImmutableFormat() || mBoundSurface;
1725}
1726
1727angle::Result TextureMtl::setBaseLevel(const gl::Context *context, GLuint baseLevel)
1728{
1729    return onBaseMaxLevelsChanged(context);
1730}
1731
1732angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface *surface)
1733{
1734    releaseTexture(true);
1735
1736    mBoundSurface         = surface;
1737    auto pBuffer          = GetImplAs<OffscreenSurfaceMtl>(surface);
1738    mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>(
1739        pBuffer->getColorTexture(), /*baseGLLevel=*/0);
1740    mFormat = pBuffer->getColorFormat();
1741    mSlices = mNativeTextureStorage->cubeFacesOrArrayLength();
1742
1743    ANGLE_TRY(ensureSamplerStateCreated(context));
1744    ANGLE_TRY(createViewFromBaseToMaxLevel());
1745
1746    // Tell context to rebind textures
1747    ContextMtl *contextMtl = mtl::GetImpl(context);
1748    contextMtl->invalidateCurrentTextures();
1749
1750    return angle::Result::Continue;
1751}
1752
1753angle::Result TextureMtl::releaseTexImage(const gl::Context *context)
1754{
1755    releaseTexture(true);
1756    mBoundSurface = nullptr;
1757    return angle::Result::Continue;
1758}
1759
1760angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context,
1761                                                    GLenum binding,
1762                                                    const gl::ImageIndex &imageIndex,
1763                                                    GLsizei samples,
1764                                                    FramebufferAttachmentRenderTarget **rtOut)
1765{
1766    ANGLE_TRY(ensureTextureCreated(context));
1767
1768    ContextMtl *contextMtl = mtl::GetImpl(context);
1769    ANGLE_MTL_TRY(contextMtl, mNativeTextureStorage);
1770
1771    *rtOut = &getRenderTarget(imageIndex);
1772
1773    return angle::Result::Continue;
1774}
1775
1776angle::Result TextureMtl::syncState(const gl::Context *context,
1777                                    const gl::Texture::DirtyBits &dirtyBits,
1778                                    gl::Command source)
1779{
1780    ContextMtl *contextMtl = mtl::GetImpl(context);
1781    for (size_t dirtyBit : dirtyBits)
1782    {
1783        switch (dirtyBit)
1784        {
1785            case gl::Texture::DIRTY_BIT_COMPARE_MODE:
1786            case gl::Texture::DIRTY_BIT_COMPARE_FUNC:
1787                // Tell context to rebind textures so that ProgramMtl has a chance to verify
1788                // depth texture compare mode.
1789                contextMtl->invalidateCurrentTextures();
1790                // fall through
1791                OS_FALLTHROUGH;
1792            case gl::Texture::DIRTY_BIT_MIN_FILTER:
1793            case gl::Texture::DIRTY_BIT_MAG_FILTER:
1794            case gl::Texture::DIRTY_BIT_WRAP_S:
1795            case gl::Texture::DIRTY_BIT_WRAP_T:
1796            case gl::Texture::DIRTY_BIT_WRAP_R:
1797            case gl::Texture::DIRTY_BIT_MAX_ANISOTROPY:
1798            case gl::Texture::DIRTY_BIT_MIN_LOD:
1799            case gl::Texture::DIRTY_BIT_MAX_LOD:
1800            case gl::Texture::DIRTY_BIT_SRGB_DECODE:
1801            case gl::Texture::DIRTY_BIT_BORDER_COLOR:
1802                // Recreate sampler state
1803                mMetalSamplerState = nil;
1804                break;
1805            case gl::Texture::DIRTY_BIT_MAX_LEVEL:
1806            case gl::Texture::DIRTY_BIT_BASE_LEVEL:
1807                ANGLE_TRY(onBaseMaxLevelsChanged(context));
1808                break;
1809            case gl::Texture::DIRTY_BIT_SWIZZLE_RED:
1810            case gl::Texture::DIRTY_BIT_SWIZZLE_GREEN:
1811            case gl::Texture::DIRTY_BIT_SWIZZLE_BLUE:
1812            case gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA:
1813            case gl::Texture::DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE:
1814            {
1815                // Recreate swizzle/stencil view.
1816                mSwizzleStencilSamplingView = nullptr;
1817            }
1818            break;
1819            default:
1820                break;
1821        }
1822    }
1823
1824    ANGLE_TRY(ensureTextureCreated(context));
1825    ANGLE_TRY(ensureSamplerStateCreated(context));
1826
1827    return angle::Result::Continue;
1828}
1829
1830angle::Result TextureMtl::bindToShader(const gl::Context *context,
1831                                       mtl::RenderCommandEncoder *cmdEncoder,
1832                                       gl::ShaderType shaderType,
1833                                       gl::Sampler *sampler,
1834                                       int textureSlotIndex,
1835                                       int samplerSlotIndex)
1836{
1837    ASSERT(mNativeTextureStorage);
1838    ASSERT(mViewFromBaseToMaxLevel);
1839
1840    float minLodClamp;
1841    float maxLodClamp;
1842    id<MTLSamplerState> samplerState;
1843
1844    if (!mSwizzleStencilSamplingView)
1845    {
1846#if ANGLE_MTL_SWIZZLE_AVAILABLE
1847        ContextMtl *contextMtl             = mtl::GetImpl(context);
1848        const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures();
1849
1850        // Sampling from unused channels of depth and stencil textures is undefined in Metal.
1851        // ANGLE relies on swizzled views to enforce values required by OpenGL ES specs. Some
1852        // drivers fail to sample from a swizzled view of a stencil texture so skip this step.
1853        const bool skipStencilSwizzle = mFormat.actualFormatId == angle::FormatID::S8_UINT &&
1854                                        features.avoidStencilTextureSwizzle.enabled;
1855        if (!skipStencilSwizzle &&
1856            (mState.getSwizzleState().swizzleRequired() ||
1857             mFormat.actualAngleFormat().hasDepthOrStencilBits() || mFormat.swizzled) &&
1858            features.hasTextureSwizzle.enabled)
1859        {
1860            const gl::InternalFormat &glInternalFormat = *mState.getBaseLevelDesc().format.info;
1861
1862            MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake(
1863                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1864                    context, mState.getSwizzleState().swizzleRed, mFormat, glInternalFormat)),
1865                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1866                    context, mState.getSwizzleState().swizzleGreen, mFormat, glInternalFormat)),
1867                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1868                    context, mState.getSwizzleState().swizzleBlue, mFormat, glInternalFormat)),
1869                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1870                    context, mState.getSwizzleState().swizzleAlpha, mFormat, glInternalFormat)));
1871
1872            MTLPixelFormat format = mViewFromBaseToMaxLevel->pixelFormat();
1873            if (mState.isStencilMode())
1874            {
1875                if (format == MTLPixelFormatDepth32Float_Stencil8)
1876                {
1877                    format = MTLPixelFormatX32_Stencil8;
1878                }
1879#    if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1880                else if (format == MTLPixelFormatDepth24Unorm_Stencil8)
1881                {
1882                    format = MTLPixelFormatX24_Stencil8;
1883                }
1884#    endif
1885            }
1886
1887            mSwizzleStencilSamplingView = mNativeTextureStorage->createMipsSwizzleView(
1888                mViewFromBaseToMaxLevel->getBaseGLLevel(), mViewFromBaseToMaxLevel->mipmapLevels(),
1889                format, swizzle);
1890        }
1891        else
1892#endif  // ANGLE_MTL_SWIZZLE_AVAILABLE
1893        {
1894            mSwizzleStencilSamplingView = mState.isStencilMode()
1895                                              ? mViewFromBaseToMaxLevel->getStencilView()
1896                                              : mViewFromBaseToMaxLevel->getNativeTexture();
1897        }
1898    }
1899
1900    if (!sampler)
1901    {
1902        samplerState = mMetalSamplerState;
1903        minLodClamp  = mState.getSamplerState().getMinLod();
1904        maxLodClamp  = mState.getSamplerState().getMaxLod();
1905    }
1906    else
1907    {
1908        SamplerMtl *samplerMtl = mtl::GetImpl(sampler);
1909        samplerState           = samplerMtl->getSampler(mtl::GetImpl(context));
1910        minLodClamp            = sampler->getSamplerState().getMinLod();
1911        maxLodClamp            = sampler->getSamplerState().getMaxLod();
1912    }
1913
1914    minLodClamp = std::max(minLodClamp, 0.f);
1915
1916    cmdEncoder->setTexture(shaderType, mSwizzleStencilSamplingView, textureSlotIndex);
1917    cmdEncoder->setSamplerState(shaderType, samplerState, minLodClamp, maxLodClamp,
1918                                samplerSlotIndex);
1919
1920    return angle::Result::Continue;
1921}
1922
1923angle::Result TextureMtl::bindToShaderImage(const gl::Context *context,
1924                                            mtl::RenderCommandEncoder *cmdEncoder,
1925                                            gl::ShaderType shaderType,
1926                                            int textureSlotIndex,
1927                                            int level,
1928                                            int layer,
1929                                            GLenum format)
1930{
1931    ASSERT(mNativeTextureStorage);
1932    ASSERT(mState.getImmutableFormat());
1933    ASSERT(0 <= level && static_cast<uint32_t>(level) < mState.getImmutableLevels());
1934    ASSERT(0 <= layer && static_cast<uint32_t>(layer) < mSlices);
1935
1936    ContextMtl *contextMtl        = mtl::GetImpl(context);
1937    angle::FormatID angleFormatId = angle::Format::InternalFormatToID(format);
1938    mtl::Format imageAccessFormat = contextMtl->getPixelFormat(angleFormatId);
1939    LayerLevelTextureViewVector &textureViewVector =
1940        mShaderImageViews[imageAccessFormat.metalFormat];
1941    mtl::TextureRef &textureRef = GetLayerLevelTextureView(&textureViewVector, layer, level,
1942                                                           mSlices, mState.getImmutableLevels());
1943
1944    if (textureRef == nullptr)
1945    {
1946        textureRef = mNativeTextureStorage->createShaderImageView2D(level, layer,
1947                                                                    imageAccessFormat.metalFormat);
1948    }
1949
1950    cmdEncoder->setRWTexture(shaderType, textureRef, textureSlotIndex);
1951    return angle::Result::Continue;
1952}
1953
1954angle::Result TextureMtl::redefineImage(const gl::Context *context,
1955                                        const gl::ImageIndex &index,
1956                                        const mtl::Format &mtlFormat,
1957                                        const gl::Extents &size)
1958{
1959    bool imageWithinNativeStorageLevels = false;
1960    if (mNativeTextureStorage && mNativeTextureStorage->isGLLevelSupported(index.getLevelIndex()))
1961    {
1962        imageWithinNativeStorageLevels = true;
1963        GLuint glLevel                 = index.getLevelIndex();
1964        // Calculate the expected size for the index we are defining. If the size is different
1965        // from the given size, or the format is different, we are redefining the image so we
1966        // must release it.
1967        ASSERT(mNativeTextureStorage->textureType() == mtl::GetTextureType(index.getType()));
1968        if (mFormat != mtlFormat || size != mNativeTextureStorage->size(glLevel))
1969        {
1970            // Keep other images
1971            releaseTexture(/*releaseImages=*/false);
1972        }
1973    }
1974
1975    // Early-out on empty textures, don't create a zero-sized storage.
1976    if (size.empty())
1977    {
1978        return angle::Result::Continue;
1979    }
1980
1981    ContextMtl *contextMtl = mtl::GetImpl(context);
1982    // Cache last defined image format:
1983    mFormat                      = mtlFormat;
1984    ImageDefinitionMtl &imageDef = getImageDefinition(index);
1985
1986    // If native texture still exists, it means the size hasn't been changed, no need to create new
1987    // image
1988    if (mNativeTextureStorage && imageDef.image && imageWithinNativeStorageLevels)
1989    {
1990        ASSERT(imageDef.image->textureType() ==
1991                   mtl::GetTextureType(GetTextureImageType(index.getType())) &&
1992               imageDef.formatID == mFormat.intendedFormatId && imageDef.image->sizeAt0() == size);
1993    }
1994    else
1995    {
1996        imageDef.formatID    = mtlFormat.intendedFormatId;
1997        bool allowFormatView = mFormat.hasDepthAndStencilBits() ||
1998                               needsFormatViewForPixelLocalStorage(
1999                                   contextMtl->getDisplay()->getNativePixelLocalStorageOptions());
2000        // Create image to hold texture's data at this level & slice:
2001        switch (index.getType())
2002        {
2003            case gl::TextureType::_2D:
2004            case gl::TextureType::CubeMap:
2005                ANGLE_TRY(mtl::Texture::Make2DTexture(
2006                    contextMtl, mtlFormat, size.width, size.height, 1,
2007                    /** renderTargetOnly */ false, allowFormatView, &imageDef.image));
2008                break;
2009            case gl::TextureType::_3D:
2010                ANGLE_TRY(mtl::Texture::Make3DTexture(
2011                    contextMtl, mtlFormat, size.width, size.height, size.depth, 1,
2012                    /** renderTargetOnly */ false, allowFormatView, &imageDef.image));
2013                break;
2014            case gl::TextureType::_2DArray:
2015                ANGLE_TRY(mtl::Texture::Make2DArrayTexture(
2016                    contextMtl, mtlFormat, size.width, size.height, 1, size.depth,
2017                    /** renderTargetOnly */ false, allowFormatView, &imageDef.image));
2018                break;
2019            default:
2020                UNREACHABLE();
2021        }
2022
2023        // Make sure emulated channels are properly initialized in this newly allocated texture.
2024        ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, imageDef.image));
2025    }
2026
2027    // Tell context to rebind textures
2028    contextMtl->invalidateCurrentTextures();
2029
2030    return angle::Result::Continue;
2031}
2032
2033// If mipmaps = 0, this function will create full mipmaps texture.
2034angle::Result TextureMtl::setStorageImpl(const gl::Context *context,
2035                                         gl::TextureType type,
2036                                         size_t mipmaps,
2037                                         const mtl::Format &mtlFormat,
2038                                         const gl::Extents &size)
2039{
2040    // Don't need to hold old images data.
2041    releaseTexture(true);
2042
2043    ContextMtl *contextMtl = mtl::GetImpl(context);
2044
2045    // Tell context to rebind textures
2046    contextMtl->invalidateCurrentTextures();
2047
2048    mFormat = mtlFormat;
2049
2050    ANGLE_TRY(createNativeTexture(context, type, mState.getImmutableLevels(), size));
2051    ANGLE_TRY(createViewFromBaseToMaxLevel());
2052
2053    return angle::Result::Continue;
2054}
2055
2056angle::Result TextureMtl::setImageImpl(const gl::Context *context,
2057                                       const gl::ImageIndex &index,
2058                                       const gl::InternalFormat &dstFormatInfo,
2059                                       const gl::Extents &size,
2060                                       GLenum srcFormat,
2061                                       GLenum srcType,
2062                                       const gl::PixelUnpackState &unpack,
2063                                       gl::Buffer *unpackBuffer,
2064                                       const uint8_t *pixels)
2065{
2066    ContextMtl *contextMtl = mtl::GetImpl(context);
2067    angle::FormatID angleFormatId =
2068        angle::Format::InternalFormatToID(dstFormatInfo.sizedInternalFormat);
2069    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
2070
2071    ANGLE_TRY(redefineImage(context, index, mtlFormat, size));
2072
2073    // Early-out on empty textures, don't create a zero-sized storage.
2074    if (size.empty())
2075    {
2076        return angle::Result::Continue;
2077    }
2078
2079    // Format of the supplied pixels.
2080    const gl::InternalFormat *srcFormatInfo;
2081    if (srcFormat != dstFormatInfo.format || srcType != dstFormatInfo.type)
2082    {
2083        srcFormatInfo = &gl::GetInternalFormatInfo(srcFormat, srcType);
2084    }
2085    else
2086    {
2087        srcFormatInfo = &dstFormatInfo;
2088    }
2089    return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth),
2090                           *srcFormatInfo, srcType, unpack, unpackBuffer, pixels);
2091}
2092
2093angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
2094                                          const gl::ImageIndex &index,
2095                                          const gl::Box &area,
2096                                          const gl::InternalFormat &formatInfo,
2097                                          GLenum type,
2098                                          const gl::PixelUnpackState &unpack,
2099                                          gl::Buffer *unpackBuffer,
2100                                          const uint8_t *oriPixels)
2101{
2102    if (!oriPixels && !unpackBuffer)
2103    {
2104        return angle::Result::Continue;
2105    }
2106
2107    ContextMtl *contextMtl = mtl::GetImpl(context);
2108
2109    ANGLE_TRY(ensureImageCreated(context, index));
2110    mtl::TextureRef &image = getImage(index);
2111
2112    GLuint sourceRowPitch   = 0;
2113    GLuint sourceDepthPitch = 0;
2114    GLuint sourceSkipBytes  = 0;
2115    ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment,
2116                                                               unpack.rowLength, &sourceRowPitch));
2117    ANGLE_CHECK_GL_MATH(
2118        contextMtl, formatInfo.computeDepthPitch(area.height, unpack.imageHeight, sourceRowPitch,
2119                                                 &sourceDepthPitch));
2120    ANGLE_CHECK_GL_MATH(contextMtl,
2121                        formatInfo.computeSkipBytes(type, sourceRowPitch, sourceDepthPitch, unpack,
2122                                                    index.usesTex3D(), &sourceSkipBytes));
2123
2124    // Get corresponding source data's ANGLE format
2125    angle::FormatID srcAngleFormatId;
2126    if (formatInfo.sizedInternalFormat == GL_DEPTH_COMPONENT24)
2127    {
2128        // GL_DEPTH_COMPONENT24 is special case, its supplied data is 32 bit depth.
2129        srcAngleFormatId = angle::FormatID::D32_UNORM;
2130    }
2131    else
2132    {
2133        srcAngleFormatId = angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
2134    }
2135    const angle::Format &srcAngleFormat = angle::Format::Get(srcAngleFormatId);
2136
2137    const uint8_t *usablePixels = oriPixels + sourceSkipBytes;
2138
2139    // Upload to texture
2140    if (index.getType() == gl::TextureType::_2DArray)
2141    {
2142        // OpenGL unifies texture array and texture 3d's box area by using z & depth as array start
2143        // index & length for texture array. However, Metal treats them differently. We need to
2144        // handle them in separate code.
2145        MTLRegion mtlRegion = MTLRegionMake3D(area.x, area.y, 0, area.width, area.height, 1);
2146
2147        for (int slice = 0; slice < area.depth; ++slice)
2148        {
2149            int sliceIndex           = slice + area.z;
2150            const uint8_t *srcPixels = usablePixels + slice * sourceDepthPitch;
2151            ANGLE_TRY(setPerSliceSubImage(context, sliceIndex, mtlRegion, formatInfo, type,
2152                                          srcAngleFormat, sourceRowPitch, sourceDepthPitch,
2153                                          unpackBuffer, srcPixels, image));
2154        }
2155    }
2156    else
2157    {
2158        MTLRegion mtlRegion =
2159            MTLRegionMake3D(area.x, area.y, area.z, area.width, area.height, area.depth);
2160
2161        ANGLE_TRY(setPerSliceSubImage(context, 0, mtlRegion, formatInfo, type, srcAngleFormat,
2162                                      sourceRowPitch, sourceDepthPitch, unpackBuffer, usablePixels,
2163                                      image));
2164    }
2165
2166    return angle::Result::Continue;
2167}
2168
2169angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context,
2170                                              int slice,
2171                                              const MTLRegion &mtlArea,
2172                                              const gl::InternalFormat &internalFormat,
2173                                              GLenum type,
2174                                              const angle::Format &pixelsAngleFormat,
2175                                              size_t pixelsRowPitch,
2176                                              size_t pixelsDepthPitch,
2177                                              gl::Buffer *unpackBuffer,
2178                                              const uint8_t *pixels,
2179                                              const mtl::TextureRef &image)
2180{
2181    // If source pixels are luminance or RGB8, we need to convert them to RGBA
2182
2183    if (mFormat.needConversion(pixelsAngleFormat.id))
2184    {
2185        return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
2186                                             pixelsAngleFormat, pixelsRowPitch, pixelsDepthPitch,
2187                                             unpackBuffer, pixels, image);
2188    }
2189
2190    // No conversion needed.
2191    ContextMtl *contextMtl = mtl::GetImpl(context);
2192
2193    if (unpackBuffer)
2194    {
2195        uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
2196        GLuint minRowPitch;
2197        ANGLE_CHECK_GL_MATH(contextMtl, internalFormat.computeRowPitch(
2198                                            type, static_cast<GLint>(mtlArea.size.width),
2199                                            /** aligment */ 1, /** rowLength */ 0, &minRowPitch));
2200        if (offset % mFormat.actualAngleFormat().pixelBytes || pixelsRowPitch < minRowPitch)
2201        {
2202            // offset is not divisible by pixelByte or the source row pitch is smaller than minimum
2203            // row pitch, use convertAndSetPerSliceSubImage() function.
2204            return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
2205                                                 pixelsAngleFormat, pixelsRowPitch,
2206                                                 pixelsDepthPitch, unpackBuffer, pixels, image);
2207        }
2208
2209        BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
2210
2211        if (mFormat.hasDepthAndStencilBits())
2212        {
2213            // NOTE(hqle): packed depth & stencil texture cannot copy from buffer directly, needs
2214            // to split its depth & stencil data and copy separately.
2215            const uint8_t *clientData = unpackBufferMtl->getBufferDataReadOnly(contextMtl);
2216            clientData += offset;
2217            ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea,
2218                                            mtl::kZeroNativeMipLevel, slice, clientData,
2219                                            pixelsRowPitch, pixelsDepthPitch, false, image));
2220        }
2221        else
2222        {
2223            mtl::BufferRef sourceBuffer = unpackBufferMtl->getCurrentBuffer();
2224            // PVRTC1 blocks are stored in a reflected Morton order
2225            // and need to be linearized for buffer uploads in Metal.
2226            // This step is skipped for textures that have only one block.
2227            if (mFormat.isPVRTC() && mtlArea.size.height > 4)
2228            {
2229                // PVRTC1 inherent requirement.
2230                ASSERT(gl::isPow2(mtlArea.size.width) && gl::isPow2(mtlArea.size.height));
2231                // Metal-specific limitation enforced by ANGLE validation.
2232                ASSERT(mtlArea.size.width == mtlArea.size.height);
2233                static_assert(gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE <= 262144,
2234                              "The current kernel can handle up to 65536 blocks per dimension.");
2235
2236                // Current command buffer implementation does not support 64-bit offsets.
2237                ANGLE_MTL_CHECK(contextMtl, offset <= std::numeric_limits<uint32_t>::max(),
2238                                GL_INVALID_OPERATION);
2239
2240                mtl::BufferRef stagingBuffer;
2241                ANGLE_TRY(
2242                    mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer));
2243
2244                mtl::BlockLinearizationParams params;
2245                params.srcBuffer       = sourceBuffer;
2246                params.dstBuffer       = stagingBuffer;
2247                params.srcBufferOffset = static_cast<uint32_t>(offset);
2248                params.blocksWide =
2249                    static_cast<GLuint>(mtlArea.size.width) / internalFormat.compressedBlockWidth;
2250                params.blocksHigh =
2251                    static_cast<GLuint>(mtlArea.size.height) / internalFormat.compressedBlockHeight;
2252
2253                // PVRTC1 textures always have at least 2 blocks in each dimension.
2254                // Enforce correct block layout for 8x8 textures that use 8x4 blocks.
2255                params.blocksWide = std::max(params.blocksWide, 2u);
2256
2257                ANGLE_TRY(contextMtl->getDisplay()->getUtils().linearizeBlocks(contextMtl, params));
2258
2259                sourceBuffer = stagingBuffer;
2260                offset       = 0;
2261            }
2262            else if (pixelsAngleFormat.id == angle::FormatID::D32_FLOAT)
2263            {
2264                // Current command buffer implementation does not support 64-bit offsets.
2265                ANGLE_MTL_CHECK(contextMtl, offset <= std::numeric_limits<uint32_t>::max(),
2266                                GL_INVALID_OPERATION);
2267
2268                mtl::BufferRef stagingBuffer;
2269                ANGLE_TRY(
2270                    mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer));
2271
2272                ASSERT(pixelsAngleFormat.pixelBytes == 4 && offset % 4 == 0);
2273                ANGLE_TRY(SaturateDepth(contextMtl, sourceBuffer, stagingBuffer,
2274                                        static_cast<uint32_t>(offset),
2275                                        static_cast<uint32_t>(pixelsRowPitch) / 4, mtlArea.size));
2276
2277                sourceBuffer = stagingBuffer;
2278                offset       = 0;
2279            }
2280
2281            // Use blit encoder to copy
2282            mtl::BlitCommandEncoder *blitEncoder =
2283                GetBlitCommandEncoderForResources(contextMtl, {sourceBuffer.get(), image.get()});
2284            CopyBufferToOriginalTextureIfDstIsAView(
2285                contextMtl, blitEncoder, sourceBuffer, offset, pixelsRowPitch, pixelsDepthPitch,
2286                mtlArea.size, image, slice, mtl::kZeroNativeMipLevel, mtlArea.origin,
2287                mFormat.isPVRTC() ? mtl::kBlitOptionRowLinearPVRTC : MTLBlitOptionNone);
2288        }
2289    }
2290    else
2291    {
2292        ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea,
2293                                        mtl::kZeroNativeMipLevel, slice, pixels, pixelsRowPitch,
2294                                        pixelsDepthPitch, false, image));
2295    }
2296    return angle::Result::Continue;
2297}
2298
2299angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *context,
2300                                                        int slice,
2301                                                        const MTLRegion &mtlArea,
2302                                                        const gl::InternalFormat &internalFormat,
2303                                                        GLenum type,
2304                                                        const angle::Format &pixelsAngleFormat,
2305                                                        size_t pixelsRowPitch,
2306                                                        size_t pixelsDepthPitch,
2307                                                        gl::Buffer *unpackBuffer,
2308                                                        const uint8_t *pixels,
2309                                                        const mtl::TextureRef &image)
2310{
2311    ASSERT(image && image->valid());
2312
2313    ContextMtl *contextMtl = mtl::GetImpl(context);
2314
2315    if (unpackBuffer)
2316    {
2317        ANGLE_MTL_CHECK(contextMtl,
2318                        reinterpret_cast<uintptr_t>(pixels) <= std::numeric_limits<uint32_t>::max(),
2319                        GL_INVALID_OPERATION);
2320
2321        uint32_t offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pixels));
2322
2323        BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
2324        if (!mFormat.getCaps().writable || mFormat.hasDepthOrStencilBits() ||
2325            mFormat.intendedAngleFormat().isBlock)
2326        {
2327            // Unsupported format, use CPU path.
2328            const uint8_t *clientData = unpackBufferMtl->getBufferDataReadOnly(contextMtl);
2329            clientData += offset;
2330            ANGLE_TRY(convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
2331                                                    pixelsAngleFormat, pixelsRowPitch,
2332                                                    pixelsDepthPitch, nullptr, clientData, image));
2333        }
2334        else
2335        {
2336            // Use compute shader
2337            mtl::CopyPixelsFromBufferParams params;
2338            params.buffer            = unpackBufferMtl->getCurrentBuffer();
2339            params.bufferStartOffset = offset;
2340            params.bufferRowPitch    = static_cast<uint32_t>(pixelsRowPitch);
2341            params.bufferDepthPitch  = static_cast<uint32_t>(pixelsDepthPitch);
2342            params.texture           = image;
2343            params.textureArea       = mtl::MTLRegionToGLBox(mtlArea);
2344
2345            // If texture is not array, slice must be zero, if texture is array, mtlArea.origin.z
2346            // must be zero.
2347            // This is because this function uses Metal convention: where slice is only used for
2348            // array textures, and z layer of mtlArea.origin is only used for 3D textures.
2349            ASSERT(slice == 0 || params.textureArea.z == 0);
2350
2351            // For mtl::RenderUtils we convert to OpenGL convention: z layer is used as either array
2352            // texture's slice or 3D texture's layer index.
2353            params.textureArea.z += slice;
2354
2355            ANGLE_TRY(contextMtl->getDisplay()->getUtils().unpackPixelsFromBufferToTexture(
2356                contextMtl, pixelsAngleFormat, params));
2357        }
2358    }  // if (unpackBuffer)
2359    else
2360    {
2361        LoadImageFunctionInfo loadFunctionInfo = mFormat.textureLoadFunctions
2362                                                     ? mFormat.textureLoadFunctions(type)
2363                                                     : LoadImageFunctionInfo();
2364        const angle::Format &dstFormat         = angle::Format::Get(mFormat.actualFormatId);
2365        const size_t dstRowPitch               = dstFormat.pixelBytes * mtlArea.size.width;
2366
2367        // It is very important to avoid allocating a new buffer for each row during these
2368        // uploads.
2369        const bool kAvoidStagingBuffers = true;
2370
2371        // Check if original image data is compressed:
2372        if (mFormat.intendedAngleFormat().isBlock)
2373        {
2374            if (mFormat.intendedFormatId != mFormat.actualFormatId)
2375            {
2376                ASSERT(loadFunctionInfo.loadFunction);
2377
2378                // Need to create a buffer to hold entire decompressed image.
2379                const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
2380                angle::MemoryBuffer decompressBuf;
2381                ANGLE_CHECK_GL_ALLOC(contextMtl,
2382                                     decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
2383
2384                // Decompress
2385                loadFunctionInfo.loadFunction(contextMtl->getImageLoadContext(), mtlArea.size.width,
2386                                              mtlArea.size.height, mtlArea.size.depth, pixels,
2387                                              pixelsRowPitch, pixelsDepthPitch,
2388                                              decompressBuf.data(), dstRowPitch, dstDepthPitch);
2389
2390                // Upload to texture
2391                ANGLE_TRY(UploadTextureContents(
2392                    context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice,
2393                    decompressBuf.data(), dstRowPitch, dstDepthPitch, kAvoidStagingBuffers, image));
2394            }
2395            else
2396            {
2397                // Assert that we're filling the level in it's entierety.
2398                ASSERT(mtlArea.size.width == static_cast<unsigned int>(image->sizeAt0().width));
2399                ASSERT(mtlArea.size.height == static_cast<unsigned int>(image->sizeAt0().height));
2400                const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
2401                ANGLE_TRY(UploadTextureContents(
2402                    context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice, pixels,
2403                    dstRowPitch, dstDepthPitch, kAvoidStagingBuffers, image));
2404            }
2405        }  // if (mFormat.intendedAngleFormat().isBlock)
2406        else
2407        {
2408            // Create scratch row buffer
2409            angle::MemoryBuffer conversionRow;
2410            ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
2411
2412            // Convert row by row:
2413            MTLRegion mtlRow   = mtlArea;
2414            mtlRow.size.height = mtlRow.size.depth = 1;
2415            for (NSUInteger d = 0; d < mtlArea.size.depth; ++d)
2416            {
2417                mtlRow.origin.z = mtlArea.origin.z + d;
2418                for (NSUInteger r = 0; r < mtlArea.size.height; ++r)
2419                {
2420                    const uint8_t *psrc = pixels + d * pixelsDepthPitch + r * pixelsRowPitch;
2421                    mtlRow.origin.y     = mtlArea.origin.y + r;
2422
2423                    // Convert pixels
2424                    if (loadFunctionInfo.loadFunction)
2425                    {
2426                        loadFunctionInfo.loadFunction(contextMtl->getImageLoadContext(),
2427                                                      mtlRow.size.width, 1, 1, psrc, pixelsRowPitch,
2428                                                      0, conversionRow.data(), dstRowPitch, 0);
2429                    }
2430                    else if (mFormat.hasDepthOrStencilBits())
2431                    {
2432                        ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0,
2433                                                psrc, dstFormat, nullptr, dstRowPitch, 0,
2434                                                conversionRow.data());
2435                    }
2436                    else
2437                    {
2438                        CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsAngleFormat.pixelBytes, 0,
2439                                          pixelsAngleFormat.pixelReadFunction, conversionRow.data(),
2440                                          dstRowPitch, dstFormat.pixelBytes, 0,
2441                                          dstFormat.pixelWriteFunction, internalFormat.format,
2442                                          dstFormat.componentType, mtlRow.size.width, 1, 1, false,
2443                                          false, false);
2444                    }
2445
2446                    // Upload to texture
2447                    ANGLE_TRY(UploadTextureContents(
2448                        context, dstFormat, mtlRow, mtl::kZeroNativeMipLevel, slice,
2449                        conversionRow.data(), dstRowPitch, 0, kAvoidStagingBuffers, image));
2450                }
2451            }
2452        }  // if (mFormat.intendedAngleFormat().isBlock)
2453    }      // if (unpackBuffer)
2454
2455    return angle::Result::Continue;
2456}
2457
2458angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
2459                                                   const mtl::Format &mtlFormat,
2460                                                   const mtl::TextureRef &texture)
2461{
2462    bool emulatedChannels = mtl::IsFormatEmulated(mtlFormat);
2463
2464    // For emulated channels that GL texture intends to not have,
2465    // we need to initialize their content.
2466    if (emulatedChannels)
2467    {
2468        uint32_t mipmaps = texture->mipmapLevels();
2469
2470        uint32_t layers = texture->cubeFacesOrArrayLength();
2471        for (uint32_t layer = 0; layer < layers; ++layer)
2472        {
2473            for (uint32_t mip = 0; mip < mipmaps; ++mip)
2474            {
2475                auto index = mtl::ImageNativeIndex::FromBaseZeroGLIndex(
2476                    GetCubeOrArraySliceMipIndex(texture, layer, mip));
2477
2478                ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mtlFormat, index));
2479            }
2480        }
2481    }
2482    return angle::Result::Continue;
2483}
2484
2485angle::Result TextureMtl::initializeContents(const gl::Context *context,
2486                                             GLenum binding,
2487                                             const gl::ImageIndex &index)
2488{
2489    if (index.isLayered())
2490    {
2491        // InitializeTextureContents is only able to initialize one layer at a time.
2492        const gl::ImageDesc &desc = mState.getImageDesc(index);
2493        uint32_t layerCount;
2494        if (index.isEntireLevelCubeMap())
2495        {
2496            layerCount = 6;
2497        }
2498        else
2499        {
2500            layerCount = desc.size.depth;
2501        }
2502
2503        gl::ImageIndexIterator ite = index.getLayerIterator(layerCount);
2504        while (ite.hasNext())
2505        {
2506            gl::ImageIndex layerIndex = ite.next();
2507            ANGLE_TRY(initializeContents(context, GL_NONE, layerIndex));
2508        }
2509        return angle::Result::Continue;
2510    }
2511    else if (index.getLayerCount() > 1)
2512    {
2513        for (int layer = 0; layer < index.getLayerCount(); ++layer)
2514        {
2515            int layerIdx = layer + index.getLayerIndex();
2516            gl::ImageIndex layerIndex =
2517                gl::ImageIndex::MakeFromType(index.getType(), index.getLevelIndex(), layerIdx);
2518            ANGLE_TRY(initializeContents(context, GL_NONE, layerIndex));
2519        }
2520        return angle::Result::Continue;
2521    }
2522
2523    ASSERT(index.getLayerCount() == 1 && !index.isLayered());
2524    ANGLE_TRY(ensureImageCreated(context, index));
2525    ContextMtl *contextMtl       = mtl::GetImpl(context);
2526    ImageDefinitionMtl &imageDef = getImageDefinition(index);
2527    const mtl::TextureRef &image = imageDef.image;
2528    const mtl::Format &format    = contextMtl->getPixelFormat(imageDef.formatID);
2529    // For Texture's image definition, we always use zero mip level.
2530    if (format.metalFormat == MTLPixelFormatInvalid)
2531    {
2532        return angle::Result::Stop;
2533    }
2534    return mtl::InitializeTextureContents(
2535        context, image, format,
2536        mtl::ImageNativeIndex::FromBaseZeroGLIndex(
2537            GetLayerMipIndex(image, GetImageLayerIndexFrom(index), /** level */ 0)));
2538}
2539
2540angle::Result TextureMtl::copySubImageImpl(const gl::Context *context,
2541                                           const gl::ImageIndex &index,
2542                                           const gl::Offset &destOffset,
2543                                           const gl::Rectangle &sourceArea,
2544                                           const gl::InternalFormat &internalFormat,
2545                                           const FramebufferMtl *source,
2546                                           const RenderTargetMtl *colorReadRT)
2547{
2548    if (!colorReadRT || !colorReadRT->getTexture())
2549    {
2550        // Is this an error?
2551        return angle::Result::Continue;
2552    }
2553
2554    gl::Extents fbSize = colorReadRT->getTexture()->size(colorReadRT->getLevelIndex());
2555    gl::Rectangle clippedSourceArea;
2556    if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height),
2557                       &clippedSourceArea))
2558    {
2559        return angle::Result::Continue;
2560    }
2561
2562    // If negative offsets are given, clippedSourceArea ensures we don't read from those offsets.
2563    // However, that changes the sourceOffset->destOffset mapping.  Here, destOffset is shifted by
2564    // the same amount as clipped to correct the error.
2565    const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x,
2566                                        destOffset.y + clippedSourceArea.y - sourceArea.y, 0);
2567
2568    ANGLE_TRY(ensureImageCreated(context, index));
2569
2570    if (!mFormat.getCaps().isRenderable())
2571    {
2572        return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea,
2573                               internalFormat, source, colorReadRT);
2574    }
2575
2576    // NOTE(hqle): Use compute shader.
2577    return copySubImageWithDraw(context, index, modifiedDestOffset, clippedSourceArea,
2578                                internalFormat, source, colorReadRT);
2579}
2580
2581angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
2582                                               const gl::ImageIndex &index,
2583                                               const gl::Offset &modifiedDestOffset,
2584                                               const gl::Rectangle &clippedSourceArea,
2585                                               const gl::InternalFormat &internalFormat,
2586                                               const FramebufferMtl *source,
2587                                               const RenderTargetMtl *colorReadRT)
2588{
2589    ContextMtl *contextMtl = mtl::GetImpl(context);
2590    DisplayMtl *displayMtl = contextMtl->getDisplay();
2591
2592    const RenderTargetMtl &imageRtt = getRenderTarget(index);
2593
2594    mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(imageRtt);
2595    mtl::ColorBlitParams blitParams;
2596
2597    blitParams.dstTextureSize = imageRtt.getTexture()->size(imageRtt.getLevelIndex());
2598    blitParams.dstRect        = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y,
2599                                              clippedSourceArea.width, clippedSourceArea.height);
2600    blitParams.dstScissorRect = blitParams.dstRect;
2601
2602    blitParams.enabledBuffers.set(0);
2603
2604    blitParams.src      = colorReadRT->getTexture();
2605    blitParams.srcLevel = colorReadRT->getLevelIndex();
2606    blitParams.srcLayer = colorReadRT->getLayerIndex();
2607
2608    blitParams.srcNormalizedCoords = mtl::NormalizedCoords(
2609        clippedSourceArea, colorReadRT->getTexture()->size(blitParams.srcLevel));
2610    blitParams.srcYFlipped  = source->flipY();
2611    blitParams.dstLuminance = internalFormat.isLUMA();
2612
2613    return displayMtl->getUtils().blitColorWithDraw(
2614        context, cmdEncoder, colorReadRT->getFormat().actualAngleFormat(), blitParams);
2615}
2616
2617angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
2618                                          const gl::ImageIndex &index,
2619                                          const gl::Offset &modifiedDestOffset,
2620                                          const gl::Rectangle &clippedSourceArea,
2621                                          const gl::InternalFormat &internalFormat,
2622                                          const FramebufferMtl *source,
2623                                          const RenderTargetMtl *colorReadRT)
2624{
2625    mtl::TextureRef &image = getImage(index);
2626    ASSERT(image && image->valid());
2627
2628    ContextMtl *contextMtl = mtl::GetImpl(context);
2629
2630    const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
2631    const int dstRowPitch          = dstFormat.pixelBytes * clippedSourceArea.width;
2632    angle::MemoryBuffer conversionRow;
2633    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
2634
2635    gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1);
2636    MTLRegion mtlDstRowArea  = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1);
2637    uint32_t dstSlice        = 0;
2638    switch (index.getType())
2639    {
2640        case gl::TextureType::_2D:
2641        case gl::TextureType::CubeMap:
2642            dstSlice = 0;
2643            break;
2644        case gl::TextureType::_2DArray:
2645            ASSERT(index.hasLayer());
2646            dstSlice = index.getLayerIndex();
2647            break;
2648        case gl::TextureType::_3D:
2649            ASSERT(index.hasLayer());
2650            dstSlice               = 0;
2651            mtlDstRowArea.origin.z = index.getLayerIndex();
2652            break;
2653        default:
2654            UNREACHABLE();
2655    }
2656
2657    // It is very important to avoid allocating a new buffer for each row during these
2658    // uploads.
2659    const bool kAvoidStagingBuffers = true;
2660
2661    // Copy row by row:
2662    for (int r = 0; r < clippedSourceArea.height; ++r)
2663    {
2664        mtlDstRowArea.origin.y = modifiedDestOffset.y + r;
2665        srcRowArea.y           = clippedSourceArea.y + r;
2666
2667        PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0);
2668
2669        // Read pixels from framebuffer to memory:
2670        gl::Rectangle flippedSrcRowArea = source->getCorrectFlippedReadArea(context, srcRowArea);
2671        ANGLE_TRY(source->readPixelsImpl(context, flippedSrcRowArea, packParams, colorReadRT,
2672                                         conversionRow.data()));
2673
2674        // Upload to texture
2675        ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlDstRowArea, mtl::kZeroNativeMipLevel,
2676                                        dstSlice, conversionRow.data(), dstRowPitch, 0,
2677                                        kAvoidStagingBuffers, image));
2678    }
2679
2680    return angle::Result::Continue;
2681}
2682
2683angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context,
2684                                             const gl::ImageIndex &index,
2685                                             const gl::Offset &destOffset,
2686                                             const gl::InternalFormat &internalFormat,
2687                                             GLint sourceLevel,
2688                                             const gl::Box &sourceBox,
2689                                             bool unpackFlipY,
2690                                             bool unpackPremultiplyAlpha,
2691                                             bool unpackUnmultiplyAlpha,
2692                                             const gl::Texture *source)
2693{
2694    // Only 2D textures are supported.
2695    ASSERT(sourceBox.depth == 1);
2696    ASSERT(source->getType() == gl::TextureType::_2D);
2697    gl::ImageIndex sourceIndex = gl::ImageIndex::Make2D(sourceLevel);
2698
2699    ContextMtl *contextMtl = mtl::GetImpl(context);
2700    TextureMtl *sourceMtl  = mtl::GetImpl(source);
2701
2702    ANGLE_TRY(ensureImageCreated(context, index));
2703
2704    ANGLE_TRY(sourceMtl->ensureImageCreated(context, sourceIndex));
2705
2706    const ImageDefinitionMtl &srcImageDef  = sourceMtl->getImageDefinition(sourceIndex);
2707    const mtl::TextureRef &sourceImage     = srcImageDef.image;
2708    const mtl::Format &sourceFormat        = contextMtl->getPixelFormat(srcImageDef.formatID);
2709    const angle::Format &sourceAngleFormat = sourceFormat.actualAngleFormat();
2710
2711    if (!mFormat.getCaps().isRenderable())
2712    {
2713        return copySubTextureCPU(context, index, destOffset, internalFormat,
2714                                 mtl::kZeroNativeMipLevel, sourceBox, sourceAngleFormat,
2715                                 unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
2716                                 sourceImage);
2717    }
2718    return copySubTextureWithDraw(
2719        context, index, destOffset, internalFormat, mtl::kZeroNativeMipLevel, sourceBox,
2720        sourceAngleFormat, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, sourceImage);
2721}
2722
2723angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
2724                                                 const gl::ImageIndex &index,
2725                                                 const gl::Offset &destOffset,
2726                                                 const gl::InternalFormat &internalFormat,
2727                                                 const mtl::MipmapNativeLevel &sourceNativeLevel,
2728                                                 const gl::Box &sourceBox,
2729                                                 const angle::Format &sourceAngleFormat,
2730                                                 bool unpackFlipY,
2731                                                 bool unpackPremultiplyAlpha,
2732                                                 bool unpackUnmultiplyAlpha,
2733                                                 const mtl::TextureRef &sourceTexture)
2734{
2735    ContextMtl *contextMtl = mtl::GetImpl(context);
2736    DisplayMtl *displayMtl = contextMtl->getDisplay();
2737
2738    mtl::TextureRef image = getImage(index);
2739    ASSERT(image && image->valid());
2740
2741    if (internalFormat.colorEncoding == GL_SRGB)
2742    {
2743        image = image->getLinearColorView();
2744    }
2745
2746    mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getTextureRenderCommandEncoder(
2747        image, mtl::ImageNativeIndex::FromBaseZeroGLIndex(GetZeroLevelIndex(image)));
2748    mtl::ColorBlitParams blitParams;
2749
2750    blitParams.dstTextureSize = image->sizeAt0();
2751    blitParams.dstRect =
2752        gl::Rectangle(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
2753    blitParams.dstScissorRect = blitParams.dstRect;
2754
2755    blitParams.enabledBuffers.set(0);
2756
2757    blitParams.src      = sourceTexture;
2758    blitParams.srcLevel = sourceNativeLevel;
2759    blitParams.srcLayer = 0;
2760    blitParams.srcNormalizedCoords =
2761        mtl::NormalizedCoords(sourceBox.toRect(), sourceTexture->size(sourceNativeLevel));
2762    blitParams.srcYFlipped            = false;
2763    blitParams.dstLuminance           = internalFormat.isLUMA();
2764    blitParams.unpackFlipY            = unpackFlipY;
2765    blitParams.unpackPremultiplyAlpha = unpackPremultiplyAlpha;
2766    blitParams.unpackUnmultiplyAlpha  = unpackUnmultiplyAlpha;
2767    blitParams.transformLinearToSrgb  = sourceAngleFormat.isSRGB;
2768
2769    return displayMtl->getUtils().copyTextureWithDraw(context, cmdEncoder, sourceAngleFormat,
2770                                                      mFormat.actualAngleFormat(), blitParams);
2771}
2772
2773angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
2774                                            const gl::ImageIndex &index,
2775                                            const gl::Offset &destOffset,
2776                                            const gl::InternalFormat &internalFormat,
2777                                            const mtl::MipmapNativeLevel &sourceNativeLevel,
2778                                            const gl::Box &sourceBox,
2779                                            const angle::Format &sourceAngleFormat,
2780                                            bool unpackFlipY,
2781                                            bool unpackPremultiplyAlpha,
2782                                            bool unpackUnmultiplyAlpha,
2783                                            const mtl::TextureRef &sourceTexture)
2784{
2785    mtl::TextureRef &image = getImage(index);
2786    ASSERT(image && image->valid());
2787
2788    ContextMtl *contextMtl = mtl::GetImpl(context);
2789
2790    const angle::Format &dstAngleFormat = mFormat.actualAngleFormat();
2791    const int srcRowPitch               = sourceAngleFormat.pixelBytes * sourceBox.width;
2792    const int srcImageSize              = srcRowPitch * sourceBox.height;
2793    const int convRowPitch              = dstAngleFormat.pixelBytes * sourceBox.width;
2794    const int convImageSize             = convRowPitch * sourceBox.height;
2795    angle::MemoryBuffer conversionSrc, conversionDst;
2796    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionSrc.resize(srcImageSize));
2797    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionDst.resize(convImageSize));
2798
2799    MTLRegion mtlSrcArea =
2800        MTLRegionMake2D(sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height);
2801    MTLRegion mtlDstArea =
2802        MTLRegionMake2D(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
2803
2804    // Read pixels from source to memory:
2805    sourceTexture->getBytes(contextMtl, srcRowPitch, 0, mtlSrcArea, sourceNativeLevel, 0,
2806                            conversionSrc.data());
2807
2808    // Convert to destination format
2809    CopyImageCHROMIUM(conversionSrc.data(), srcRowPitch, sourceAngleFormat.pixelBytes, 0,
2810                      sourceAngleFormat.pixelReadFunction, conversionDst.data(), convRowPitch,
2811                      dstAngleFormat.pixelBytes, 0, dstAngleFormat.pixelWriteFunction,
2812                      internalFormat.format, internalFormat.componentType, sourceBox.width,
2813                      sourceBox.height, 1, unpackFlipY, unpackPremultiplyAlpha,
2814                      unpackUnmultiplyAlpha);
2815
2816    // Upload to texture
2817    ANGLE_TRY(UploadTextureContents(context, dstAngleFormat, mtlDstArea, mtl::kZeroNativeMipLevel,
2818                                    0, conversionDst.data(), convRowPitch, 0, false, image));
2819
2820    return angle::Result::Continue;
2821}
2822
2823}  // namespace rx
2824