• 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 "common/Color.h"
14#include "common/MemoryBuffer.h"
15#include "common/debug.h"
16#include "common/mathutil.h"
17#include "image_util/imageformats.h"
18#include "libANGLE/Surface.h"
19#include "libANGLE/renderer/metal/BufferMtl.h"
20#include "libANGLE/renderer/metal/ContextMtl.h"
21#include "libANGLE/renderer/metal/DisplayMtl.h"
22#include "libANGLE/renderer/metal/FrameBufferMtl.h"
23#include "libANGLE/renderer/metal/ImageMtl.h"
24#include "libANGLE/renderer/metal/SamplerMtl.h"
25#include "libANGLE/renderer/metal/SurfaceMtl.h"
26#include "libANGLE/renderer/metal/mtl_common.h"
27#include "libANGLE/renderer/metal/mtl_format_utils.h"
28#include "libANGLE/renderer/metal/mtl_utils.h"
29#include "libANGLE/renderer/renderer_utils.h"
30
31namespace rx
32{
33
34namespace
35{
36
37gl::ImageIndex GetZeroLevelIndex(const mtl::TextureRef &image)
38{
39    switch (image->textureType())
40    {
41        case MTLTextureType2D:
42            return gl::ImageIndex::Make2D(0);
43        case MTLTextureTypeCube:
44            return gl::ImageIndex::MakeFromType(gl::TextureType::CubeMap, 0);
45        case MTLTextureType2DArray:
46            return gl::ImageIndex::Make2DArray(0 /** entire layers */);
47        case MTLTextureType2DMultisample:
48            return gl::ImageIndex::Make2DMultisample();
49        case MTLTextureType3D:
50            return gl::ImageIndex::Make3D(0 /** entire layers */);
51        default:
52            UNREACHABLE();
53            break;
54    }
55
56    return gl::ImageIndex();
57}
58
59// Slice is ignored if texture type is not Cube or 2D array
60gl::ImageIndex GetCubeOrArraySliceMipIndex(const mtl::TextureRef &image,
61                                           uint32_t slice,
62                                           uint32_t level)
63{
64    switch (image->textureType())
65    {
66        case MTLTextureType2D:
67            return gl::ImageIndex::Make2D(level);
68        case MTLTextureTypeCube:
69        {
70            auto cubeFace = static_cast<gl::TextureTarget>(
71                static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + slice);
72            return gl::ImageIndex::MakeCubeMapFace(cubeFace, level);
73        }
74        case MTLTextureType2DArray:
75            return gl::ImageIndex::Make2DArray(level, slice);
76        case MTLTextureType2DMultisample:
77            return gl::ImageIndex::Make2DMultisample();
78        case MTLTextureType3D:
79            return gl::ImageIndex::Make3D(level);
80        default:
81            UNREACHABLE();
82            break;
83    }
84
85    return gl::ImageIndex();
86}
87
88// layer is ignored if texture type is not Cube or 2D array or 3D
89gl::ImageIndex GetLayerMipIndex(const mtl::TextureRef &image, uint32_t layer, uint32_t level)
90{
91    switch (image->textureType())
92    {
93        case MTLTextureType2D:
94            return gl::ImageIndex::Make2D(level);
95        case MTLTextureTypeCube:
96        {
97            auto cubeFace = static_cast<gl::TextureTarget>(
98                static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + layer);
99            return gl::ImageIndex::MakeCubeMapFace(cubeFace, level);
100        }
101        case MTLTextureType2DArray:
102            return gl::ImageIndex::Make2DArray(level, layer);
103        case MTLTextureType2DMultisample:
104            return gl::ImageIndex::Make2DMultisample();
105        case MTLTextureType3D:
106            return gl::ImageIndex::Make3D(level, layer);
107        default:
108            UNREACHABLE();
109            break;
110    }
111
112    return gl::ImageIndex();
113}
114
115GLuint GetImageLayerIndexFrom(const gl::ImageIndex &index)
116{
117    switch (index.getType())
118    {
119        case gl::TextureType::_2D:
120        case gl::TextureType::_2DMultisample:
121        case gl::TextureType::Rectangle:
122            return 0;
123        case gl::TextureType::CubeMap:
124            return index.cubeMapFaceIndex();
125        case gl::TextureType::_2DArray:
126        case gl::TextureType::_3D:
127            return index.getLayerIndex();
128        default:
129            UNREACHABLE();
130    }
131
132    return 0;
133}
134
135GLuint GetImageCubeFaceIndexOrZeroFrom(const gl::ImageIndex &index)
136{
137    switch (index.getType())
138    {
139        case gl::TextureType::CubeMap:
140            return index.cubeMapFaceIndex();
141        default:
142            break;
143    }
144
145    return 0;
146}
147
148// Given texture type, get texture type of one image for a glTexImage call.
149// For example, for texture 2d, one image is also texture 2d.
150// for texture cube, one image is texture 2d.
151gl::TextureType GetTextureImageType(gl::TextureType texType)
152{
153    switch (texType)
154    {
155        case gl::TextureType::CubeMap:
156            return gl::TextureType::_2D;
157        case gl::TextureType::_2D:
158        case gl::TextureType::_2DArray:
159        case gl::TextureType::_2DMultisample:
160        case gl::TextureType::_3D:
161        case gl::TextureType::Rectangle:
162            return texType;
163        default:
164            UNREACHABLE();
165            return gl::TextureType::InvalidEnum;
166    }
167}
168
169// D24X8 by default writes depth data to high 24 bits of 32 bit integers. However, Metal separate
170// depth stencil blitting expects depth data to be in low 24 bits of the data.
171void WriteDepthStencilToDepth24(const uint8_t *srcPtr, uint8_t *dstPtr)
172{
173    auto src = reinterpret_cast<const angle::DepthStencil *>(srcPtr);
174    auto dst = reinterpret_cast<uint32_t *>(dstPtr);
175    *dst     = gl::floatToNormalized<24, uint32_t>(static_cast<float>(src->depth));
176}
177
178#if TARGET_OS_SIMULATOR
179void CopyTextureData(const MTLSize &regionSize,
180                     size_t srcRowPitch,
181                     size_t src2DImageSize,
182                     const uint8_t *psrc,
183                     size_t destRowPitch,
184                     size_t dest2DImageSize,
185                     uint8_t *pdst)
186{
187    {
188        size_t rowCopySize = std::min(srcRowPitch, destRowPitch);
189        for (NSUInteger d = 0; d < regionSize.depth; ++d)
190        {
191            for (NSUInteger r = 0; r < regionSize.height; ++r)
192            {
193                const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch;
194                uint8_t *pCopyDst       = pdst + d * dest2DImageSize + r * destRowPitch;
195                memcpy(pCopyDst, pCopySrc, rowCopySize);
196            }
197        }
198    }
199}
200#endif  // TARGET_OS_SIMULATOR
201
202void ConvertDepthStencilData(const MTLSize &regionSize,
203                             const angle::Format &srcAngleFormat,
204                             size_t srcRowPitch,
205                             size_t src2DImageSize,
206                             const uint8_t *psrc,
207                             const angle::Format &dstAngleFormat,
208                             rx::PixelWriteFunction pixelWriteFunctionOverride,
209                             size_t destRowPitch,
210                             size_t dest2DImageSize,
211                             uint8_t *pdst)
212{
213    if (srcAngleFormat.id == dstAngleFormat.id)
214    {
215        size_t rowCopySize = std::min(srcRowPitch, destRowPitch);
216        for (NSUInteger d = 0; d < regionSize.depth; ++d)
217        {
218            for (NSUInteger r = 0; r < regionSize.height; ++r)
219            {
220                const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch;
221                uint8_t *pCopyDst       = pdst + d * dest2DImageSize + r * destRowPitch;
222                memcpy(pCopyDst, pCopySrc, rowCopySize);
223            }
224        }
225    }
226    else
227    {
228        rx::PixelWriteFunction pixelWriteFunction = pixelWriteFunctionOverride
229                                                        ? pixelWriteFunctionOverride
230                                                        : dstAngleFormat.pixelWriteFunction;
231        // This is only for depth & stencil case.
232        ASSERT(srcAngleFormat.depthBits || srcAngleFormat.stencilBits);
233        ASSERT(srcAngleFormat.pixelReadFunction && pixelWriteFunction);
234
235        // cache to store read result of source pixel
236        angle::DepthStencil depthStencilData;
237        auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData);
238        ASSERT(srcAngleFormat.pixelBytes <= sizeof(depthStencilData));
239
240        for (NSUInteger d = 0; d < regionSize.depth; ++d)
241        {
242            for (NSUInteger r = 0; r < regionSize.height; ++r)
243            {
244                for (NSUInteger c = 0; c < regionSize.width; ++c)
245                {
246                    const uint8_t *sourcePixelData =
247                        psrc + d * src2DImageSize + r * srcRowPitch + c * srcAngleFormat.pixelBytes;
248
249                    uint8_t *destPixelData = pdst + d * dest2DImageSize + r * destRowPitch +
250                                             c * dstAngleFormat.pixelBytes;
251
252                    srcAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData);
253                    pixelWriteFunction(sourcePixelReadData, destPixelData);
254                }
255            }
256        }
257    }
258}
259
260angle::Result CopyDepthStencilTextureContentsToStagingBuffer(
261    ContextMtl *contextMtl,
262    const angle::Format &textureAngleFormat,
263    const angle::Format &stagingAngleFormat,
264    rx::PixelWriteFunction pixelWriteFunctionOverride,
265    const MTLSize &regionSize,
266    const uint8_t *data,
267    size_t bytesPerRow,
268    size_t bytesPer2DImage,
269    size_t *bufferRowPitchOut,
270    size_t *buffer2DImageSizeOut,
271    mtl::BufferRef *bufferOut)
272{
273    size_t stagingBufferRowPitch    = regionSize.width * stagingAngleFormat.pixelBytes;
274    size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height;
275    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSize.depth;
276    mtl::BufferRef stagingBuffer;
277    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
278
279    uint8_t *pdst = stagingBuffer->map(contextMtl);
280
281    ConvertDepthStencilData(regionSize, textureAngleFormat, bytesPerRow, bytesPer2DImage, data,
282                            stagingAngleFormat, pixelWriteFunctionOverride, stagingBufferRowPitch,
283                            stagingBuffer2DImageSize, pdst);
284
285    stagingBuffer->unmap(contextMtl);
286
287    *bufferOut            = stagingBuffer;
288    *bufferRowPitchOut    = stagingBufferRowPitch;
289    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
290
291    return angle::Result::Continue;
292}
293
294#if TARGET_OS_SIMULATOR
295angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl,
296                                                 const angle::Format &textureAngleFormat,
297                                                 const MTLSize &regionSize,
298                                                 const uint8_t *data,
299                                                 size_t bytesPerRow,
300                                                 size_t bytesPer2DImage,
301                                                 size_t *bufferRowPitchOut,
302                                                 size_t *buffer2DImageSizeOut,
303                                                 mtl::BufferRef *bufferOut)
304{
305    size_t stagingBufferRowPitch    = regionSize.width * textureAngleFormat.pixelBytes;
306    size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height;
307    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSize.depth;
308    mtl::BufferRef stagingBuffer;
309    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
310
311    uint8_t *pdst = stagingBuffer->map(contextMtl);
312    CopyTextureData(regionSize, bytesPerRow, bytesPer2DImage, data, stagingBufferRowPitch,
313                    stagingBuffer2DImageSize, pdst);
314
315    stagingBuffer->unmap(contextMtl);
316
317    *bufferOut            = stagingBuffer;
318    *bufferRowPitchOut    = stagingBufferRowPitch;
319    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
320
321    return angle::Result::Continue;
322}
323
324angle::Result CopyCompressedTextureContentsToStagingBuffer(ContextMtl *contextMtl,
325                                                           const angle::Format &textureAngleFormat,
326                                                           const MTLSize &regionSizeInBlocks,
327                                                           const uint8_t *data,
328                                                           size_t bytesPerBlockRow,
329                                                           size_t bytesPer2DImage,
330                                                           size_t *bufferRowPitchOut,
331                                                           size_t *buffer2DImageSizeOut,
332                                                           mtl::BufferRef *bufferOut)
333{
334    size_t stagingBufferRowPitch    = bytesPerBlockRow;
335    size_t stagingBuffer2DImageSize = bytesPer2DImage;
336    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSizeInBlocks.depth;
337    mtl::BufferRef stagingBuffer;
338    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
339
340    uint8_t *pdst = stagingBuffer->map(contextMtl);
341    CopyTextureData(regionSizeInBlocks, bytesPerBlockRow, bytesPer2DImage, data,
342                    stagingBufferRowPitch, stagingBuffer2DImageSize, pdst);
343
344    stagingBuffer->unmap(contextMtl);
345
346    *bufferOut            = stagingBuffer;
347    *bufferRowPitchOut    = stagingBufferRowPitch;
348    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
349
350    return angle::Result::Continue;
351}
352#endif
353
354angle::Result UploadDepthStencilTextureContentsWithStagingBuffer(
355    ContextMtl *contextMtl,
356    const angle::Format &textureAngleFormat,
357    MTLRegion region,
358    const mtl::MipmapNativeLevel &mipmapLevel,
359    uint32_t slice,
360    const uint8_t *data,
361    size_t bytesPerRow,
362    size_t bytesPer2DImage,
363    const mtl::TextureRef &texture)
364{
365    ASSERT(texture && texture->valid());
366
367    ASSERT(!texture->isCPUAccessible());
368
369    ASSERT(!textureAngleFormat.depthBits || !textureAngleFormat.stencilBits);
370
371    // Compressed texture is not supporte atm
372    ASSERT(!textureAngleFormat.isBlock);
373
374    // Copy data to staging buffer
375    size_t stagingBufferRowPitch;
376    size_t stagingBuffer2DImageSize;
377    mtl::BufferRef stagingBuffer;
378    ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
379        contextMtl, textureAngleFormat, textureAngleFormat, textureAngleFormat.pixelWriteFunction,
380        region.size, data, bytesPerRow, bytesPer2DImage, &stagingBufferRowPitch,
381        &stagingBuffer2DImageSize, &stagingBuffer));
382
383    // Copy staging buffer to texture.
384    mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
385    encoder->copyBufferToTexture(stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize,
386                                 region.size, texture, slice, mipmapLevel, region.origin,
387                                 MTLBlitOptionNone);
388
389    return angle::Result::Continue;
390}
391
392// Packed depth stencil upload using staging buffer
393angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
394    ContextMtl *contextMtl,
395    const angle::Format &textureAngleFormat,
396    MTLRegion region,
397    const mtl::MipmapNativeLevel &mipmapLevel,
398    uint32_t slice,
399    const uint8_t *data,
400    size_t bytesPerRow,
401    size_t bytesPer2DImage,
402    const mtl::TextureRef &texture)
403{
404    ASSERT(texture && texture->valid());
405
406    ASSERT(!texture->isCPUAccessible());
407
408    ASSERT(textureAngleFormat.depthBits && textureAngleFormat.stencilBits);
409
410    // We have to split the depth & stencil data into 2 buffers.
411    angle::FormatID stagingDepthBufferFormatId;
412    angle::FormatID stagingStencilBufferFormatId;
413    // Custom depth write function. We cannot use those in imageformats.cpp since Metal has some
414    // special cases.
415    rx::PixelWriteFunction stagingDepthBufferWriteFunctionOverride = nullptr;
416
417    switch (textureAngleFormat.id)
418    {
419        case angle::FormatID::D24_UNORM_S8_UINT:
420            // D24_UNORM_X8_UINT writes depth data to high 24 bits. But Metal expects depth data to
421            // be in low 24 bits.
422            stagingDepthBufferFormatId              = angle::FormatID::D24_UNORM_X8_UINT;
423            stagingDepthBufferWriteFunctionOverride = WriteDepthStencilToDepth24;
424            stagingStencilBufferFormatId            = angle::FormatID::S8_UINT;
425            break;
426        case angle::FormatID::D32_FLOAT_S8X24_UINT:
427            stagingDepthBufferFormatId   = angle::FormatID::D32_FLOAT;
428            stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
429            break;
430        default:
431            ANGLE_MTL_UNREACHABLE(contextMtl);
432    }
433
434    const angle::Format &angleStagingDepthFormat = angle::Format::Get(stagingDepthBufferFormatId);
435    const angle::Format &angleStagingStencilFormat =
436        angle::Format::Get(stagingStencilBufferFormatId);
437
438    size_t stagingDepthBufferRowPitch, stagingStencilBufferRowPitch;
439    size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize;
440    mtl::BufferRef stagingDepthbuffer, stagingStencilBuffer;
441
442    // Copy depth data to staging depth buffer
443    ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
444        contextMtl, textureAngleFormat, angleStagingDepthFormat,
445        stagingDepthBufferWriteFunctionOverride, region.size, data, bytesPerRow, bytesPer2DImage,
446        &stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthbuffer));
447
448    // Copy stencil data to staging stencil buffer
449    ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer(
450        contextMtl, textureAngleFormat, angleStagingStencilFormat, nullptr, region.size, data,
451        bytesPerRow, bytesPer2DImage, &stagingStencilBufferRowPitch,
452        &stagingStencilBuffer2DImageSize, &stagingStencilBuffer));
453
454    mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
455
456    encoder->copyBufferToTexture(stagingDepthbuffer, 0, stagingDepthBufferRowPitch,
457                                 stagingDepthBuffer2DImageSize, region.size, texture, slice,
458                                 mipmapLevel, region.origin, MTLBlitOptionDepthFromDepthStencil);
459    encoder->copyBufferToTexture(stagingStencilBuffer, 0, stagingStencilBufferRowPitch,
460                                 stagingStencilBuffer2DImageSize, region.size, texture, slice,
461                                 mipmapLevel, region.origin, MTLBlitOptionStencilFromDepthStencil);
462
463    return angle::Result::Continue;
464}
465
466#if TARGET_OS_SIMULATOR
467angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl,
468                                                     const angle::Format &textureAngleFormat,
469                                                     MTLRegion region,
470                                                     const mtl::MipmapNativeLevel &mipmapLevel,
471                                                     uint32_t slice,
472                                                     const uint8_t *data,
473                                                     size_t bytesPerRow,
474                                                     size_t bytesPer2DImage,
475                                                     const mtl::TextureRef &texture)
476{
477    ASSERT(texture && texture->valid());
478
479    angle::FormatID stagingBufferFormatID   = textureAngleFormat.id;
480    const angle::Format &angleStagingFormat = angle::Format::Get(stagingBufferFormatID);
481
482    size_t stagingBufferRowPitch;
483    size_t stagingBuffer2DImageSize;
484    mtl::BufferRef stagingBuffer;
485
486    // Block-compressed formats need a bit of massaging for copy.
487    if (textureAngleFormat.isBlock)
488    {
489        GLenum internalFormat         = textureAngleFormat.glInternalFormat;
490        const gl::InternalFormat &fmt = gl::GetSizedInternalFormatInfo(internalFormat);
491        MTLRegion newRegion           = region;
492        bytesPerRow =
493            (region.size.width + fmt.compressedBlockWidth - 1) / fmt.compressedBlockWidth * 16;
494        bytesPer2DImage = (region.size.height + fmt.compressedBlockHeight - 1) /
495                          fmt.compressedBlockHeight * bytesPerRow;
496        newRegion.size.width =
497            (region.size.width + fmt.compressedBlockWidth - 1) / fmt.compressedBlockWidth;
498        newRegion.size.height =
499            (region.size.height + fmt.compressedBlockHeight - 1) / fmt.compressedBlockHeight;
500        ANGLE_TRY(CopyCompressedTextureContentsToStagingBuffer(
501            contextMtl, angleStagingFormat, newRegion.size, data, bytesPerRow, bytesPer2DImage,
502            &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer));
503    }
504    // Copy to staging buffer before uploading to texture.
505    else
506    {
507        ANGLE_TRY(CopyTextureContentsToStagingBuffer(
508            contextMtl, angleStagingFormat, region.size, data, bytesPerRow, bytesPer2DImage,
509            &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer));
510    }
511    mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
512
513    encoder->copyBufferToTexture(stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize,
514                                 region.size, texture, slice, mipmapLevel, region.origin, 0);
515
516    return angle::Result::Continue;
517}
518#endif  // TARGET_OS_SIMULATOR
519
520angle::Result UploadTextureContents(const gl::Context *context,
521                                    const angle::Format &textureAngleFormat,
522                                    const MTLRegion &region,
523                                    const mtl::MipmapNativeLevel &mipmapLevel,
524                                    uint32_t slice,
525                                    const uint8_t *data,
526                                    size_t bytesPerRow,
527                                    size_t bytesPer2DImage,
528                                    const mtl::TextureRef &texture)
529{
530    ASSERT(texture && texture->valid());
531    ContextMtl *contextMtl = mtl::GetImpl(context);
532
533#if TARGET_OS_SIMULATOR
534    if (!textureAngleFormat.depthBits && !textureAngleFormat.stencilBits)
535    {
536        ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, textureAngleFormat, region,
537                                                         mipmapLevel, slice, data, bytesPerRow,
538                                                         bytesPer2DImage, texture));
539        return angle::Result::Continue;
540    }
541#else
542    if (texture->isCPUAccessible())
543    {
544        // If texture is CPU accessible, just call replaceRegion() directly.
545        texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow,
546                               bytesPer2DImage);
547
548        return angle::Result::Continue;
549    }
550#endif  // TARGET_OS_SIMULATOR
551
552    ASSERT(textureAngleFormat.depthBits || textureAngleFormat.stencilBits);
553
554    // Texture is not CPU accessible, we need to use staging buffer
555    if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits)
556    {
557        ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer(
558            contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow,
559            bytesPer2DImage, texture));
560    }
561    else
562    {
563        ANGLE_TRY(UploadDepthStencilTextureContentsWithStagingBuffer(
564            contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow,
565            bytesPer2DImage, texture));
566    }
567
568    return angle::Result::Continue;
569}
570
571// This might be unused on platform not supporting swizzle.
572ANGLE_MTL_UNUSED
573GLenum OverrideSwizzleValue(const gl::Context *context,
574                            GLenum swizzle,
575                            const mtl::Format &format,
576                            const gl::InternalFormat &glInternalFormat)
577{
578    if (format.actualAngleFormat().depthBits)
579    {
580        ASSERT(!format.swizzled);
581        if (context->getState().getClientMajorVersion() >= 3 && glInternalFormat.sized)
582        {
583            // ES 3.0 spec: treat depth texture as red texture during sampling.
584            if (swizzle == GL_GREEN || swizzle == GL_BLUE)
585            {
586                return GL_NONE;
587            }
588            else if (swizzle == GL_ALPHA)
589            {
590                return GL_ONE;
591            }
592        }
593        else
594        {
595            // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_depth_texture.txt
596            // Treat depth texture as luminance texture during sampling.
597            if (swizzle == GL_GREEN || swizzle == GL_BLUE)
598            {
599                return GL_RED;
600            }
601            else if (swizzle == GL_ALPHA)
602            {
603                return GL_ONE;
604            }
605        }
606    }
607    else if (format.swizzled)
608    {
609        // Combine the swizzles
610        switch (swizzle)
611        {
612            case GL_RED:
613                return format.swizzle[0];
614            case GL_GREEN:
615                return format.swizzle[1];
616            case GL_BLUE:
617                return format.swizzle[2];
618            case GL_ALPHA:
619                return format.swizzle[3];
620            default:
621                break;
622        }
623    }
624
625    return swizzle;
626}
627
628}  // namespace
629
630// TextureMtl implementation
631TextureMtl::TextureMtl(const gl::TextureState &state) : TextureImpl(state) {}
632
633TextureMtl::~TextureMtl() = default;
634
635void TextureMtl::onDestroy(const gl::Context *context)
636{
637    releaseTexture(true);
638}
639
640void TextureMtl::releaseTexture(bool releaseImages)
641{
642    releaseTexture(releaseImages, false);
643}
644
645void TextureMtl::releaseTexture(bool releaseImages, bool releaseTextureObjectsOnly)
646{
647
648    if (releaseImages)
649    {
650        mTexImageDefs.clear();
651    }
652    else if (mNativeTexture)
653    {
654        // Release native texture but keep its old per face per mipmap level image views.
655        retainImageDefinitions();
656    }
657
658    mNativeTexture             = nullptr;
659    mNativeSwizzleSamplingView = nullptr;
660
661    // Clear render target cache for each texture's image. We don't erase them because they
662    // might still be referenced by a framebuffer.
663    for (auto &sliceRenderTargets : mPerLayerRenderTargets)
664    {
665        for (RenderTargetMtl &mipRenderTarget : sliceRenderTargets.second)
666        {
667            mipRenderTarget.reset();
668        }
669    }
670
671    for (mtl::TextureRef &view : mNativeLevelViews)
672    {
673        view.reset();
674    }
675
676    if (!releaseTextureObjectsOnly)
677    {
678        mMetalSamplerState = nil;
679        mFormat            = mtl::Format();
680        mIsPow2            = false;
681    }
682}
683
684angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
685{
686    if (mNativeTexture)
687    {
688        return angle::Result::Continue;
689    }
690
691    ContextMtl *contextMtl = mtl::GetImpl(context);
692
693    // Create actual texture object:
694    mCurrentBaseLevel = mState.getEffectiveBaseLevel();
695
696    const GLuint mips  = mState.getMipmapMaxLevel() - mCurrentBaseLevel + 1;
697    gl::ImageDesc desc = mState.getBaseLevelDesc();
698    ANGLE_MTL_CHECK(contextMtl, desc.format.valid(), GL_INVALID_OPERATION);
699    angle::FormatID angleFormatId =
700        angle::Format::InternalFormatToID(desc.format.info->sizedInternalFormat);
701    mFormat = contextMtl->getPixelFormat(angleFormatId);
702
703    return createNativeTexture(context, mState.getType(), mips, desc.size);
704}
705
706angle::Result TextureMtl::createNativeTexture(const gl::Context *context,
707                                              gl::TextureType type,
708                                              GLuint mips,
709                                              const gl::Extents &size)
710{
711    ContextMtl *contextMtl = mtl::GetImpl(context);
712
713    // Create actual texture object:
714    mCurrentBaseLevel = mState.getEffectiveBaseLevel();
715    mCurrentMaxLevel  = mState.getEffectiveMaxLevel();
716
717    mIsPow2          = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
718    mSlices          = 1;
719    int numCubeFaces = 1;
720    switch (type)
721    {
722        case gl::TextureType::_2D:
723            ANGLE_TRY(mtl::Texture::Make2DTexture(
724                contextMtl, mFormat, size.width, size.height, mips,
725                /** renderTargetOnly */ false,
726                /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
727            break;
728        case gl::TextureType::CubeMap:
729            mSlices = numCubeFaces = 6;
730            ANGLE_TRY(mtl::Texture::MakeCubeTexture(
731                contextMtl, mFormat, size.width, mips,
732                /** renderTargetOnly */ false,
733                /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
734            break;
735        case gl::TextureType::_3D:
736            ANGLE_TRY(mtl::Texture::Make3DTexture(
737                contextMtl, mFormat, size.width, size.height, size.depth, mips,
738                /** renderTargetOnly */ false,
739                /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
740            break;
741        case gl::TextureType::_2DArray:
742            mSlices = size.depth;
743            ANGLE_TRY(mtl::Texture::Make2DArrayTexture(
744                contextMtl, mFormat, size.width, size.height, mips, mSlices,
745                /** renderTargetOnly */ false,
746                /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &mNativeTexture));
747            break;
748        default:
749            UNREACHABLE();
750    }
751
752    ANGLE_TRY(checkForEmulatedChannels(context, mFormat, mNativeTexture));
753
754    // Transfer data from images to actual texture object
755    mtl::BlitCommandEncoder *encoder = nullptr;
756    for (int face = 0; face < numCubeFaces; ++face)
757    {
758        for (mtl::MipmapNativeLevel actualMip = mtl::kZeroNativeMipLevel; actualMip.get() < mips;
759             ++actualMip)
760        {
761            GLuint imageMipLevel = mtl::GetGLMipLevel(actualMip, mState.getEffectiveBaseLevel());
762            mtl::TextureRef &imageToTransfer = mTexImageDefs[face][imageMipLevel].image;
763
764            // Only transfer if this mip & slice image has been defined and in correct size &
765            // format.
766            gl::Extents actualMipSize = mNativeTexture->size(actualMip);
767            if (imageToTransfer && imageToTransfer->sizeAt0() == actualMipSize &&
768                imageToTransfer->pixelFormat() == mNativeTexture->pixelFormat())
769            {
770                if (!encoder)
771                {
772                    encoder = contextMtl->getBlitCommandEncoder();
773                }
774                encoder->copyTexture(imageToTransfer, 0, mtl::kZeroNativeMipLevel, mNativeTexture,
775                                     face, actualMip, imageToTransfer->arrayLength(), 1);
776
777                // Invalidate texture image definition at this index so that we can make it a
778                // view of the native texture at this index later.
779                imageToTransfer = nullptr;
780            }
781        }
782    }
783
784    // Create sampler state
785    ANGLE_TRY(ensureSamplerStateCreated(context));
786
787    return angle::Result::Continue;
788}
789
790angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context)
791{
792    if (mMetalSamplerState)
793    {
794        return angle::Result::Continue;
795    }
796
797    ContextMtl *contextMtl = mtl::GetImpl(context);
798    DisplayMtl *displayMtl = contextMtl->getDisplay();
799
800    mtl::SamplerDesc samplerDesc(mState.getSamplerState());
801
802    if (mFormat.actualAngleFormat().depthBits && !mFormat.getCaps().filterable)
803    {
804        // On devices not supporting filtering for depth textures, we need to convert to nearest
805        // here.
806        samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
807        samplerDesc.magFilter = MTLSamplerMinMagFilterNearest;
808        if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped)
809        {
810            samplerDesc.mipFilter = MTLSamplerMipFilterNearest;
811        }
812
813        samplerDesc.maxAnisotropy = 1;
814    }
815    if (mState.getType() == gl::TextureType::Rectangle)
816    {
817        samplerDesc.rAddressMode = MTLSamplerAddressModeClampToEdge;
818        samplerDesc.sAddressMode = MTLSamplerAddressModeClampToEdge;
819        samplerDesc.tAddressMode = MTLSamplerAddressModeClampToEdge;
820    }
821    mMetalSamplerState =
822        displayMtl->getStateCache().getSamplerState(displayMtl->getMetalDevice(), samplerDesc);
823
824    return angle::Result::Continue;
825}
826
827angle::Result TextureMtl::onBaseMaxLevelsChanged(const gl::Context *context)
828{
829    if (!mNativeTexture || (mCurrentBaseLevel == mState.getEffectiveBaseLevel() &&
830                            mCurrentMaxLevel == mState.getEffectiveMaxLevel()))
831    {
832        return angle::Result::Continue;
833    }
834
835    ContextMtl *contextMtl = mtl::GetImpl(context);
836
837    // Release native texture but keep old image definitions so that it can be recreated from old
838    // image definitions with different base level
839    releaseTexture(false, true);
840
841    // Tell context to rebind textures
842    contextMtl->invalidateCurrentTextures();
843
844    return angle::Result::Continue;
845}
846
847angle::Result TextureMtl::ensureImageCreated(const gl::Context *context,
848                                             const gl::ImageIndex &index)
849{
850    mtl::TextureRef &image = getImage(index);
851    if (!image)
852    {
853        // Image at this level hasn't been defined yet. We need to define it:
854        const gl::ImageDesc &desc = mState.getImageDesc(index);
855        ANGLE_TRY(redefineImage(context, index, mFormat, desc.size));
856    }
857    return angle::Result::Continue;
858}
859
860angle::Result TextureMtl::ensureNativeLevelViewsCreated()
861{
862    ASSERT(mNativeTexture);
863    const GLuint baseLevel = mState.getEffectiveBaseLevel();
864    for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel;
865         mip.get() < mNativeTexture->mipmapLevels(); ++mip)
866    {
867        if (mNativeLevelViews[mip])
868        {
869            continue;
870        }
871
872        if (mNativeTexture->textureType() != MTLTextureTypeCube &&
873            mTexImageDefs[0][mtl::GetGLMipLevel(mip, baseLevel)].image)
874        {
875            // Reuse texture image view.
876            mNativeLevelViews[mip] = mTexImageDefs[0][mtl::GetGLMipLevel(mip, baseLevel)].image;
877        }
878        else
879        {
880            mNativeLevelViews[mip] = mNativeTexture->createMipView(mip);
881        }
882    }
883    return angle::Result::Continue;
884}
885
886mtl::TextureRef TextureMtl::createImageViewFromNativeTexture(
887    GLuint cubeFaceOrZero,
888    const mtl::MipmapNativeLevel &nativeLevel)
889{
890    mtl::TextureRef image;
891    if (mNativeTexture->textureType() == MTLTextureTypeCube)
892    {
893        // Cube texture's image is per face.
894        image = mNativeTexture->createSliceMipView(cubeFaceOrZero, nativeLevel);
895    }
896    else
897    {
898        if (mNativeLevelViews[nativeLevel])
899        {
900            // Reuse the native level view
901            image = mNativeLevelViews[nativeLevel];
902        }
903        else
904        {
905            image = mNativeTexture->createMipView(nativeLevel);
906        }
907    }
908
909    return image;
910}
911
912void TextureMtl::retainImageDefinitions()
913{
914    if (!mNativeTexture)
915    {
916        return;
917    }
918    const GLuint mips = mNativeTexture->mipmapLevels();
919
920    int numCubeFaces = 1;
921    switch (mState.getType())
922    {
923        case gl::TextureType::CubeMap:
924            numCubeFaces = 6;
925            break;
926        default:
927            break;
928    }
929
930    // Create image view per cube face, per mip level
931    for (int face = 0; face < numCubeFaces; ++face)
932    {
933        for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel; mip.get() < mips; ++mip)
934        {
935            GLuint imageMipLevel         = mtl::GetGLMipLevel(mip, mCurrentBaseLevel);
936            ImageDefinitionMtl &imageDef = mTexImageDefs[face][imageMipLevel];
937            if (imageDef.image)
938            {
939                continue;
940            }
941            imageDef.image    = createImageViewFromNativeTexture(face, mip);
942            imageDef.formatID = mFormat.intendedFormatId;
943        }
944    }
945}
946
947bool TextureMtl::isIndexWithinMinMaxLevels(const gl::ImageIndex &imageIndex) const
948{
949    return imageIndex.getLevelIndex() >= static_cast<GLint>(mState.getEffectiveBaseLevel()) &&
950           imageIndex.getLevelIndex() <= static_cast<GLint>(mState.getEffectiveMaxLevel());
951}
952
953mtl::MipmapNativeLevel TextureMtl::getNativeLevel(const gl::ImageIndex &imageIndex) const
954{
955    int baseLevel = mState.getEffectiveBaseLevel();
956    return mtl::GetNativeMipLevel(imageIndex.getLevelIndex(), baseLevel);
957}
958
959mtl::TextureRef &TextureMtl::getImage(const gl::ImageIndex &imageIndex)
960{
961    return getImageDefinition(imageIndex).image;
962}
963
964ImageDefinitionMtl &TextureMtl::getImageDefinition(const gl::ImageIndex &imageIndex)
965{
966    GLuint cubeFaceOrZero        = GetImageCubeFaceIndexOrZeroFrom(imageIndex);
967    ImageDefinitionMtl &imageDef = mTexImageDefs[cubeFaceOrZero][imageIndex.getLevelIndex()];
968
969    if (!imageDef.image && mNativeTexture)
970    {
971        // If native texture is already created, and the image at this index is not available,
972        // then create a view of native texture at this index, so that modifications of the image
973        // are reflected back to native texture's respective index.
974        if (!isIndexWithinMinMaxLevels(imageIndex))
975        {
976            // Image below base level is skipped.
977            return imageDef;
978        }
979
980        mtl::MipmapNativeLevel nativeLevel = getNativeLevel(imageIndex);
981        if (nativeLevel.get() >= mNativeTexture->mipmapLevels())
982        {
983            // Image outside native texture's mip levels is skipped.
984            return imageDef;
985        }
986
987        imageDef.image    = createImageViewFromNativeTexture(cubeFaceOrZero, nativeLevel);
988        imageDef.formatID = mFormat.intendedFormatId;
989    }
990
991    return imageDef;
992}
993RenderTargetMtl &TextureMtl::getRenderTarget(const gl::ImageIndex &imageIndex)
994{
995    ASSERT(imageIndex.getType() == gl::TextureType::_2D ||
996           imageIndex.getType() == gl::TextureType::Rectangle ||
997           imageIndex.getType() == gl::TextureType::_2DMultisample || imageIndex.hasLayer());
998    GLuint layer         = GetImageLayerIndexFrom(imageIndex);
999    RenderTargetMtl &rtt = mPerLayerRenderTargets[layer][imageIndex.getLevelIndex()];
1000    if (!rtt.getTexture())
1001    {
1002        // Lazy initialization of render target:
1003        mtl::TextureRef &image = getImage(imageIndex);
1004        if (image)
1005        {
1006            if (imageIndex.getType() == gl::TextureType::CubeMap)
1007            {
1008                // Cube map is special, the image is already the view of its layer
1009                rtt.set(image, mtl::kZeroNativeMipLevel, 0, mFormat);
1010            }
1011            else
1012            {
1013                rtt.set(image, mtl::kZeroNativeMipLevel, layer, mFormat);
1014            }
1015        }
1016    }
1017    return rtt;
1018}
1019
1020angle::Result TextureMtl::setImage(const gl::Context *context,
1021                                   const gl::ImageIndex &index,
1022                                   GLenum internalFormat,
1023                                   const gl::Extents &size,
1024                                   GLenum format,
1025                                   GLenum type,
1026                                   const gl::PixelUnpackState &unpack,
1027                                   gl::Buffer *unpackBuffer,
1028                                   const uint8_t *pixels)
1029{
1030    const gl::InternalFormat &dstFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
1031
1032    return setImageImpl(context, index, dstFormatInfo, size, format, type, unpack, unpackBuffer,
1033                        pixels);
1034}
1035
1036angle::Result TextureMtl::setSubImage(const gl::Context *context,
1037                                      const gl::ImageIndex &index,
1038                                      const gl::Box &area,
1039                                      GLenum format,
1040                                      GLenum type,
1041                                      const gl::PixelUnpackState &unpack,
1042                                      gl::Buffer *unpackBuffer,
1043                                      const uint8_t *pixels)
1044{
1045    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
1046
1047    return setSubImageImpl(context, index, area, formatInfo, type, unpack, unpackBuffer, pixels);
1048}
1049
1050angle::Result TextureMtl::setCompressedImage(const gl::Context *context,
1051                                             const gl::ImageIndex &index,
1052                                             GLenum internalFormat,
1053                                             const gl::Extents &size,
1054                                             const gl::PixelUnpackState &unpack,
1055                                             size_t imageSize,
1056                                             const uint8_t *pixels)
1057{
1058    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1059    const gl::State &glState             = context->getState();
1060    gl::Buffer *unpackBuffer             = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
1061
1062    return setImageImpl(context, index, formatInfo, size, internalFormat, GL_UNSIGNED_BYTE, unpack,
1063                        unpackBuffer, pixels);
1064}
1065
1066angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context,
1067                                                const gl::ImageIndex &index,
1068                                                const gl::Box &area,
1069                                                GLenum format,
1070                                                const gl::PixelUnpackState &unpack,
1071                                                size_t imageSize,
1072                                                const uint8_t *pixels)
1073{
1074
1075    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE);
1076
1077    const gl::State &glState = context->getState();
1078    gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
1079
1080    return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, unpackBuffer,
1081                           pixels);
1082}
1083
1084angle::Result TextureMtl::copyImage(const gl::Context *context,
1085                                    const gl::ImageIndex &index,
1086                                    const gl::Rectangle &sourceArea,
1087                                    GLenum internalFormat,
1088                                    gl::Framebuffer *source)
1089{
1090    gl::Extents newImageSize(sourceArea.width, sourceArea.height, 1);
1091    const gl::InternalFormat &internalFormatInfo =
1092        gl::GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE);
1093
1094    ContextMtl *contextMtl = mtl::GetImpl(context);
1095    angle::FormatID angleFormatId =
1096        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
1097    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1098
1099    FramebufferMtl *srcFramebufferMtl = mtl::GetImpl(source);
1100    RenderTargetMtl *srcReadRT        = srcFramebufferMtl->getColorReadRenderTarget(context);
1101    RenderTargetMtl colorReadRT;
1102    if (srcReadRT)
1103    {
1104        // Need to duplicate RenderTargetMtl since the srcReadRT would be invalidated in
1105        // redefineImage(). This can happen if the source and this texture are the same texture.
1106        // Duplication ensures the copyImage() will be able to proceed even if the source texture
1107        // will be redefined.
1108        colorReadRT.duplicateFrom(*srcReadRT);
1109    }
1110
1111    ANGLE_TRY(redefineImage(context, index, mtlFormat, newImageSize));
1112
1113    gl::Extents fbSize = source->getReadColorAttachment()->getSize();
1114    gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
1115    if (context->isWebGL() && !fbRect.encloses(sourceArea))
1116    {
1117        ANGLE_TRY(initializeContents(context, index));
1118    }
1119
1120    return copySubImageImpl(context, index, gl::Offset(0, 0, 0), sourceArea, internalFormatInfo,
1121                            srcFramebufferMtl, &colorReadRT);
1122}
1123
1124angle::Result TextureMtl::copySubImage(const gl::Context *context,
1125                                       const gl::ImageIndex &index,
1126                                       const gl::Offset &destOffset,
1127                                       const gl::Rectangle &sourceArea,
1128                                       gl::Framebuffer *source)
1129{
1130    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
1131    FramebufferMtl *srcFramebufferMtl       = mtl::GetImpl(source);
1132    RenderTargetMtl *colorReadRT            = srcFramebufferMtl->getColorReadRenderTarget(context);
1133    return copySubImageImpl(context, index, destOffset, sourceArea, currentFormat,
1134                            srcFramebufferMtl, colorReadRT);
1135}
1136
1137angle::Result TextureMtl::copyTexture(const gl::Context *context,
1138                                      const gl::ImageIndex &index,
1139                                      GLenum internalFormat,
1140                                      GLenum type,
1141                                      GLint sourceLevel,
1142                                      bool unpackFlipY,
1143                                      bool unpackPremultiplyAlpha,
1144                                      bool unpackUnmultiplyAlpha,
1145                                      const gl::Texture *source)
1146{
1147    const gl::ImageDesc &sourceImageDesc = source->getTextureState().getImageDesc(
1148        NonCubeTextureTypeToTarget(source->getType()), sourceLevel);
1149    const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat, type);
1150
1151    // Only 2D textures are supported.
1152    ASSERT(sourceImageDesc.size.depth == 1);
1153
1154    ContextMtl *contextMtl = mtl::GetImpl(context);
1155    angle::FormatID angleFormatId =
1156        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
1157    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1158
1159    ANGLE_TRY(redefineImage(context, index, mtlFormat, sourceImageDesc.size));
1160
1161    return copySubTextureImpl(
1162        context, index, gl::Offset(0, 0, 0), internalFormatInfo, sourceLevel,
1163        gl::Box(0, 0, 0, sourceImageDesc.size.width, sourceImageDesc.size.height, 1), unpackFlipY,
1164        unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
1165}
1166
1167angle::Result TextureMtl::copySubTexture(const gl::Context *context,
1168                                         const gl::ImageIndex &index,
1169                                         const gl::Offset &destOffset,
1170                                         GLint sourceLevel,
1171                                         const gl::Box &sourceBox,
1172                                         bool unpackFlipY,
1173                                         bool unpackPremultiplyAlpha,
1174                                         bool unpackUnmultiplyAlpha,
1175                                         const gl::Texture *source)
1176{
1177    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
1178
1179    return copySubTextureImpl(context, index, destOffset, currentFormat, sourceLevel, sourceBox,
1180                              unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
1181}
1182
1183angle::Result TextureMtl::copyCompressedTexture(const gl::Context *context,
1184                                                const gl::Texture *source)
1185{
1186    UNIMPLEMENTED();
1187
1188    return angle::Result::Stop;
1189}
1190
1191angle::Result TextureMtl::setStorage(const gl::Context *context,
1192                                     gl::TextureType type,
1193                                     size_t mipmaps,
1194                                     GLenum internalFormat,
1195                                     const gl::Extents &size)
1196{
1197    ContextMtl *contextMtl               = mtl::GetImpl(context);
1198    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
1199    angle::FormatID angleFormatId =
1200        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
1201    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1202
1203    return setStorageImpl(context, type, mipmaps, mtlFormat, size);
1204}
1205
1206angle::Result TextureMtl::setStorageExternalMemory(const gl::Context *context,
1207                                                   gl::TextureType type,
1208                                                   size_t levels,
1209                                                   GLenum internalFormat,
1210                                                   const gl::Extents &size,
1211                                                   gl::MemoryObject *memoryObject,
1212                                                   GLuint64 offset,
1213                                                   GLbitfield createFlags,
1214                                                   GLbitfield usageFlags,
1215                                                   const void *imageCreateInfoPNext)
1216{
1217    UNIMPLEMENTED();
1218
1219    return angle::Result::Stop;
1220}
1221
1222angle::Result TextureMtl::setStorageMultisample(const gl::Context *context,
1223                                                gl::TextureType type,
1224                                                GLsizei samples,
1225                                                GLint internalformat,
1226                                                const gl::Extents &size,
1227                                                bool fixedSampleLocations)
1228{
1229    UNIMPLEMENTED();
1230
1231    return angle::Result::Stop;
1232}
1233
1234angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context,
1235                                            gl::TextureType type,
1236                                            egl::Image *image)
1237{
1238    releaseTexture(true);
1239
1240    ContextMtl *contextMtl = mtl::GetImpl(context);
1241
1242    ImageMtl *imageMtl = mtl::GetImpl(image);
1243    if (type != imageMtl->getImageTextureType())
1244    {
1245        return angle::Result::Stop;
1246    }
1247
1248    mNativeTexture = imageMtl->getTexture();
1249
1250    const angle::FormatID angleFormatId =
1251        angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat);
1252    mFormat = contextMtl->getPixelFormat(angleFormatId);
1253
1254    mSlices = mNativeTexture->cubeFacesOrArrayLength();
1255
1256    gl::Extents size = mNativeTexture->sizeAt0();
1257    mIsPow2          = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
1258    ANGLE_TRY(ensureSamplerStateCreated(context));
1259
1260    // Tell context to rebind textures
1261    contextMtl->invalidateCurrentTextures();
1262
1263    return angle::Result::Continue;
1264}
1265
1266angle::Result TextureMtl::setImageExternal(const gl::Context *context,
1267                                           gl::TextureType type,
1268                                           egl::Stream *stream,
1269                                           const egl::Stream::GLTextureDescription &desc)
1270{
1271    UNIMPLEMENTED();
1272    return angle::Result::Stop;
1273}
1274
1275angle::Result TextureMtl::generateMipmap(const gl::Context *context)
1276{
1277    ANGLE_TRY(ensureTextureCreated(context));
1278
1279    ContextMtl *contextMtl = mtl::GetImpl(context);
1280    if (!mNativeTexture)
1281    {
1282        return angle::Result::Continue;
1283    }
1284
1285    const mtl::FormatCaps &caps = mFormat.getCaps();
1286    //
1287    bool sRGB = mFormat.actualInternalFormat().colorEncoding == GL_SRGB;
1288
1289    bool avoidGPUPath =
1290        contextMtl->getDisplay()->getFeatures().forceNonCSBaseMipmapGeneration.enabled &&
1291        mNativeTexture->widthAt0() < 5;
1292
1293    if (!avoidGPUPath && caps.writable && mState.getType() == gl::TextureType::_3D)
1294    {
1295        // http://anglebug.com/4921.
1296        // Use compute for 3D mipmap generation.
1297        ANGLE_TRY(ensureNativeLevelViewsCreated());
1298        ANGLE_TRY(contextMtl->getDisplay()->getUtils().generateMipmapCS(contextMtl, mNativeTexture,
1299                                                                        sRGB, &mNativeLevelViews));
1300    }
1301    else if (!avoidGPUPath && caps.filterable && caps.colorRenderable)
1302    {
1303        mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
1304        blitEncoder->generateMipmapsForTexture(mNativeTexture);
1305    }
1306    else
1307    {
1308        ANGLE_TRY(generateMipmapCPU(context));
1309    }
1310
1311    return angle::Result::Continue;
1312}
1313
1314angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
1315{
1316    ASSERT(mNativeTexture && mNativeTexture->valid());
1317
1318    ContextMtl *contextMtl           = mtl::GetImpl(context);
1319    const angle::Format &angleFormat = mFormat.actualAngleFormat();
1320    // This format must have mip generation function.
1321    ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction);
1322
1323    for (uint32_t slice = 0; slice < mSlices; ++slice)
1324    {
1325        mtl::MipmapNativeLevel maxMipLevel =
1326            mtl::GetNativeMipLevel(mNativeTexture->mipmapLevels() - 1, 0);
1327        const mtl::MipmapNativeLevel firstLevel = mtl::kZeroNativeMipLevel;
1328
1329        uint32_t prevLevelWidth    = mNativeTexture->widthAt0();
1330        uint32_t prevLevelHeight   = mNativeTexture->heightAt0();
1331        uint32_t prevLevelDepth    = mNativeTexture->depthAt0();
1332        size_t prevLevelRowPitch   = angleFormat.pixelBytes * prevLevelWidth;
1333        size_t prevLevelDepthPitch = prevLevelRowPitch * prevLevelHeight;
1334        std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow)
1335                                                     uint8_t[prevLevelDepthPitch * prevLevelDepth]);
1336        ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData);
1337        std::unique_ptr<uint8_t[]> dstLevelData;
1338
1339        // Download base level data
1340        mNativeTexture->getBytes(
1341            contextMtl, prevLevelRowPitch, prevLevelDepthPitch,
1342            MTLRegionMake3D(0, 0, 0, prevLevelWidth, prevLevelHeight, prevLevelDepth), firstLevel,
1343            slice, prevLevelData.get());
1344
1345        for (mtl::MipmapNativeLevel mip = firstLevel + 1; mip <= maxMipLevel; ++mip)
1346        {
1347            uint32_t dstWidth  = mNativeTexture->width(mip);
1348            uint32_t dstHeight = mNativeTexture->height(mip);
1349            uint32_t dstDepth  = mNativeTexture->depth(mip);
1350
1351            size_t dstRowPitch   = angleFormat.pixelBytes * dstWidth;
1352            size_t dstDepthPitch = dstRowPitch * dstHeight;
1353            size_t dstDataSize   = dstDepthPitch * dstDepth;
1354            if (!dstLevelData)
1355            {
1356                // Allocate once and reuse the buffer
1357                dstLevelData.reset(new (std::nothrow) uint8_t[dstDataSize]);
1358                ANGLE_CHECK_GL_ALLOC(contextMtl, dstLevelData);
1359            }
1360
1361            // Generate mip level
1362            angleFormat.mipGenerationFunction(
1363                prevLevelWidth, prevLevelHeight, 1, prevLevelData.get(), prevLevelRowPitch,
1364                prevLevelDepthPitch, dstLevelData.get(), dstRowPitch, dstDepthPitch);
1365
1366            // Upload to texture
1367            ANGLE_TRY(UploadTextureContents(
1368                context, angleFormat, MTLRegionMake3D(0, 0, 0, dstWidth, dstHeight, dstDepth), mip,
1369                slice, dstLevelData.get(), dstRowPitch, dstDepthPitch, mNativeTexture));
1370
1371            prevLevelWidth      = dstWidth;
1372            prevLevelHeight     = dstHeight;
1373            prevLevelDepth      = dstDepth;
1374            prevLevelRowPitch   = dstRowPitch;
1375            prevLevelDepthPitch = dstDepthPitch;
1376            std::swap(prevLevelData, dstLevelData);
1377        }  // for mip level
1378
1379    }  // For layers
1380
1381    return angle::Result::Continue;
1382}
1383
1384angle::Result TextureMtl::setBaseLevel(const gl::Context *context, GLuint baseLevel)
1385{
1386    return onBaseMaxLevelsChanged(context);
1387}
1388
1389angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface *surface)
1390{
1391    releaseTexture(true);
1392
1393    auto pBuffer     = GetImplAs<OffscreenSurfaceMtl>(surface);
1394    mNativeTexture   = pBuffer->getColorTexture();
1395    mFormat          = pBuffer->getColorFormat();
1396    gl::Extents size = mNativeTexture->sizeAt0();
1397    mIsPow2          = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
1398    ANGLE_TRY(ensureSamplerStateCreated(context));
1399
1400    // Tell context to rebind textures
1401    ContextMtl *contextMtl = mtl::GetImpl(context);
1402    contextMtl->invalidateCurrentTextures();
1403
1404    return angle::Result::Continue;
1405}
1406
1407angle::Result TextureMtl::releaseTexImage(const gl::Context *context)
1408{
1409    releaseTexture(true);
1410    return angle::Result::Continue;
1411}
1412
1413angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context,
1414                                                    GLenum binding,
1415                                                    const gl::ImageIndex &imageIndex,
1416                                                    GLsizei samples,
1417                                                    FramebufferAttachmentRenderTarget **rtOut)
1418{
1419    ANGLE_TRY(ensureTextureCreated(context));
1420
1421    ContextMtl *contextMtl = mtl::GetImpl(context);
1422    ANGLE_MTL_TRY(contextMtl, mNativeTexture);
1423
1424    *rtOut = &getRenderTarget(imageIndex);
1425
1426    return angle::Result::Continue;
1427}
1428
1429angle::Result TextureMtl::syncState(const gl::Context *context,
1430                                    const gl::Texture::DirtyBits &dirtyBits,
1431                                    gl::Command source)
1432{
1433    ContextMtl *contextMtl = mtl::GetImpl(context);
1434    for (size_t dirtyBit : dirtyBits)
1435    {
1436        switch (dirtyBit)
1437        {
1438            case gl::Texture::DIRTY_BIT_COMPARE_MODE:
1439            case gl::Texture::DIRTY_BIT_COMPARE_FUNC:
1440                // Tell context to rebind textures so that ProgramMtl has a chance to verify
1441                // depth texture compare mode.
1442                contextMtl->invalidateCurrentTextures();
1443                // fall through
1444                OS_FALLTHROUGH;
1445            case gl::Texture::DIRTY_BIT_MIN_FILTER:
1446            case gl::Texture::DIRTY_BIT_MAG_FILTER:
1447            case gl::Texture::DIRTY_BIT_WRAP_S:
1448            case gl::Texture::DIRTY_BIT_WRAP_T:
1449            case gl::Texture::DIRTY_BIT_WRAP_R:
1450            case gl::Texture::DIRTY_BIT_MAX_ANISOTROPY:
1451            case gl::Texture::DIRTY_BIT_MIN_LOD:
1452            case gl::Texture::DIRTY_BIT_MAX_LOD:
1453            case gl::Texture::DIRTY_BIT_SRGB_DECODE:
1454            case gl::Texture::DIRTY_BIT_BORDER_COLOR:
1455                // Recreate sampler state
1456                mMetalSamplerState = nil;
1457                break;
1458            case gl::Texture::DIRTY_BIT_MAX_LEVEL:
1459            case gl::Texture::DIRTY_BIT_BASE_LEVEL:
1460                ANGLE_TRY(onBaseMaxLevelsChanged(context));
1461                break;
1462            case gl::Texture::DIRTY_BIT_SWIZZLE_RED:
1463            case gl::Texture::DIRTY_BIT_SWIZZLE_GREEN:
1464            case gl::Texture::DIRTY_BIT_SWIZZLE_BLUE:
1465            case gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA:
1466            {
1467                // Recreate swizzle view.
1468                mNativeSwizzleSamplingView = nullptr;
1469            }
1470            break;
1471            default:
1472                break;
1473        }
1474    }
1475
1476    ANGLE_TRY(ensureTextureCreated(context));
1477    ANGLE_TRY(ensureSamplerStateCreated(context));
1478
1479    return angle::Result::Continue;
1480}
1481
1482angle::Result TextureMtl::bindToShader(const gl::Context *context,
1483                                       mtl::RenderCommandEncoder *cmdEncoder,
1484                                       gl::ShaderType shaderType,
1485                                       gl::Sampler *sampler,
1486                                       int textureSlotIndex,
1487                                       int samplerSlotIndex)
1488{
1489    ASSERT(mNativeTexture);
1490
1491    float minLodClamp;
1492    float maxLodClamp;
1493    id<MTLSamplerState> samplerState;
1494
1495    if (!mNativeSwizzleSamplingView)
1496    {
1497#if ANGLE_MTL_SWIZZLE_AVAILABLE
1498        ContextMtl *contextMtl = mtl::GetImpl(context);
1499
1500        if ((mState.getSwizzleState().swizzleRequired() || mFormat.actualAngleFormat().depthBits ||
1501             mFormat.swizzled) &&
1502            contextMtl->getDisplay()->getFeatures().hasTextureSwizzle.enabled)
1503        {
1504            const gl::InternalFormat &glInternalFormat = *mState.getBaseLevelDesc().format.info;
1505
1506            MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake(
1507                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1508                    context, mState.getSwizzleState().swizzleRed, mFormat, glInternalFormat)),
1509                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1510                    context, mState.getSwizzleState().swizzleGreen, mFormat, glInternalFormat)),
1511                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1512                    context, mState.getSwizzleState().swizzleBlue, mFormat, glInternalFormat)),
1513                mtl::GetTextureSwizzle(OverrideSwizzleValue(
1514                    context, mState.getSwizzleState().swizzleAlpha, mFormat, glInternalFormat)));
1515
1516            mNativeSwizzleSamplingView = mNativeTexture->createSwizzleView(swizzle);
1517        }
1518        else
1519#endif  // ANGLE_MTL_SWIZZLE_AVAILABLE
1520        {
1521            mNativeSwizzleSamplingView = mNativeTexture;
1522        }
1523    }
1524
1525    if (!sampler)
1526    {
1527        samplerState = mMetalSamplerState;
1528        minLodClamp  = mState.getSamplerState().getMinLod();
1529        maxLodClamp  = mState.getSamplerState().getMaxLod();
1530    }
1531    else
1532    {
1533        SamplerMtl *samplerMtl = mtl::GetImpl(sampler);
1534        samplerState           = samplerMtl->getSampler(mtl::GetImpl(context));
1535        minLodClamp            = sampler->getSamplerState().getMinLod();
1536        maxLodClamp            = sampler->getSamplerState().getMaxLod();
1537    }
1538
1539    minLodClamp = std::max(minLodClamp, 0.f);
1540
1541    cmdEncoder->setTexture(shaderType, mNativeSwizzleSamplingView, textureSlotIndex);
1542    cmdEncoder->setSamplerState(shaderType, samplerState, minLodClamp, maxLodClamp,
1543                                samplerSlotIndex);
1544
1545    return angle::Result::Continue;
1546}
1547
1548angle::Result TextureMtl::redefineImage(const gl::Context *context,
1549                                        const gl::ImageIndex &index,
1550                                        const mtl::Format &mtlFormat,
1551                                        const gl::Extents &size)
1552{
1553    bool imageWithinLevelRange = false;
1554    if (isIndexWithinMinMaxLevels(index) && mNativeTexture && mNativeTexture->valid())
1555    {
1556        imageWithinLevelRange              = true;
1557        mtl::MipmapNativeLevel nativeLevel = getNativeLevel(index);
1558        // Calculate the expected size for the index we are defining. If the size is different
1559        // from the given size, or the format is different, we are redefining the image so we
1560        // must release it.
1561        bool typeChanged = mNativeTexture->textureType() != mtl::GetTextureType(index.getType());
1562        if (mFormat != mtlFormat || size != mNativeTexture->size(nativeLevel) || typeChanged)
1563        {
1564            // Keep other images data if texture type hasn't been changed.
1565            releaseTexture(typeChanged);
1566        }
1567    }
1568
1569    // Early-out on empty textures, don't create a zero-sized storage.
1570    if (size.empty())
1571    {
1572        return angle::Result::Continue;
1573    }
1574
1575    ContextMtl *contextMtl = mtl::GetImpl(context);
1576    // Cache last defined image format:
1577    mFormat                      = mtlFormat;
1578    ImageDefinitionMtl &imageDef = getImageDefinition(index);
1579
1580    // If native texture still exists, it means the size hasn't been changed, no need to create new
1581    // image
1582    if (mNativeTexture && imageDef.image && imageWithinLevelRange)
1583    {
1584        ASSERT(imageDef.image->textureType() ==
1585                   mtl::GetTextureType(GetTextureImageType(index.getType())) &&
1586               imageDef.formatID == mFormat.intendedFormatId && imageDef.image->sizeAt0() == size);
1587    }
1588    else
1589    {
1590        imageDef.formatID = mtlFormat.intendedFormatId;
1591        // Create image to hold texture's data at this level & slice:
1592        switch (index.getType())
1593        {
1594            case gl::TextureType::_2D:
1595            case gl::TextureType::CubeMap:
1596                ANGLE_TRY(mtl::Texture::Make2DTexture(
1597                    contextMtl, mtlFormat, size.width, size.height, 1,
1598                    /** renderTargetOnly */ false,
1599                    /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &imageDef.image));
1600                break;
1601            case gl::TextureType::_3D:
1602                ANGLE_TRY(mtl::Texture::Make3DTexture(
1603                    contextMtl, mtlFormat, size.width, size.height, size.depth, 1,
1604                    /** renderTargetOnly */ false,
1605                    /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &imageDef.image));
1606                break;
1607            case gl::TextureType::_2DArray:
1608                ANGLE_TRY(mtl::Texture::Make2DArrayTexture(
1609                    contextMtl, mtlFormat, size.width, size.height, 1, size.depth,
1610                    /** renderTargetOnly */ false,
1611                    /** allowFormatView */ mFormat.hasDepthAndStencilBits(), &imageDef.image));
1612                break;
1613            default:
1614                UNREACHABLE();
1615        }
1616    }
1617
1618    // Make sure emulated channels are properly initialized
1619    ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, imageDef.image));
1620
1621    // Tell context to rebind textures
1622    contextMtl->invalidateCurrentTextures();
1623
1624    return angle::Result::Continue;
1625}
1626
1627// If mipmaps = 0, this function will create full mipmaps texture.
1628angle::Result TextureMtl::setStorageImpl(const gl::Context *context,
1629                                         gl::TextureType type,
1630                                         size_t mipmaps,
1631                                         const mtl::Format &mtlFormat,
1632                                         const gl::Extents &size)
1633{
1634    // Don't need to hold old images data.
1635    releaseTexture(true);
1636
1637    ContextMtl *contextMtl = mtl::GetImpl(context);
1638
1639    // Tell context to rebind textures
1640    contextMtl->invalidateCurrentTextures();
1641
1642    mFormat = mtlFormat;
1643
1644    // Texture will be created later in ensureTextureCreated()
1645
1646    return angle::Result::Continue;
1647}
1648
1649angle::Result TextureMtl::setImageImpl(const gl::Context *context,
1650                                       const gl::ImageIndex &index,
1651                                       const gl::InternalFormat &dstFormatInfo,
1652                                       const gl::Extents &size,
1653                                       GLenum srcFormat,
1654                                       GLenum srcType,
1655                                       const gl::PixelUnpackState &unpack,
1656                                       gl::Buffer *unpackBuffer,
1657                                       const uint8_t *pixels)
1658{
1659    ContextMtl *contextMtl = mtl::GetImpl(context);
1660    angle::FormatID angleFormatId =
1661        angle::Format::InternalFormatToID(dstFormatInfo.sizedInternalFormat);
1662    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
1663
1664    ANGLE_TRY(redefineImage(context, index, mtlFormat, size));
1665
1666    // Early-out on empty textures, don't create a zero-sized storage.
1667    if (size.empty())
1668    {
1669        return angle::Result::Continue;
1670    }
1671
1672    // Format of the supplied pixels.
1673    const gl::InternalFormat *srcFormatInfo;
1674    if (srcFormat != dstFormatInfo.format || srcType != dstFormatInfo.type)
1675    {
1676        srcFormatInfo = &gl::GetInternalFormatInfo(srcFormat, srcType);
1677    }
1678    else
1679    {
1680        srcFormatInfo = &dstFormatInfo;
1681    }
1682    return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth),
1683                           *srcFormatInfo, srcType, unpack, unpackBuffer, pixels);
1684}
1685
1686angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
1687                                          const gl::ImageIndex &index,
1688                                          const gl::Box &area,
1689                                          const gl::InternalFormat &formatInfo,
1690                                          GLenum type,
1691                                          const gl::PixelUnpackState &unpack,
1692                                          gl::Buffer *unpackBuffer,
1693                                          const uint8_t *oriPixels)
1694{
1695    if (!oriPixels && !unpackBuffer)
1696    {
1697        return angle::Result::Continue;
1698    }
1699
1700    ContextMtl *contextMtl = mtl::GetImpl(context);
1701
1702    ANGLE_TRY(ensureImageCreated(context, index));
1703    mtl::TextureRef &image = getImage(index);
1704
1705    GLuint sourceRowPitch   = 0;
1706    GLuint sourceDepthPitch = 0;
1707    GLuint sourceSkipBytes  = 0;
1708    ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment,
1709                                                               unpack.rowLength, &sourceRowPitch));
1710    ANGLE_CHECK_GL_MATH(
1711        contextMtl, formatInfo.computeDepthPitch(area.height, unpack.imageHeight, sourceRowPitch,
1712                                                 &sourceDepthPitch));
1713    ANGLE_CHECK_GL_MATH(contextMtl,
1714                        formatInfo.computeSkipBytes(type, sourceRowPitch, sourceDepthPitch, unpack,
1715                                                    index.usesTex3D(), &sourceSkipBytes));
1716
1717    // Check if partial image update is supported for this format
1718    if (!formatInfo.supportSubImage())
1719    {
1720        // area must be the whole mip level
1721        sourceRowPitch   = 0;
1722        gl::Extents size = image->sizeAt0();
1723        if (area.x != 0 || area.y != 0 || area.width != size.width || area.height != size.height)
1724        {
1725            ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
1726        }
1727    }
1728
1729    // Get corresponding source data's ANGLE format
1730    angle::FormatID srcAngleFormatId;
1731    if (formatInfo.sizedInternalFormat == GL_DEPTH_COMPONENT24)
1732    {
1733        // GL_DEPTH_COMPONENT24 is special case, its supplied data is 32 bit depth.
1734        srcAngleFormatId = angle::FormatID::D32_UNORM;
1735    }
1736    else
1737    {
1738        srcAngleFormatId = angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
1739    }
1740    const angle::Format &srcAngleFormat = angle::Format::Get(srcAngleFormatId);
1741
1742    const uint8_t *usablePixels = oriPixels + sourceSkipBytes;
1743
1744    // Upload to texture
1745    if (index.getType() == gl::TextureType::_2DArray)
1746    {
1747        // OpenGL unifies texture array and texture 3d's box area by using z & depth as array start
1748        // index & length for texture array. However, Metal treats them differently. We need to
1749        // handle them in separate code.
1750        MTLRegion mtlRegion = MTLRegionMake3D(area.x, area.y, 0, area.width, area.height, 1);
1751
1752        for (int slice = 0; slice < area.depth; ++slice)
1753        {
1754            int sliceIndex           = slice + area.z;
1755            const uint8_t *srcPixels = usablePixels + slice * sourceDepthPitch;
1756            ANGLE_TRY(setPerSliceSubImage(context, sliceIndex, mtlRegion, formatInfo, type,
1757                                          srcAngleFormat, sourceRowPitch, sourceDepthPitch,
1758                                          unpackBuffer, srcPixels, image));
1759        }
1760    }
1761    else
1762    {
1763        MTLRegion mtlRegion =
1764            MTLRegionMake3D(area.x, area.y, area.z, area.width, area.height, area.depth);
1765
1766        ANGLE_TRY(setPerSliceSubImage(context, 0, mtlRegion, formatInfo, type, srcAngleFormat,
1767                                      sourceRowPitch, sourceDepthPitch, unpackBuffer, usablePixels,
1768                                      image));
1769    }
1770
1771    return angle::Result::Continue;
1772}
1773
1774angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context,
1775                                              int slice,
1776                                              const MTLRegion &mtlArea,
1777                                              const gl::InternalFormat &internalFormat,
1778                                              GLenum type,
1779                                              const angle::Format &pixelsAngleFormat,
1780                                              size_t pixelsRowPitch,
1781                                              size_t pixelsDepthPitch,
1782                                              gl::Buffer *unpackBuffer,
1783                                              const uint8_t *pixels,
1784                                              const mtl::TextureRef &image)
1785{
1786    // If source pixels are luminance or RGB8, we need to convert them to RGBA
1787
1788    if (mFormat.needConversion(pixelsAngleFormat.id))
1789    {
1790        return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
1791                                             pixelsAngleFormat, pixelsRowPitch, pixelsDepthPitch,
1792                                             unpackBuffer, pixels, image);
1793    }
1794
1795    // No conversion needed.
1796    ContextMtl *contextMtl = mtl::GetImpl(context);
1797
1798    if (unpackBuffer)
1799    {
1800        uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
1801        GLuint minRowPitch;
1802        ANGLE_CHECK_GL_MATH(contextMtl, internalFormat.computeRowPitch(
1803                                            type, static_cast<GLint>(mtlArea.size.width),
1804                                            /** aligment */ 1, /** rowLength */ 0, &minRowPitch));
1805        if (offset % mFormat.actualAngleFormat().pixelBytes || pixelsRowPitch < minRowPitch)
1806        {
1807            // offset is not divisible by pixelByte or the source row pitch is smaller than minimum
1808            // row pitch, use convertAndSetPerSliceSubImage() function.
1809            return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
1810                                                 pixelsAngleFormat, pixelsRowPitch,
1811                                                 pixelsDepthPitch, unpackBuffer, pixels, image);
1812        }
1813
1814        BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
1815
1816        if (mFormat.hasDepthAndStencilBits())
1817        {
1818            // NOTE(hqle): packed depth & stencil texture cannot copy from buffer directly, needs
1819            // to split its depth & stencil data and copy separately.
1820            const uint8_t *clientData = unpackBufferMtl->getClientShadowCopyData(contextMtl);
1821            clientData += offset;
1822            ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea,
1823                                            mtl::kZeroNativeMipLevel, slice, clientData,
1824                                            pixelsRowPitch, pixelsDepthPitch, image));
1825        }
1826        else
1827        {
1828            // Use blit encoder to copy
1829            mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
1830            blitEncoder->copyBufferToTexture(
1831                unpackBufferMtl->getCurrentBuffer(), offset, pixelsRowPitch, pixelsDepthPitch,
1832                mtlArea.size, image, slice, mtl::kZeroNativeMipLevel, mtlArea.origin,
1833                mFormat.isPVRTC() ? mtl::kBlitOptionRowLinearPVRTC : MTLBlitOptionNone);
1834        }
1835    }
1836    else
1837    {
1838        // Upload texture data directly
1839        ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea,
1840                                        mtl::kZeroNativeMipLevel, slice, pixels, pixelsRowPitch,
1841                                        pixelsDepthPitch, image));
1842    }
1843    return angle::Result::Continue;
1844}
1845
1846angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *context,
1847                                                        int slice,
1848                                                        const MTLRegion &mtlArea,
1849                                                        const gl::InternalFormat &internalFormat,
1850                                                        GLenum type,
1851                                                        const angle::Format &pixelsAngleFormat,
1852                                                        size_t pixelsRowPitch,
1853                                                        size_t pixelsDepthPitch,
1854                                                        gl::Buffer *unpackBuffer,
1855                                                        const uint8_t *pixels,
1856                                                        const mtl::TextureRef &image)
1857{
1858    ASSERT(image && image->valid());
1859
1860    ContextMtl *contextMtl = mtl::GetImpl(context);
1861
1862    if (unpackBuffer)
1863    {
1864        ANGLE_MTL_CHECK(contextMtl,
1865                        reinterpret_cast<uintptr_t>(pixels) <= std::numeric_limits<uint32_t>::max(),
1866                        GL_INVALID_OPERATION);
1867
1868        uint32_t offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pixels));
1869
1870        BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer);
1871        if (!mFormat.getCaps().writable || mFormat.hasDepthOrStencilBits() ||
1872            mFormat.intendedAngleFormat().isBlock)
1873        {
1874            // Unsupported format, use CPU path.
1875            const uint8_t *clientData = unpackBufferMtl->getClientShadowCopyData(contextMtl);
1876            clientData += offset;
1877            ANGLE_TRY(convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type,
1878                                                    pixelsAngleFormat, pixelsRowPitch,
1879                                                    pixelsDepthPitch, nullptr, clientData, image));
1880        }
1881        else
1882        {
1883            // Use compute shader
1884            mtl::CopyPixelsFromBufferParams params;
1885            params.buffer            = unpackBufferMtl->getCurrentBuffer();
1886            params.bufferStartOffset = offset;
1887            params.bufferRowPitch    = static_cast<uint32_t>(pixelsRowPitch);
1888            params.bufferDepthPitch  = static_cast<uint32_t>(pixelsDepthPitch);
1889            params.texture           = image;
1890            params.textureArea       = mtl::MTLRegionToGLBox(mtlArea);
1891
1892            // If texture is not array, slice must be zero, if texture is array, mtlArea.origin.z
1893            // must be zero.
1894            // This is because this function uses Metal convention: where slice is only used for
1895            // array textures, and z layer of mtlArea.origin is only used for 3D textures.
1896            ASSERT(slice == 0 || params.textureArea.z == 0);
1897
1898            // For mtl::RenderUtils we convert to OpenGL convention: z layer is used as either array
1899            // texture's slice or 3D texture's layer index.
1900            params.textureArea.z += slice;
1901
1902            ANGLE_TRY(contextMtl->getDisplay()->getUtils().unpackPixelsFromBufferToTexture(
1903                contextMtl, pixelsAngleFormat, params));
1904        }
1905    }  // if (unpackBuffer)
1906    else
1907    {
1908        LoadImageFunctionInfo loadFunctionInfo = mFormat.textureLoadFunctions
1909                                                     ? mFormat.textureLoadFunctions(type)
1910                                                     : LoadImageFunctionInfo();
1911        const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
1912        const size_t dstRowPitch       = dstFormat.pixelBytes * mtlArea.size.width;
1913
1914        // Check if original image data is compressed:
1915        if (mFormat.intendedAngleFormat().isBlock)
1916        {
1917            if (mFormat.intendedFormatId != mFormat.actualFormatId)
1918            {
1919                ASSERT(loadFunctionInfo.loadFunction);
1920
1921                // Need to create a buffer to hold entire decompressed image.
1922                const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
1923                angle::MemoryBuffer decompressBuf;
1924                ANGLE_CHECK_GL_ALLOC(contextMtl,
1925                                     decompressBuf.resize(dstDepthPitch * mtlArea.size.depth));
1926
1927                // Decompress
1928                loadFunctionInfo.loadFunction(mtlArea.size.width, mtlArea.size.height,
1929                                              mtlArea.size.depth, pixels, pixelsRowPitch,
1930                                              pixelsDepthPitch, decompressBuf.data(), dstRowPitch,
1931                                              dstDepthPitch);
1932
1933                // Upload to texture
1934                ANGLE_TRY(UploadTextureContents(
1935                    context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice,
1936                    decompressBuf.data(), dstRowPitch, dstDepthPitch, image));
1937            }
1938            else
1939            {
1940                // Assert that we're filling the level in it's entierety.
1941                ASSERT(mtlArea.size.width == static_cast<unsigned int>(image->sizeAt0().width));
1942                ASSERT(mtlArea.size.height == static_cast<unsigned int>(image->sizeAt0().height));
1943                const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height;
1944                ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlArea,
1945                                                mtl::kZeroNativeMipLevel, slice, pixels,
1946                                                dstRowPitch, dstDepthPitch, image));
1947            }
1948        }  // if (mFormat.intendedAngleFormat().isBlock)
1949        else
1950        {
1951            // Create scratch row buffer
1952            angle::MemoryBuffer conversionRow;
1953            ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
1954
1955            // Convert row by row:
1956            MTLRegion mtlRow   = mtlArea;
1957            mtlRow.size.height = mtlRow.size.depth = 1;
1958            for (NSUInteger d = 0; d < mtlArea.size.depth; ++d)
1959            {
1960                mtlRow.origin.z = mtlArea.origin.z + d;
1961                for (NSUInteger r = 0; r < mtlArea.size.height; ++r)
1962                {
1963                    const uint8_t *psrc = pixels + d * pixelsDepthPitch + r * pixelsRowPitch;
1964                    mtlRow.origin.y     = mtlArea.origin.y + r;
1965
1966                    // Convert pixels
1967                    if (loadFunctionInfo.loadFunction)
1968                    {
1969                        loadFunctionInfo.loadFunction(mtlRow.size.width, 1, 1, psrc, pixelsRowPitch,
1970                                                      0, conversionRow.data(), dstRowPitch, 0);
1971                    }
1972                    else if (mFormat.hasDepthOrStencilBits())
1973                    {
1974                        ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0,
1975                                                psrc, dstFormat, nullptr, dstRowPitch, 0,
1976                                                conversionRow.data());
1977                    }
1978                    else
1979                    {
1980                        CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsAngleFormat.pixelBytes, 0,
1981                                          pixelsAngleFormat.pixelReadFunction, conversionRow.data(),
1982                                          dstRowPitch, dstFormat.pixelBytes, 0,
1983                                          dstFormat.pixelWriteFunction, internalFormat.format,
1984                                          dstFormat.componentType, mtlRow.size.width, 1, 1, false,
1985                                          false, false);
1986                    }
1987
1988                    // Upload to texture
1989                    ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlRow,
1990                                                    mtl::kZeroNativeMipLevel, slice,
1991                                                    conversionRow.data(), dstRowPitch, 0, image));
1992                }
1993            }
1994        }  // if (mFormat.intendedAngleFormat().isBlock)
1995    }      // if (unpackBuffer)
1996
1997    return angle::Result::Continue;
1998}
1999
2000angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
2001                                                   const mtl::Format &mtlFormat,
2002                                                   const mtl::TextureRef &texture)
2003{
2004    bool emulatedChannels = mtl::IsFormatEmulated(mtlFormat);
2005
2006    // For emulated channels that GL texture intends to not have,
2007    // we need to initialize their content.
2008    if (emulatedChannels)
2009    {
2010        uint32_t mipmaps = texture->mipmapLevels();
2011
2012        uint32_t layers = texture->cubeFacesOrArrayLength();
2013        for (uint32_t layer = 0; layer < layers; ++layer)
2014        {
2015            for (uint32_t mip = 0; mip < mipmaps; ++mip)
2016            {
2017                auto index = mtl::ImageNativeIndex::FromBaseZeroGLIndex(
2018                    GetCubeOrArraySliceMipIndex(texture, layer, mip));
2019
2020                ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mtlFormat, index));
2021            }
2022        }
2023    }
2024    return angle::Result::Continue;
2025}
2026
2027angle::Result TextureMtl::initializeContents(const gl::Context *context,
2028                                             const gl::ImageIndex &index)
2029{
2030    if (index.isLayered())
2031    {
2032        // InitializeTextureContents is only able to initialize one layer at a time.
2033        const gl::ImageDesc &desc = mState.getImageDesc(index);
2034        uint32_t layerCount;
2035        if (index.isEntireLevelCubeMap())
2036        {
2037            layerCount = 6;
2038        }
2039        else
2040        {
2041            layerCount = desc.size.depth;
2042        }
2043
2044        gl::ImageIndexIterator ite = index.getLayerIterator(layerCount);
2045        while (ite.hasNext())
2046        {
2047            gl::ImageIndex layerIndex = ite.next();
2048            ANGLE_TRY(initializeContents(context, layerIndex));
2049        }
2050        return angle::Result::Continue;
2051    }
2052    else if (index.getLayerCount() > 1)
2053    {
2054        for (int layer = 0; layer < index.getLayerCount(); ++layer)
2055        {
2056            int layerIdx = layer + index.getLayerIndex();
2057            gl::ImageIndex layerIndex =
2058                gl::ImageIndex::MakeFromType(index.getType(), index.getLevelIndex(), layerIdx);
2059            ANGLE_TRY(initializeContents(context, layerIndex));
2060        }
2061        return angle::Result::Continue;
2062    }
2063
2064    ASSERT(index.getLayerCount() == 1 && !index.isLayered());
2065    ANGLE_TRY(ensureImageCreated(context, index));
2066    ContextMtl *contextMtl       = mtl::GetImpl(context);
2067    ImageDefinitionMtl &imageDef = getImageDefinition(index);
2068    const mtl::TextureRef &image = imageDef.image;
2069    const mtl::Format &format    = contextMtl->getPixelFormat(imageDef.formatID);
2070    // For Texture's image definition, we always use zero mip level.
2071    if (format.metalFormat == MTLPixelFormatInvalid)
2072    {
2073        return angle::Result::Stop;
2074    }
2075    return mtl::InitializeTextureContents(
2076        context, image, format,
2077        mtl::ImageNativeIndex::FromBaseZeroGLIndex(
2078            GetLayerMipIndex(image, GetImageLayerIndexFrom(index), /** level */ 0)));
2079}
2080
2081angle::Result TextureMtl::copySubImageImpl(const gl::Context *context,
2082                                           const gl::ImageIndex &index,
2083                                           const gl::Offset &destOffset,
2084                                           const gl::Rectangle &sourceArea,
2085                                           const gl::InternalFormat &internalFormat,
2086                                           const FramebufferMtl *source,
2087                                           const RenderTargetMtl *colorReadRT)
2088{
2089    if (!colorReadRT || !colorReadRT->getTexture())
2090    {
2091        // Is this an error?
2092        return angle::Result::Continue;
2093    }
2094
2095    gl::Extents fbSize = colorReadRT->getTexture()->size(colorReadRT->getLevelIndex());
2096    gl::Rectangle clippedSourceArea;
2097    if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height),
2098                       &clippedSourceArea))
2099    {
2100        return angle::Result::Continue;
2101    }
2102
2103    // If negative offsets are given, clippedSourceArea ensures we don't read from those offsets.
2104    // However, that changes the sourceOffset->destOffset mapping.  Here, destOffset is shifted by
2105    // the same amount as clipped to correct the error.
2106    const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x,
2107                                        destOffset.y + clippedSourceArea.y - sourceArea.y, 0);
2108
2109    ANGLE_TRY(ensureImageCreated(context, index));
2110
2111    if (!mFormat.getCaps().isRenderable())
2112    {
2113        return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea,
2114                               internalFormat, source, colorReadRT);
2115    }
2116
2117    // NOTE(hqle): Use compute shader.
2118    return copySubImageWithDraw(context, index, modifiedDestOffset, clippedSourceArea,
2119                                internalFormat, source, colorReadRT);
2120}
2121
2122angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
2123                                               const gl::ImageIndex &index,
2124                                               const gl::Offset &modifiedDestOffset,
2125                                               const gl::Rectangle &clippedSourceArea,
2126                                               const gl::InternalFormat &internalFormat,
2127                                               const FramebufferMtl *source,
2128                                               const RenderTargetMtl *colorReadRT)
2129{
2130    ContextMtl *contextMtl = mtl::GetImpl(context);
2131    DisplayMtl *displayMtl = contextMtl->getDisplay();
2132
2133    const RenderTargetMtl &imageRtt = getRenderTarget(index);
2134
2135    mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(imageRtt);
2136    mtl::ColorBlitParams blitParams;
2137
2138    blitParams.dstTextureSize = imageRtt.getTexture()->size(imageRtt.getLevelIndex());
2139    blitParams.dstRect        = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y,
2140                                       clippedSourceArea.width, clippedSourceArea.height);
2141    blitParams.dstScissorRect = blitParams.dstRect;
2142
2143    blitParams.enabledBuffers.set(0);
2144
2145    blitParams.src      = colorReadRT->getTexture();
2146    blitParams.srcLevel = colorReadRT->getLevelIndex();
2147    blitParams.srcLayer = colorReadRT->getLayerIndex();
2148
2149    blitParams.srcNormalizedCoords = mtl::NormalizedCoords(
2150        clippedSourceArea, colorReadRT->getTexture()->size(blitParams.srcLevel));
2151    blitParams.srcYFlipped  = source->flipY();
2152    blitParams.dstLuminance = internalFormat.isLUMA();
2153
2154    return displayMtl->getUtils().blitColorWithDraw(
2155        context, cmdEncoder, colorReadRT->getFormat()->actualAngleFormat(), blitParams);
2156}
2157
2158angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
2159                                          const gl::ImageIndex &index,
2160                                          const gl::Offset &modifiedDestOffset,
2161                                          const gl::Rectangle &clippedSourceArea,
2162                                          const gl::InternalFormat &internalFormat,
2163                                          const FramebufferMtl *source,
2164                                          const RenderTargetMtl *colorReadRT)
2165{
2166    mtl::TextureRef &image = getImage(index);
2167    ASSERT(image && image->valid());
2168
2169    ContextMtl *contextMtl = mtl::GetImpl(context);
2170
2171    const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
2172    const int dstRowPitch          = dstFormat.pixelBytes * clippedSourceArea.width;
2173    angle::MemoryBuffer conversionRow;
2174    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
2175
2176    gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1);
2177    MTLRegion mtlDstRowArea  = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1);
2178    uint32_t dstSlice        = 0;
2179    switch (index.getType())
2180    {
2181        case gl::TextureType::_2D:
2182        case gl::TextureType::CubeMap:
2183            dstSlice = 0;
2184            break;
2185        case gl::TextureType::_2DArray:
2186            ASSERT(index.hasLayer());
2187            dstSlice = index.getLayerIndex();
2188            break;
2189        case gl::TextureType::_3D:
2190            ASSERT(index.hasLayer());
2191            dstSlice               = 0;
2192            mtlDstRowArea.origin.z = index.getLayerIndex();
2193            break;
2194        default:
2195            UNREACHABLE();
2196    }
2197
2198    // Copy row by row:
2199    for (int r = 0; r < clippedSourceArea.height; ++r)
2200    {
2201        mtlDstRowArea.origin.y = modifiedDestOffset.y + r;
2202        srcRowArea.y           = clippedSourceArea.y + r;
2203
2204        PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0);
2205
2206        // Read pixels from framebuffer to memory:
2207        gl::Rectangle flippedSrcRowArea = source->getCorrectFlippedReadArea(context, srcRowArea);
2208        ANGLE_TRY(source->readPixelsImpl(context, flippedSrcRowArea, packParams, colorReadRT,
2209                                         conversionRow.data()));
2210
2211        // Upload to texture
2212        ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlDstRowArea, mtl::kZeroNativeMipLevel,
2213                                        dstSlice, conversionRow.data(), dstRowPitch, 0, image));
2214    }
2215
2216    return angle::Result::Continue;
2217}
2218
2219angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context,
2220                                             const gl::ImageIndex &index,
2221                                             const gl::Offset &destOffset,
2222                                             const gl::InternalFormat &internalFormat,
2223                                             GLint sourceLevel,
2224                                             const gl::Box &sourceBox,
2225                                             bool unpackFlipY,
2226                                             bool unpackPremultiplyAlpha,
2227                                             bool unpackUnmultiplyAlpha,
2228                                             const gl::Texture *source)
2229{
2230    // Only 2D textures are supported.
2231    ASSERT(sourceBox.depth == 1);
2232    ASSERT(source->getType() == gl::TextureType::_2D);
2233    gl::ImageIndex sourceIndex = gl::ImageIndex::Make2D(sourceLevel);
2234
2235    ContextMtl *contextMtl = mtl::GetImpl(context);
2236    TextureMtl *sourceMtl  = mtl::GetImpl(source);
2237
2238    ANGLE_TRY(ensureImageCreated(context, index));
2239
2240    ANGLE_TRY(sourceMtl->ensureImageCreated(context, sourceIndex));
2241
2242    const ImageDefinitionMtl &srcImageDef  = sourceMtl->getImageDefinition(sourceIndex);
2243    const mtl::TextureRef &sourceImage     = srcImageDef.image;
2244    const mtl::Format &sourceFormat        = contextMtl->getPixelFormat(srcImageDef.formatID);
2245    const angle::Format &sourceAngleFormat = sourceFormat.actualAngleFormat();
2246
2247    if (!mFormat.getCaps().isRenderable())
2248    {
2249        return copySubTextureCPU(context, index, destOffset, internalFormat,
2250                                 mtl::kZeroNativeMipLevel, sourceBox, sourceAngleFormat,
2251                                 unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
2252                                 sourceImage);
2253    }
2254    return copySubTextureWithDraw(
2255        context, index, destOffset, internalFormat, mtl::kZeroNativeMipLevel, sourceBox,
2256        sourceAngleFormat, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, sourceImage);
2257}
2258
2259angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
2260                                                 const gl::ImageIndex &index,
2261                                                 const gl::Offset &destOffset,
2262                                                 const gl::InternalFormat &internalFormat,
2263                                                 const mtl::MipmapNativeLevel &sourceNativeLevel,
2264                                                 const gl::Box &sourceBox,
2265                                                 const angle::Format &sourceAngleFormat,
2266                                                 bool unpackFlipY,
2267                                                 bool unpackPremultiplyAlpha,
2268                                                 bool unpackUnmultiplyAlpha,
2269                                                 const mtl::TextureRef &sourceTexture)
2270{
2271    ContextMtl *contextMtl = mtl::GetImpl(context);
2272    DisplayMtl *displayMtl = contextMtl->getDisplay();
2273
2274    mtl::TextureRef image = getImage(index);
2275    ASSERT(image && image->valid());
2276
2277    if (internalFormat.colorEncoding == GL_SRGB)
2278    {
2279        image = image->getLinearColorView();
2280    }
2281
2282    mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getTextureRenderCommandEncoder(
2283        image, mtl::ImageNativeIndex::FromBaseZeroGLIndex(GetZeroLevelIndex(image)));
2284    mtl::ColorBlitParams blitParams;
2285
2286    blitParams.dstTextureSize = image->sizeAt0();
2287    blitParams.dstRect =
2288        gl::Rectangle(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
2289    blitParams.dstScissorRect = blitParams.dstRect;
2290
2291    blitParams.enabledBuffers.set(0);
2292
2293    blitParams.src      = sourceTexture;
2294    blitParams.srcLevel = sourceNativeLevel;
2295    blitParams.srcLayer = 0;
2296    blitParams.srcNormalizedCoords =
2297        mtl::NormalizedCoords(sourceBox.toRect(), sourceTexture->size(sourceNativeLevel));
2298    blitParams.srcYFlipped            = false;
2299    blitParams.dstLuminance           = internalFormat.isLUMA();
2300    blitParams.unpackFlipY            = unpackFlipY;
2301    blitParams.unpackPremultiplyAlpha = unpackPremultiplyAlpha;
2302    blitParams.unpackUnmultiplyAlpha  = unpackUnmultiplyAlpha;
2303
2304    return displayMtl->getUtils().copyTextureWithDraw(context, cmdEncoder, sourceAngleFormat,
2305                                                      mFormat.actualAngleFormat(), blitParams);
2306}
2307
2308angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
2309                                            const gl::ImageIndex &index,
2310                                            const gl::Offset &destOffset,
2311                                            const gl::InternalFormat &internalFormat,
2312                                            const mtl::MipmapNativeLevel &sourceNativeLevel,
2313                                            const gl::Box &sourceBox,
2314                                            const angle::Format &sourceAngleFormat,
2315                                            bool unpackFlipY,
2316                                            bool unpackPremultiplyAlpha,
2317                                            bool unpackUnmultiplyAlpha,
2318                                            const mtl::TextureRef &sourceTexture)
2319{
2320    mtl::TextureRef &image = getImage(index);
2321    ASSERT(image && image->valid());
2322
2323    ContextMtl *contextMtl = mtl::GetImpl(context);
2324
2325    const angle::Format &dstAngleFormat = mFormat.actualAngleFormat();
2326    const int srcRowPitch               = sourceAngleFormat.pixelBytes * sourceBox.width;
2327    const int srcImageSize              = srcRowPitch * sourceBox.height;
2328    const int convRowPitch              = dstAngleFormat.pixelBytes * sourceBox.width;
2329    const int convImageSize             = convRowPitch * sourceBox.height;
2330    angle::MemoryBuffer conversionSrc, conversionDst;
2331    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionSrc.resize(srcImageSize));
2332    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionDst.resize(convImageSize));
2333
2334    MTLRegion mtlSrcArea =
2335        MTLRegionMake2D(sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height);
2336    MTLRegion mtlDstArea =
2337        MTLRegionMake2D(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
2338
2339    // Read pixels from source to memory:
2340    sourceTexture->getBytes(contextMtl, srcRowPitch, 0, mtlSrcArea, sourceNativeLevel, 0,
2341                            conversionSrc.data());
2342
2343    // Convert to destination format
2344    CopyImageCHROMIUM(conversionSrc.data(), srcRowPitch, sourceAngleFormat.pixelBytes, 0,
2345                      sourceAngleFormat.pixelReadFunction, conversionDst.data(), convRowPitch,
2346                      dstAngleFormat.pixelBytes, 0, dstAngleFormat.pixelWriteFunction,
2347                      internalFormat.format, internalFormat.componentType, sourceBox.width,
2348                      sourceBox.height, 1, unpackFlipY, unpackPremultiplyAlpha,
2349                      unpackUnmultiplyAlpha);
2350
2351    // Upload to texture
2352    ANGLE_TRY(UploadTextureContents(context, dstAngleFormat, mtlDstArea, mtl::kZeroNativeMipLevel,
2353                                    0, conversionDst.data(), convRowPitch, 0, image));
2354
2355    return angle::Result::Continue;
2356}
2357
2358}
2359