• 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 "libANGLE/renderer/metal/ContextMtl.h"
18#include "libANGLE/renderer/metal/DisplayMtl.h"
19#include "libANGLE/renderer/metal/FrameBufferMtl.h"
20#include "libANGLE/renderer/metal/mtl_common.h"
21#include "libANGLE/renderer/metal/mtl_format_utils.h"
22#include "libANGLE/renderer/metal/mtl_utils.h"
23#include "libANGLE/renderer/renderer_utils.h"
24
25namespace rx
26{
27
28namespace
29{
30
31MTLColorWriteMask GetColorWriteMask(const mtl::Format &mtlFormat, bool *emulatedChannelsOut)
32{
33    const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat();
34    const angle::Format &actualFormat   = mtlFormat.actualAngleFormat();
35    bool emulatedChannels               = false;
36    MTLColorWriteMask colorWritableMask = MTLColorWriteMaskAll;
37    if (intendedFormat.alphaBits == 0 && actualFormat.alphaBits)
38    {
39        emulatedChannels = true;
40        // Disable alpha write to this texture
41        colorWritableMask = colorWritableMask & (~MTLColorWriteMaskAlpha);
42    }
43    if (intendedFormat.luminanceBits == 0)
44    {
45        if (intendedFormat.redBits == 0 && actualFormat.redBits)
46        {
47            emulatedChannels = true;
48            // Disable red write to this texture
49            colorWritableMask = colorWritableMask & (~MTLColorWriteMaskRed);
50        }
51        if (intendedFormat.greenBits == 0 && actualFormat.greenBits)
52        {
53            emulatedChannels = true;
54            // Disable green write to this texture
55            colorWritableMask = colorWritableMask & (~MTLColorWriteMaskGreen);
56        }
57        if (intendedFormat.blueBits == 0 && actualFormat.blueBits)
58        {
59            emulatedChannels = true;
60            // Disable blue write to this texture
61            colorWritableMask = colorWritableMask & (~MTLColorWriteMaskBlue);
62        }
63    }
64
65    *emulatedChannelsOut = emulatedChannels;
66
67    return colorWritableMask;
68}
69
70gl::ImageIndex GetImageBaseLevelIndex(const mtl::TextureRef &image)
71{
72    gl::ImageIndex imageBaseIndex;
73    switch (image->textureType())
74    {
75        case MTLTextureType2D:
76            imageBaseIndex = gl::ImageIndex::Make2D(0);
77            break;
78        default:
79            UNREACHABLE();
80            break;
81    }
82
83    return imageBaseIndex;
84}
85
86GLuint GetImageLayerIndex(const gl::ImageIndex &index)
87{
88    switch (index.getType())
89    {
90        case gl::TextureType::_2D:
91            return 0;
92        case gl::TextureType::CubeMap:
93            return index.cubeMapFaceIndex();
94        default:
95            UNREACHABLE();
96    }
97
98    return 0;
99}
100
101// Given texture type, get texture type of one image at a slice and a level.
102// For example, for texture 2d, one image is also texture 2d.
103// for texture cube, one image is texture 2d.
104// For texture 2d array, one image is texture 2d.
105gl::TextureType GetTextureImageType(gl::TextureType texType)
106{
107    switch (texType)
108    {
109        case gl::TextureType::_2D:
110        case gl::TextureType::CubeMap:
111            return gl::TextureType::_2D;
112        default:
113            UNREACHABLE();
114            return gl::TextureType::InvalidEnum;
115    }
116}
117
118angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl,
119                                                 const angle::Format &textureAngleFormat,
120                                                 const angle::Format &stagingAngleFormat,
121                                                 const MTLSize &regionSize,
122                                                 const uint8_t *data,
123                                                 size_t bytesPerRow,
124                                                 size_t *bufferRowPitchOut,
125                                                 size_t *buffer2DImageSizeOut,
126                                                 mtl::BufferRef *bufferOut)
127{
128    // NOTE(hqle): 3D textures not supported yet.
129    ASSERT(regionSize.depth == 1);
130
131    size_t stagingBufferRowPitch    = regionSize.width * stagingAngleFormat.pixelBytes;
132    size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height;
133    size_t stagingBufferSize        = stagingBuffer2DImageSize * regionSize.depth;
134    mtl::BufferRef stagingBuffer;
135    ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer));
136
137    uint8_t *pdst = stagingBuffer->map(contextMtl);
138
139    if (textureAngleFormat.id == stagingAngleFormat.id)
140    {
141        for (NSUInteger r = 0; r < regionSize.height; ++r)
142        {
143            const uint8_t *pCopySrc = data + r * bytesPerRow;
144            uint8_t *pCopyDst       = pdst + r * stagingBufferRowPitch;
145            memcpy(pCopyDst, pCopySrc, stagingBufferRowPitch);
146        }
147    }
148    else
149    {
150        // This is only for depth & stencil case.
151        ASSERT(textureAngleFormat.depthBits || textureAngleFormat.stencilBits);
152        ASSERT(textureAngleFormat.pixelReadFunction && stagingAngleFormat.pixelWriteFunction);
153
154        // cache to store read result of source pixel
155        angle::DepthStencil depthStencilData;
156        auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData);
157        ASSERT(textureAngleFormat.pixelBytes <= sizeof(depthStencilData));
158
159        for (NSUInteger r = 0; r < regionSize.height; ++r)
160        {
161            for (NSUInteger c = 0; c < regionSize.width; ++c)
162            {
163                const uint8_t *sourcePixelData =
164                    data + r * bytesPerRow + c * textureAngleFormat.pixelBytes;
165
166                uint8_t *destPixelData =
167                    pdst + r * stagingBufferRowPitch + c * stagingAngleFormat.pixelBytes;
168
169                textureAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData);
170                stagingAngleFormat.pixelWriteFunction(sourcePixelReadData, destPixelData);
171            }
172        }
173    }
174
175    stagingBuffer->unmap(contextMtl);
176
177    *bufferOut            = stagingBuffer;
178    *bufferRowPitchOut    = stagingBufferRowPitch;
179    *buffer2DImageSizeOut = stagingBuffer2DImageSize;
180
181    return angle::Result::Continue;
182}
183
184angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl,
185                                                     const mtl::TextureRef &texture,
186                                                     const angle::Format &textureAngleFormat,
187                                                     MTLRegion region,
188                                                     uint32_t mipmapLevel,
189                                                     uint32_t slice,
190                                                     const uint8_t *data,
191                                                     size_t bytesPerRow)
192{
193    ASSERT(texture && texture->valid());
194
195    ASSERT(!texture->isCPUAccessible());
196
197    ASSERT(!textureAngleFormat.depthBits || !textureAngleFormat.stencilBits);
198
199    // Compressed texture is not supporte atm
200    ASSERT(!textureAngleFormat.isBlock);
201
202    // Copy data to staging buffer
203    size_t stagingBufferRowPitch;
204    size_t stagingBuffer2DImageSize;
205    mtl::BufferRef stagingBuffer;
206    ANGLE_TRY(CopyTextureContentsToStagingBuffer(
207        contextMtl, textureAngleFormat, textureAngleFormat, region.size, data, bytesPerRow,
208        &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer));
209
210    // Copy staging buffer to texture.
211    mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
212    encoder->copyBufferToTexture(stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize,
213                                 region.size, texture, slice, mipmapLevel, region.origin,
214                                 MTLBlitOptionNone);
215
216    return angle::Result::Continue;
217}
218
219// Packed depth stencil upload using staging buffer
220angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer(
221    ContextMtl *contextMtl,
222    const mtl::TextureRef &texture,
223    const angle::Format &textureAngleFormat,
224    MTLRegion region,
225    uint32_t mipmapLevel,
226    uint32_t slice,
227    const uint8_t *data,
228    size_t bytesPerRow)
229{
230    ASSERT(texture && texture->valid());
231
232    ASSERT(!texture->isCPUAccessible());
233
234    ASSERT(textureAngleFormat.depthBits && textureAngleFormat.stencilBits);
235
236    // We have to split the depth & stencil data into 2 buffers.
237    angle::FormatID stagingDepthBufferFormatId;
238    angle::FormatID stagingStencilBufferFormatId;
239
240    switch (textureAngleFormat.id)
241    {
242        case angle::FormatID::D24_UNORM_S8_UINT:
243            stagingDepthBufferFormatId   = angle::FormatID::D24_UNORM_X8_UINT;
244            stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
245            break;
246        case angle::FormatID::D32_FLOAT_S8X24_UINT:
247            stagingDepthBufferFormatId   = angle::FormatID::D32_FLOAT;
248            stagingStencilBufferFormatId = angle::FormatID::S8_UINT;
249            break;
250        default:
251            ANGLE_MTL_UNREACHABLE(contextMtl);
252    }
253
254    const angle::Format &angleStagingDepthFormat = angle::Format::Get(stagingDepthBufferFormatId);
255    const angle::Format &angleStagingStencilFormat =
256        angle::Format::Get(stagingStencilBufferFormatId);
257
258    size_t stagingDepthBufferRowPitch, stagingStencilBufferRowPitch;
259    size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize;
260    mtl::BufferRef stagingDepthbuffer, stagingStencilBuffer;
261
262    ANGLE_TRY(CopyTextureContentsToStagingBuffer(
263        contextMtl, textureAngleFormat, angleStagingDepthFormat, region.size, data, bytesPerRow,
264        &stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthbuffer));
265
266    ANGLE_TRY(CopyTextureContentsToStagingBuffer(
267        contextMtl, textureAngleFormat, angleStagingStencilFormat, region.size, data, bytesPerRow,
268        &stagingStencilBufferRowPitch, &stagingStencilBuffer2DImageSize, &stagingStencilBuffer));
269
270    mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder();
271
272    encoder->copyBufferToTexture(stagingDepthbuffer, 0, stagingDepthBufferRowPitch,
273                                 stagingDepthBuffer2DImageSize, region.size, texture, slice,
274                                 mipmapLevel, region.origin, MTLBlitOptionDepthFromDepthStencil);
275    encoder->copyBufferToTexture(stagingStencilBuffer, 0, stagingStencilBufferRowPitch,
276                                 stagingStencilBuffer2DImageSize, region.size, texture, slice,
277                                 mipmapLevel, region.origin, MTLBlitOptionStencilFromDepthStencil);
278
279    return angle::Result::Continue;
280}
281
282angle::Result UploadTextureContents(const gl::Context *context,
283                                    const mtl::TextureRef &texture,
284                                    const angle::Format &textureAngleFormat,
285                                    const MTLRegion &region,
286                                    uint32_t mipmapLevel,
287                                    uint32_t slice,
288                                    const uint8_t *data,
289                                    size_t bytesPerRow)
290{
291    ASSERT(texture && texture->valid());
292    ContextMtl *contextMtl = mtl::GetImpl(context);
293
294    if (texture->isCPUAccessible())
295    {
296        // If texture is CPU accessible, just call replaceRegion() directly.
297        texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow);
298
299        return angle::Result::Continue;
300    }
301
302    // Texture is not CPU accessible, we need to use staging buffer
303    if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits)
304    {
305        ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer(
306            contextMtl, texture, textureAngleFormat, region, mipmapLevel, slice, data,
307            bytesPerRow));
308    }
309    else
310    {
311        ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, texture, textureAngleFormat,
312                                                         region, mipmapLevel, slice, data,
313                                                         bytesPerRow));
314    }
315
316    return angle::Result::Continue;
317}
318
319}  // namespace
320
321// TextureMtl implementation
322TextureMtl::TextureMtl(const gl::TextureState &state) : TextureImpl(state) {}
323
324TextureMtl::~TextureMtl() = default;
325
326void TextureMtl::onDestroy(const gl::Context *context)
327{
328    releaseTexture(true);
329}
330
331void TextureMtl::releaseTexture(bool releaseImages)
332{
333    mFormat = mtl::Format();
334
335    mNativeTexture     = nullptr;
336    mMetalSamplerState = nil;
337
338    for (RenderTargetMtl &rt : mLayeredRenderTargets)
339    {
340        rt.set(nullptr);
341    }
342
343    if (releaseImages)
344    {
345        mTexImages.clear();
346    }
347
348    mLayeredTextureViews.clear();
349
350    mIsPow2 = false;
351}
352
353angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
354{
355    if (mNativeTexture)
356    {
357        return angle::Result::Continue;
358    }
359
360    ContextMtl *contextMtl = mtl::GetImpl(context);
361
362    // Create actual texture object:
363    int layers                = 0;
364    const GLuint mips         = mState.getMipmapMaxLevel() + 1;
365    const gl::ImageDesc &desc = mState.getBaseLevelDesc();
366
367    mIsPow2 = gl::isPow2(desc.size.width) && gl::isPow2(desc.size.height);
368
369    switch (mState.getType())
370    {
371        case gl::TextureType::_2D:
372            layers = 1;
373            ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mFormat, desc.size.width,
374                                                  desc.size.height, mips, false, true,
375                                                  &mNativeTexture));
376            mLayeredRenderTargets.resize(1);
377            mLayeredRenderTargets[0].set(mNativeTexture, 0, 0, mFormat);
378            mLayeredTextureViews.resize(1);
379            mLayeredTextureViews[0] = mNativeTexture;
380            break;
381        case gl::TextureType::CubeMap:
382            layers = 6;
383            ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, desc.size.width, mips,
384                                                    false, true, &mNativeTexture));
385            mLayeredRenderTargets.resize(gl::kCubeFaceCount);
386            mLayeredTextureViews.resize(gl::kCubeFaceCount);
387            for (uint32_t f = 0; f < gl::kCubeFaceCount; ++f)
388            {
389                mLayeredTextureViews[f] = mNativeTexture->createCubeFaceView(f);
390                mLayeredRenderTargets[f].set(mLayeredTextureViews[f], 0, 0, mFormat);
391            }
392            break;
393        default:
394            UNREACHABLE();
395    }
396
397    ANGLE_TRY(checkForEmulatedChannels(context, mFormat, mNativeTexture));
398
399    // Transfer data from images to actual texture object
400    mtl::BlitCommandEncoder *encoder = nullptr;
401    for (int layer = 0; layer < layers; ++layer)
402    {
403        for (GLuint mip = 0; mip < mips; ++mip)
404        {
405            mtl::TextureRef &imageToTransfer = mTexImages[layer][mip];
406
407            // Only transfer if this mip & slice image has been defined and in correct size &
408            // format.
409            gl::Extents actualMipSize = mNativeTexture->size(mip);
410            if (imageToTransfer && imageToTransfer->size() == actualMipSize &&
411                imageToTransfer->pixelFormat() == mNativeTexture->pixelFormat())
412            {
413                MTLSize mtlSize =
414                    MTLSizeMake(actualMipSize.width, actualMipSize.height, actualMipSize.depth);
415                MTLOrigin mtlOrigin = MTLOriginMake(0, 0, 0);
416
417                if (!encoder)
418                {
419                    encoder = contextMtl->getBlitCommandEncoder();
420                }
421                encoder->copyTexture(mNativeTexture, layer, mip, mtlOrigin, mtlSize,
422                                     imageToTransfer, 0, 0, mtlOrigin);
423            }
424
425            imageToTransfer = nullptr;
426            // Make this image the actual texture object's view at this mip and slice.
427            // So that in future, glTexSubImage* will update the actual texture
428            // directly.
429            mTexImages[layer][mip] = mNativeTexture->createSliceMipView(layer, mip);
430        }
431    }
432
433    // Create sampler state
434    ANGLE_TRY(ensureSamplerStateCreated(context));
435
436    return angle::Result::Continue;
437}
438
439angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context)
440{
441    if (mMetalSamplerState)
442    {
443        return angle::Result::Continue;
444    }
445
446    ContextMtl *contextMtl = mtl::GetImpl(context);
447    DisplayMtl *displayMtl = contextMtl->getDisplay();
448
449    mtl::SamplerDesc samplerDesc(mState.getSamplerState());
450
451    if (mFormat.actualAngleFormat().depthBits &&
452        !displayMtl->getFeatures().hasDepthTextureFiltering.enabled)
453    {
454        // On devices not supporting filtering for depth textures, we need to convert to nearest
455        // here.
456        samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
457        samplerDesc.magFilter = MTLSamplerMinMagFilterNearest;
458        if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped)
459        {
460            samplerDesc.mipFilter = MTLSamplerMipFilterNearest;
461        }
462
463        samplerDesc.maxAnisotropy = 1;
464    }
465
466    mMetalSamplerState =
467        displayMtl->getStateCache().getSamplerState(displayMtl->getMetalDevice(), samplerDesc);
468
469    return angle::Result::Continue;
470}
471
472angle::Result TextureMtl::ensureImageCreated(const gl::Context *context,
473                                             const gl::ImageIndex &index)
474{
475    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
476    if (!image)
477    {
478        // Image at this level hasn't been defined yet. We need to define it:
479        const gl::ImageDesc &desc = mState.getImageDesc(index);
480        ANGLE_TRY(redefineImage(context, index, mFormat, desc.size));
481    }
482    return angle::Result::Continue;
483}
484
485angle::Result TextureMtl::setImage(const gl::Context *context,
486                                   const gl::ImageIndex &index,
487                                   GLenum internalFormat,
488                                   const gl::Extents &size,
489                                   GLenum format,
490                                   GLenum type,
491                                   const gl::PixelUnpackState &unpack,
492                                   gl::Buffer *unpackBuffer,
493                                   const uint8_t *pixels)
494{
495    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
496
497    return setImageImpl(context, index, formatInfo, size, type, unpack, pixels);
498}
499
500angle::Result TextureMtl::setSubImage(const gl::Context *context,
501                                      const gl::ImageIndex &index,
502                                      const gl::Box &area,
503                                      GLenum format,
504                                      GLenum type,
505                                      const gl::PixelUnpackState &unpack,
506                                      gl::Buffer *unpackBuffer,
507                                      const uint8_t *pixels)
508{
509    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type);
510
511    return setSubImageImpl(context, index, area, formatInfo, type, unpack, pixels);
512}
513
514angle::Result TextureMtl::setCompressedImage(const gl::Context *context,
515                                             const gl::ImageIndex &index,
516                                             GLenum internalFormat,
517                                             const gl::Extents &size,
518                                             const gl::PixelUnpackState &unpack,
519                                             size_t imageSize,
520                                             const uint8_t *pixels)
521{
522    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
523
524    return setImageImpl(context, index, formatInfo, size, GL_UNSIGNED_BYTE, unpack, pixels);
525}
526
527angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context,
528                                                const gl::ImageIndex &index,
529                                                const gl::Box &area,
530                                                GLenum format,
531                                                const gl::PixelUnpackState &unpack,
532                                                size_t imageSize,
533                                                const uint8_t *pixels)
534{
535
536    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE);
537
538    return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, pixels);
539}
540
541angle::Result TextureMtl::copyImage(const gl::Context *context,
542                                    const gl::ImageIndex &index,
543                                    const gl::Rectangle &sourceArea,
544                                    GLenum internalFormat,
545                                    gl::Framebuffer *source)
546{
547    gl::Extents newImageSize(sourceArea.width, sourceArea.height, 1);
548    const gl::InternalFormat &internalFormatInfo =
549        gl::GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE);
550
551    ContextMtl *contextMtl = mtl::GetImpl(context);
552    angle::FormatID angleFormatId =
553        angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
554    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
555
556    ANGLE_TRY(redefineImage(context, index, mtlFormat, newImageSize));
557
558    if (context->isWebGL())
559    {
560        ANGLE_TRY(initializeContents(context, index));
561    }
562
563    return copySubImageImpl(context, index, gl::Offset(0, 0, 0), sourceArea, internalFormatInfo,
564                            source);
565}
566
567angle::Result TextureMtl::copySubImage(const gl::Context *context,
568                                       const gl::ImageIndex &index,
569                                       const gl::Offset &destOffset,
570                                       const gl::Rectangle &sourceArea,
571                                       gl::Framebuffer *source)
572{
573    const gl::InternalFormat &currentFormat = *mState.getImageDesc(index).format.info;
574    return copySubImageImpl(context, index, destOffset, sourceArea, currentFormat, source);
575}
576
577angle::Result TextureMtl::copyTexture(const gl::Context *context,
578                                      const gl::ImageIndex &index,
579                                      GLenum internalFormat,
580                                      GLenum type,
581                                      size_t sourceLevel,
582                                      bool unpackFlipY,
583                                      bool unpackPremultiplyAlpha,
584                                      bool unpackUnmultiplyAlpha,
585                                      const gl::Texture *source)
586{
587    UNIMPLEMENTED();
588
589    return angle::Result::Stop;
590}
591
592angle::Result TextureMtl::copySubTexture(const gl::Context *context,
593                                         const gl::ImageIndex &index,
594                                         const gl::Offset &destOffset,
595                                         size_t sourceLevel,
596                                         const gl::Box &sourceBox,
597                                         bool unpackFlipY,
598                                         bool unpackPremultiplyAlpha,
599                                         bool unpackUnmultiplyAlpha,
600                                         const gl::Texture *source)
601{
602    UNIMPLEMENTED();
603
604    return angle::Result::Stop;
605}
606
607angle::Result TextureMtl::copyCompressedTexture(const gl::Context *context,
608                                                const gl::Texture *source)
609{
610    UNIMPLEMENTED();
611
612    return angle::Result::Stop;
613}
614
615angle::Result TextureMtl::setStorage(const gl::Context *context,
616                                     gl::TextureType type,
617                                     size_t mipmaps,
618                                     GLenum internalFormat,
619                                     const gl::Extents &size)
620{
621    ContextMtl *contextMtl               = mtl::GetImpl(context);
622    const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
623    angle::FormatID angleFormatId =
624        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
625    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
626
627    return setStorageImpl(context, type, mipmaps, mtlFormat, size);
628}
629
630angle::Result TextureMtl::setStorageExternalMemory(const gl::Context *context,
631                                                   gl::TextureType type,
632                                                   size_t levels,
633                                                   GLenum internalFormat,
634                                                   const gl::Extents &size,
635                                                   gl::MemoryObject *memoryObject,
636                                                   GLuint64 offset)
637{
638    UNIMPLEMENTED();
639
640    return angle::Result::Stop;
641}
642
643angle::Result TextureMtl::setStorageMultisample(const gl::Context *context,
644                                                gl::TextureType type,
645                                                GLsizei samples,
646                                                GLint internalformat,
647                                                const gl::Extents &size,
648                                                bool fixedSampleLocations)
649{
650    UNIMPLEMENTED();
651
652    return angle::Result::Stop;
653}
654
655angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context,
656                                            gl::TextureType type,
657                                            egl::Image *image)
658{
659    UNIMPLEMENTED();
660
661    return angle::Result::Stop;
662}
663
664angle::Result TextureMtl::setImageExternal(const gl::Context *context,
665                                           gl::TextureType type,
666                                           egl::Stream *stream,
667                                           const egl::Stream::GLTextureDescription &desc)
668{
669    UNIMPLEMENTED();
670    return angle::Result::Stop;
671}
672
673angle::Result TextureMtl::generateMipmap(const gl::Context *context)
674{
675    ANGLE_TRY(ensureTextureCreated(context));
676
677    ContextMtl *contextMtl = mtl::GetImpl(context);
678    if (!mNativeTexture)
679    {
680        return angle::Result::Continue;
681    }
682
683    const gl::TextureCapsMap &textureCapsMap = contextMtl->getNativeTextureCaps();
684    const gl::TextureCaps &textureCaps       = textureCapsMap.get(mFormat.intendedFormatId);
685
686    if (textureCaps.filterable && textureCaps.renderbuffer)
687    {
688        mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
689        blitEncoder->generateMipmapsForTexture(mNativeTexture);
690    }
691    else
692    {
693        ANGLE_TRY(generateMipmapCPU(context));
694    }
695
696    return angle::Result::Continue;
697}
698
699angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context)
700{
701    ASSERT(mNativeTexture && mNativeTexture->valid());
702    ASSERT(mLayeredTextureViews.size() <= std::numeric_limits<uint32_t>::max());
703    uint32_t layers = static_cast<uint32_t>(mLayeredTextureViews.size());
704
705    ContextMtl *contextMtl           = mtl::GetImpl(context);
706    const angle::Format &angleFormat = mFormat.actualAngleFormat();
707    // This format must have mip generation function.
708    ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction);
709
710    // NOTE(hqle): Support base level of ES 3.0.
711    for (uint32_t layer = 0; layer < layers; ++layer)
712    {
713        int maxMipLevel = static_cast<int>(mNativeTexture->mipmapLevels()) - 1;
714        int firstLevel  = 0;
715
716        uint32_t prevLevelWidth  = mNativeTexture->width();
717        uint32_t prevLevelHeight = mNativeTexture->height();
718        size_t prevLevelRowPitch = angleFormat.pixelBytes * prevLevelWidth;
719        std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow)
720                                                     uint8_t[prevLevelRowPitch * prevLevelHeight]);
721        ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData);
722        std::unique_ptr<uint8_t[]> dstLevelData;
723
724        // Download base level data
725        mLayeredTextureViews[layer]->getBytes(
726            contextMtl, prevLevelRowPitch, MTLRegionMake2D(0, 0, prevLevelWidth, prevLevelHeight),
727            firstLevel, prevLevelData.get());
728
729        for (int mip = firstLevel + 1; mip <= maxMipLevel; ++mip)
730        {
731            uint32_t dstWidth  = mNativeTexture->width(mip);
732            uint32_t dstHeight = mNativeTexture->height(mip);
733
734            size_t dstRowPitch = angleFormat.pixelBytes * dstWidth;
735            size_t dstDataSize = dstRowPitch * dstHeight;
736            if (!dstLevelData)
737            {
738                // Allocate once and reuse the buffer
739                dstLevelData.reset(new (std::nothrow) uint8_t[dstDataSize]);
740                ANGLE_CHECK_GL_ALLOC(contextMtl, dstLevelData);
741            }
742
743            // Generate mip level
744            angleFormat.mipGenerationFunction(prevLevelWidth, prevLevelHeight, 1,
745                                              prevLevelData.get(), prevLevelRowPitch, 0,
746                                              dstLevelData.get(), dstRowPitch, 0);
747
748            // Upload to texture
749            ANGLE_TRY(UploadTextureContents(context, mNativeTexture, angleFormat,
750                                            MTLRegionMake2D(0, 0, dstWidth, dstHeight), mip, layer,
751                                            dstLevelData.get(), dstRowPitch));
752
753            prevLevelWidth    = dstWidth;
754            prevLevelHeight   = dstHeight;
755            prevLevelRowPitch = dstRowPitch;
756            std::swap(prevLevelData, dstLevelData);
757        }  // for mip level
758
759    }  // For layers
760
761    return angle::Result::Continue;
762}
763
764angle::Result TextureMtl::setBaseLevel(const gl::Context *context, GLuint baseLevel)
765{
766    // NOTE(hqle): ES 3.0
767    UNIMPLEMENTED();
768
769    return angle::Result::Stop;
770}
771
772angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface *surface)
773{
774    UNIMPLEMENTED();
775
776    return angle::Result::Stop;
777}
778
779angle::Result TextureMtl::releaseTexImage(const gl::Context *context)
780{
781    UNIMPLEMENTED();
782
783    return angle::Result::Stop;
784}
785
786angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context,
787                                                    GLenum binding,
788                                                    const gl::ImageIndex &imageIndex,
789                                                    GLsizei samples,
790                                                    FramebufferAttachmentRenderTarget **rtOut)
791{
792    ANGLE_TRY(ensureTextureCreated(context));
793    // NOTE(hqle): Support MSAA.
794    // Non-zero mip level attachments are an ES 3.0 feature.
795    ASSERT(imageIndex.getLevelIndex() == 0);
796
797    ContextMtl *contextMtl = mtl::GetImpl(context);
798    ANGLE_MTL_TRY(contextMtl, mNativeTexture);
799
800    switch (imageIndex.getType())
801    {
802        case gl::TextureType::_2D:
803            *rtOut = &mLayeredRenderTargets[0];
804            break;
805        case gl::TextureType::CubeMap:
806            *rtOut = &mLayeredRenderTargets[imageIndex.cubeMapFaceIndex()];
807            break;
808        default:
809            UNREACHABLE();
810    }
811
812    return angle::Result::Continue;
813}
814
815angle::Result TextureMtl::syncState(const gl::Context *context,
816                                    const gl::Texture::DirtyBits &dirtyBits)
817{
818    if (dirtyBits.any())
819    {
820        // Invalidate sampler state
821        mMetalSamplerState = nil;
822    }
823
824    ANGLE_TRY(ensureTextureCreated(context));
825    ANGLE_TRY(ensureSamplerStateCreated(context));
826
827    return angle::Result::Continue;
828}
829
830angle::Result TextureMtl::bindVertexShader(const gl::Context *context,
831                                           mtl::RenderCommandEncoder *cmdEncoder,
832                                           int textureSlotIndex,
833                                           int samplerSlotIndex)
834{
835    ASSERT(mNativeTexture);
836    // ES 2.0: non power of two texture won't have any mipmap.
837    // We don't support OES_texture_npot atm.
838    float maxLodClamp = FLT_MAX;
839    if (!mIsPow2)
840    {
841        maxLodClamp = 0;
842    }
843
844    cmdEncoder->setVertexTexture(mNativeTexture, textureSlotIndex);
845    cmdEncoder->setVertexSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex);
846
847    return angle::Result::Continue;
848}
849
850angle::Result TextureMtl::bindFragmentShader(const gl::Context *context,
851                                             mtl::RenderCommandEncoder *cmdEncoder,
852                                             int textureSlotIndex,
853                                             int samplerSlotIndex)
854{
855    ASSERT(mNativeTexture);
856    // ES 2.0: non power of two texture won't have any mipmap.
857    // We don't support OES_texture_npot atm.
858    float maxLodClamp = FLT_MAX;
859    if (!mIsPow2)
860    {
861        maxLodClamp = 0;
862    }
863
864    cmdEncoder->setFragmentTexture(mNativeTexture, textureSlotIndex);
865    cmdEncoder->setFragmentSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex);
866
867    return angle::Result::Continue;
868}
869
870angle::Result TextureMtl::redefineImage(const gl::Context *context,
871                                        const gl::ImageIndex &index,
872                                        const mtl::Format &mtlFormat,
873                                        const gl::Extents &size)
874{
875    if (mNativeTexture)
876    {
877        if (mNativeTexture->valid())
878        {
879            // Calculate the expected size for the index we are defining. If the size is different
880            // from the given size, or the format is different, we are redefining the image so we
881            // must release it.
882            bool typeChanged =
883                mNativeTexture->textureType() != mtl::GetTextureType(index.getType());
884            if (mFormat != mtlFormat || size != mNativeTexture->size(index) || typeChanged)
885            {
886                // Keep other images data if texture type hasn't been changed.
887                releaseTexture(typeChanged);
888            }
889        }
890    }
891
892    // Early-out on empty textures, don't create a zero-sized storage.
893    if (size.empty())
894    {
895        return angle::Result::Continue;
896    }
897
898    ContextMtl *contextMtl = mtl::GetImpl(context);
899    // Cache last defined image format:
900    mFormat                = mtlFormat;
901    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
902
903    // If actual texture exists, it means the size hasn't been changed, no need to create new image
904    if (mNativeTexture && image)
905    {
906        ASSERT(image->textureType() == mtl::GetTextureType(GetTextureImageType(index.getType())) &&
907               image->pixelFormat() == mFormat.metalFormat && image->size() == size);
908    }
909    else
910    {
911        // Create image to hold texture's data at this level & slice:
912        switch (index.getType())
913        {
914            case gl::TextureType::_2D:
915            case gl::TextureType::CubeMap:
916                ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mtlFormat, size.width,
917                                                      size.height, 1, false, false, &image));
918                break;
919            default:
920                UNREACHABLE();
921        }
922    }
923
924    // Make sure emulated channels are properly initialized
925    ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, image));
926
927    // Tell context to rebind textures
928    contextMtl->invalidateCurrentTextures();
929
930    return angle::Result::Continue;
931}
932
933// If mipmaps = 0, this function will create full mipmaps texture.
934angle::Result TextureMtl::setStorageImpl(const gl::Context *context,
935                                         gl::TextureType type,
936                                         size_t mipmaps,
937                                         const mtl::Format &mtlFormat,
938                                         const gl::Extents &size)
939{
940    if (mNativeTexture)
941    {
942        // Don't need to hold old images data.
943        releaseTexture(true);
944    }
945
946    ContextMtl *contextMtl = mtl::GetImpl(context);
947
948    // Tell context to rebind textures
949    contextMtl->invalidateCurrentTextures();
950
951    mFormat = mtlFormat;
952
953    // Texture will be created later in ensureTextureCreated()
954
955    return angle::Result::Continue;
956}
957
958angle::Result TextureMtl::setImageImpl(const gl::Context *context,
959                                       const gl::ImageIndex &index,
960                                       const gl::InternalFormat &formatInfo,
961                                       const gl::Extents &size,
962                                       GLenum type,
963                                       const gl::PixelUnpackState &unpack,
964                                       const uint8_t *pixels)
965{
966    ContextMtl *contextMtl = mtl::GetImpl(context);
967    angle::FormatID angleFormatId =
968        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
969    const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId);
970
971    ANGLE_TRY(redefineImage(context, index, mtlFormat, size));
972
973    // Early-out on empty textures, don't create a zero-sized storage.
974    if (size.empty())
975    {
976        return angle::Result::Continue;
977    }
978
979    return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth),
980                           formatInfo, type, unpack, pixels);
981}
982
983angle::Result TextureMtl::setSubImageImpl(const gl::Context *context,
984                                          const gl::ImageIndex &index,
985                                          const gl::Box &area,
986                                          const gl::InternalFormat &formatInfo,
987                                          GLenum type,
988                                          const gl::PixelUnpackState &unpack,
989                                          const uint8_t *pixels)
990{
991    if (!pixels)
992    {
993        return angle::Result::Continue;
994    }
995
996    ASSERT(unpack.skipRows == 0 && unpack.skipPixels == 0 && unpack.skipImages == 0);
997
998    ContextMtl *contextMtl = mtl::GetImpl(context);
999
1000    angle::FormatID angleFormatId =
1001        angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat);
1002    const mtl::Format &mtlSrcFormat = contextMtl->getPixelFormat(angleFormatId);
1003
1004    if (mFormat.metalFormat != mtlSrcFormat.metalFormat)
1005    {
1006        ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
1007    }
1008
1009    ANGLE_TRY(ensureImageCreated(context, index));
1010    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
1011
1012    GLuint sourceRowPitch = 0;
1013    ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment,
1014                                                               unpack.rowLength, &sourceRowPitch));
1015    // Check if partial image update is supported for this format
1016    if (!formatInfo.supportSubImage())
1017    {
1018        // area must be the whole mip level
1019        sourceRowPitch   = 0;
1020        gl::Extents size = image->size(index);
1021        if (area.x != 0 || area.y != 0 || area.width != size.width || area.height != size.height)
1022        {
1023            ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
1024        }
1025    }
1026
1027    // Only 2D/cube texture is supported atm
1028    auto mtlRegion = MTLRegionMake2D(area.x, area.y, area.width, area.height);
1029
1030    const angle::Format &srcAngleFormat =
1031        angle::Format::Get(angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat));
1032
1033    // If source pixels are luminance or RGB8, we need to convert them to RGBA
1034    if (mFormat.actualFormatId != srcAngleFormat.id)
1035    {
1036        return convertAndSetSubImage(context, index, mtlRegion, formatInfo, srcAngleFormat,
1037                                     sourceRowPitch, pixels);
1038    }
1039
1040    // Upload to texture
1041    ANGLE_TRY(UploadTextureContents(context, image, mFormat.actualAngleFormat(), mtlRegion, 0, 0,
1042                                    pixels, sourceRowPitch));
1043
1044    return angle::Result::Continue;
1045}
1046
1047angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context,
1048                                                const gl::ImageIndex &index,
1049                                                const MTLRegion &mtlArea,
1050                                                const gl::InternalFormat &internalFormat,
1051                                                const angle::Format &pixelsFormat,
1052                                                size_t pixelsRowPitch,
1053                                                const uint8_t *pixels)
1054{
1055    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
1056    ASSERT(image && image->valid());
1057    ASSERT(image->textureType() == MTLTextureType2D);
1058
1059    ContextMtl *contextMtl = mtl::GetImpl(context);
1060
1061    // Create scratch buffer
1062    const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
1063    angle::MemoryBuffer conversionRow;
1064    const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width;
1065    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
1066
1067    MTLRegion mtlRow    = mtlArea;
1068    mtlRow.size.height  = 1;
1069    const uint8_t *psrc = pixels;
1070    for (NSUInteger r = 0; r < mtlArea.size.height; ++r, psrc += pixelsRowPitch)
1071    {
1072        mtlRow.origin.y = mtlArea.origin.y + r;
1073
1074        // Convert pixels
1075        CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsFormat.pixelBytes, 0,
1076                          pixelsFormat.pixelReadFunction, conversionRow.data(), dstRowPitch,
1077                          dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction,
1078                          internalFormat.format, dstFormat.componentType, mtlRow.size.width, 1, 1,
1079                          false, false, false);
1080
1081        // Upload to texture
1082        ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlRow, 0, 0,
1083                                        conversionRow.data(), dstRowPitch));
1084    }
1085
1086    return angle::Result::Continue;
1087}
1088
1089angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context,
1090                                                   const mtl::Format &mtlFormat,
1091                                                   const mtl::TextureRef &texture)
1092{
1093    bool emulatedChannels               = false;
1094    MTLColorWriteMask colorWritableMask = GetColorWriteMask(mtlFormat, &emulatedChannels);
1095    texture->setColorWritableMask(colorWritableMask);
1096
1097    // For emulated channels that GL texture intends to not have,
1098    // we need to initialize their content.
1099    if (emulatedChannels)
1100    {
1101        uint32_t mipmaps = texture->mipmapLevels();
1102
1103        int layers = texture->textureType() == MTLTextureTypeCube ? 6 : 1;
1104        for (int layer = 0; layer < layers; ++layer)
1105        {
1106            auto cubeFace = static_cast<gl::TextureTarget>(
1107                static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + layer);
1108            for (uint32_t mip = 0; mip < mipmaps; ++mip)
1109            {
1110                gl::ImageIndex index;
1111                if (layers > 1)
1112                {
1113                    index = gl::ImageIndex::MakeCubeMapFace(cubeFace, mip);
1114                }
1115                else
1116                {
1117                    index = gl::ImageIndex::Make2D(mip);
1118                }
1119
1120                ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mFormat, index));
1121            }
1122        }
1123    }
1124    return angle::Result::Continue;
1125}
1126
1127angle::Result TextureMtl::initializeContents(const gl::Context *context,
1128                                             const gl::ImageIndex &index)
1129{
1130    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
1131    return mtl::InitializeTextureContents(context, image, mFormat, GetImageBaseLevelIndex(image));
1132}
1133
1134angle::Result TextureMtl::copySubImageImpl(const gl::Context *context,
1135                                           const gl::ImageIndex &index,
1136                                           const gl::Offset &destOffset,
1137                                           const gl::Rectangle &sourceArea,
1138                                           const gl::InternalFormat &internalFormat,
1139                                           gl::Framebuffer *source)
1140{
1141    gl::Extents fbSize = source->getReadColorAttachment()->getSize();
1142    gl::Rectangle clippedSourceArea;
1143    if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height),
1144                       &clippedSourceArea))
1145    {
1146        return angle::Result::Continue;
1147    }
1148
1149    // If negative offsets are given, clippedSourceArea ensures we don't read from those offsets.
1150    // However, that changes the sourceOffset->destOffset mapping.  Here, destOffset is shifted by
1151    // the same amount as clipped to correct the error.
1152    const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x,
1153                                        destOffset.y + clippedSourceArea.y - sourceArea.y, 0);
1154
1155    ANGLE_TRY(ensureImageCreated(context, index));
1156
1157    if (!mtl::Format::FormatRenderable(mFormat.metalFormat))
1158    {
1159        return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea,
1160                               internalFormat, source);
1161    }
1162
1163    // NOTE(hqle): Use compute shader.
1164    return copySubImageWithDraw(context, index, modifiedDestOffset, clippedSourceArea,
1165                                internalFormat, source);
1166}
1167
1168angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
1169                                               const gl::ImageIndex &index,
1170                                               const gl::Offset &modifiedDestOffset,
1171                                               const gl::Rectangle &clippedSourceArea,
1172                                               const gl::InternalFormat &internalFormat,
1173                                               gl::Framebuffer *source)
1174{
1175    ContextMtl *contextMtl         = mtl::GetImpl(context);
1176    DisplayMtl *displayMtl         = contextMtl->getDisplay();
1177    FramebufferMtl *framebufferMtl = mtl::GetImpl(source);
1178
1179    RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget();
1180
1181    if (!colorReadRT || !colorReadRT->getTexture())
1182    {
1183        // Is this an error?
1184        return angle::Result::Continue;
1185    }
1186
1187    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
1188    ASSERT(image && image->valid());
1189
1190    mtl::RenderCommandEncoder *cmdEncoder =
1191        contextMtl->getRenderCommandEncoder(image, GetImageBaseLevelIndex(image));
1192    mtl::BlitParams blitParams;
1193
1194    blitParams.dstOffset    = modifiedDestOffset;
1195    blitParams.dstColorMask = image->getColorWritableMask();
1196
1197    blitParams.src          = colorReadRT->getTexture();
1198    blitParams.srcLevel     = static_cast<uint32_t>(colorReadRT->getLevelIndex());
1199    blitParams.srcRect      = clippedSourceArea;
1200    blitParams.srcYFlipped  = framebufferMtl->flipY();
1201    blitParams.dstLuminance = internalFormat.isLUMA();
1202
1203    displayMtl->getUtils().blitWithDraw(context, cmdEncoder, blitParams);
1204
1205    return angle::Result::Continue;
1206}
1207
1208angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
1209                                          const gl::ImageIndex &index,
1210                                          const gl::Offset &modifiedDestOffset,
1211                                          const gl::Rectangle &clippedSourceArea,
1212                                          const gl::InternalFormat &internalFormat,
1213                                          gl::Framebuffer *source)
1214{
1215    mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
1216    ASSERT(image && image->valid());
1217
1218    ContextMtl *contextMtl         = mtl::GetImpl(context);
1219    FramebufferMtl *framebufferMtl = mtl::GetImpl(source);
1220    RenderTargetMtl *colorReadRT   = framebufferMtl->getColorReadRenderTarget();
1221
1222    if (!colorReadRT || !colorReadRT->getTexture())
1223    {
1224        // Is this an error?
1225        return angle::Result::Continue;
1226    }
1227
1228    const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId);
1229    const int dstRowPitch          = dstFormat.pixelBytes * clippedSourceArea.width;
1230    angle::MemoryBuffer conversionRow;
1231    ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch));
1232
1233    MTLRegion mtlDstRowArea  = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1);
1234    gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1);
1235
1236    for (int r = 0; r < clippedSourceArea.height; ++r)
1237    {
1238        mtlDstRowArea.origin.y = modifiedDestOffset.y + r;
1239        srcRowArea.y           = clippedSourceArea.y + r;
1240
1241        PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0);
1242
1243        // Read pixels from framebuffer to memory:
1244        gl::Rectangle flippedSrcRowArea = framebufferMtl->getReadPixelArea(srcRowArea);
1245        ANGLE_TRY(framebufferMtl->readPixelsImpl(context, flippedSrcRowArea, packParams,
1246                                                 framebufferMtl->getColorReadRenderTarget(),
1247                                                 conversionRow.data()));
1248
1249        // Upload to texture
1250        ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlDstRowArea, 0, 0,
1251                                        conversionRow.data(), dstRowPitch));
1252    }
1253
1254    return angle::Result::Continue;
1255}
1256}
1257