• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// FramebufferMtl.mm:
7//    Implements the class methods for FramebufferMtl.
8//
9
10#include "libANGLE/angletypes.h"
11#include "libANGLE/renderer/metal/ContextMtl.h"
12
13#include <TargetConditionals.h>
14
15#include "common/MemoryBuffer.h"
16#include "common/angleutils.h"
17#include "common/debug.h"
18#include "libANGLE/ErrorStrings.h"
19#include "libANGLE/renderer/metal/BufferMtl.h"
20#include "libANGLE/renderer/metal/DisplayMtl.h"
21#include "libANGLE/renderer/metal/FrameBufferMtl.h"
22#include "libANGLE/renderer/metal/SurfaceMtl.h"
23#include "libANGLE/renderer/metal/mtl_utils.h"
24#include "libANGLE/renderer/renderer_utils.h"
25
26namespace rx
27{
28namespace
29{
30// Override clear color based on texture's write mask
31void OverrideMTLClearColor(const mtl::TextureRef &texture,
32                           const mtl::ClearColorValue &clearColor,
33                           MTLClearColor *colorOut)
34{
35    *colorOut =
36        mtl::EmulatedAlphaClearColor(clearColor.toMTLClearColor(), texture->getColorWritableMask());
37}
38
39const gl::InternalFormat &GetReadAttachmentInfo(const gl::Context *context,
40                                                RenderTargetMtl *renderTarget)
41{
42    GLenum implFormat;
43
44    if (renderTarget)
45    {
46        implFormat = renderTarget->getFormat().actualAngleFormat().fboImplementationInternalFormat;
47    }
48    else
49    {
50        implFormat = GL_NONE;
51    }
52
53    return gl::GetSizedInternalFormatInfo(implFormat);
54}
55
56angle::Result CopyTextureSliceLevelToTempBuffer(const gl::Context *context,
57                                                const mtl::TextureRef &srcTexture,
58                                                const mtl::MipmapNativeLevel &mipNativeLevel,
59                                                uint32_t layerIndex,
60                                                mtl::BufferRef *outBuffer)
61{
62    ASSERT(outBuffer);
63
64    ContextMtl *contextMtl           = mtl::GetImpl(context);
65    auto formatId                    = mtl::Format::MetalToAngleFormatID(srcTexture->pixelFormat());
66    const mtl::Format &metalFormat   = contextMtl->getPixelFormat(formatId);
67    const angle::Format &angleFormat = metalFormat.actualAngleFormat();
68
69    uint32_t width       = srcTexture->width(mipNativeLevel);
70    uint32_t height      = srcTexture->height(mipNativeLevel);
71    uint32_t sizeInBytes = width * height * angleFormat.pixelBytes;
72
73    mtl::BufferRef tempBuffer;
74    ANGLE_TRY(mtl::Buffer::MakeBufferWithStorageMode(
75        contextMtl, mtl::Buffer::getStorageModeForSharedBuffer(contextMtl), sizeInBytes, nullptr,
76        &tempBuffer));
77
78    gl::Rectangle region(0, 0, width, height);
79    uint32_t bytesPerRow = angleFormat.pixelBytes * width;
80    uint32_t destOffset  = 0;
81    ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(context, srcTexture, bytesPerRow, region,
82                                                    mipNativeLevel, layerIndex, destOffset,
83                                                    tempBuffer));
84
85    *outBuffer = tempBuffer;
86    return angle::Result::Continue;
87}
88
89angle::Result Copy2DTextureSlice0Level0ToTempTexture(const gl::Context *context,
90                                                     const mtl::TextureRef &srcTexture,
91                                                     mtl::TextureRef *outTexture)
92{
93    ASSERT(outTexture);
94
95    ContextMtl *contextMtl = mtl::GetImpl(context);
96    auto formatId          = mtl::Format::MetalToAngleFormatID(srcTexture->pixelFormat());
97    const auto &format     = contextMtl->getPixelFormat(formatId);
98
99    mtl::TextureRef tempTexture;
100    ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, format, srcTexture->widthAt0(),
101                                          srcTexture->heightAt0(), srcTexture->mipmapLevels(),
102                                          false, true, &tempTexture));
103
104    auto *blitEncoder = contextMtl->getBlitCommandEncoder();
105    blitEncoder->copyTexture(srcTexture,
106                             0,                          // srcStartSlice
107                             mtl::MipmapNativeLevel(0),  // MipmapNativeLevel
108                             tempTexture,                // dst
109                             0,                          // dstStartSlice
110                             mtl::MipmapNativeLevel(0),  // dstStartLevel
111                             1,                          // sliceCount,
112                             1);                         // levelCount
113
114    *outTexture = tempTexture;
115    return angle::Result::Continue;
116}
117
118}  // namespace
119
120// FramebufferMtl implementation
121FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, ContextMtl *context, bool flipY)
122    : FramebufferImpl(state),
123      mColorRenderTargets(context->getNativeCaps().maxColorAttachments, nullptr),
124      mBackbuffer(nullptr),
125      mFlipY(flipY)
126{
127    reset();
128}
129
130FramebufferMtl::~FramebufferMtl() {}
131
132void FramebufferMtl::reset()
133{
134    for (auto &rt : mColorRenderTargets)
135    {
136        rt = nullptr;
137    }
138    mDepthRenderTarget = mStencilRenderTarget = nullptr;
139
140    mRenderPassFirstColorAttachmentFormat = nullptr;
141
142    mReadPixelBuffer = nullptr;
143}
144
145void FramebufferMtl::destroy(const gl::Context *context)
146{
147    reset();
148}
149
150angle::Result FramebufferMtl::discard(const gl::Context *context,
151                                      size_t count,
152                                      const GLenum *attachments)
153{
154    return invalidate(context, count, attachments);
155}
156
157angle::Result FramebufferMtl::invalidate(const gl::Context *context,
158                                         size_t count,
159                                         const GLenum *attachments)
160{
161    return invalidateImpl(context, count, attachments);
162}
163
164angle::Result FramebufferMtl::invalidateSub(const gl::Context *context,
165                                            size_t count,
166                                            const GLenum *attachments,
167                                            const gl::Rectangle &area)
168{
169    if (area.encloses(getCompleteRenderArea()))
170    {
171        return invalidateImpl(context, count, attachments);
172    }
173    return angle::Result::Continue;
174}
175
176angle::Result FramebufferMtl::clear(const gl::Context *context, GLbitfield mask)
177{
178    ContextMtl *contextMtl = mtl::GetImpl(context);
179
180    if (ANGLE_UNLIKELY(contextMtl->getForceResyncDrawFramebuffer()))
181    {
182        ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(),
183                            gl::Command::Clear));
184    }
185
186    mtl::ClearRectParams clearOpts;
187
188    bool clearColor   = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT));
189    bool clearDepth   = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT));
190    bool clearStencil = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_STENCIL_BUFFER_BIT));
191
192    gl::DrawBufferMask clearColorBuffers;
193    if (clearColor)
194    {
195        clearColorBuffers    = mState.getEnabledDrawBuffers();
196        clearOpts.clearColor = contextMtl->getClearColorValue();
197    }
198    if (clearDepth)
199    {
200        clearOpts.clearDepth = contextMtl->getClearDepthValue();
201    }
202    if (clearStencil)
203    {
204        clearOpts.clearStencil = contextMtl->getClearStencilValue();
205    }
206
207    return clearImpl(context, clearColorBuffers, &clearOpts);
208}
209
210angle::Result FramebufferMtl::clearBufferfv(const gl::Context *context,
211                                            GLenum buffer,
212                                            GLint drawbuffer,
213                                            const GLfloat *values)
214{
215    if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer()))
216    {
217        ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(),
218                            gl::Command::Clear));
219    }
220
221    mtl::ClearRectParams clearOpts;
222
223    gl::DrawBufferMask clearColorBuffers;
224    if (buffer == GL_DEPTH)
225    {
226        clearOpts.clearDepth = values[0];
227    }
228    else
229    {
230        clearColorBuffers.set(drawbuffer);
231        clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]);
232    }
233
234    return clearImpl(context, clearColorBuffers, &clearOpts);
235}
236angle::Result FramebufferMtl::clearBufferuiv(const gl::Context *context,
237                                             GLenum buffer,
238                                             GLint drawbuffer,
239                                             const GLuint *values)
240{
241    if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer()))
242    {
243        ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(),
244                            gl::Command::Clear));
245    }
246
247    gl::DrawBufferMask clearColorBuffers;
248    clearColorBuffers.set(drawbuffer);
249
250    mtl::ClearRectParams clearOpts;
251    clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]);
252
253    return clearImpl(context, clearColorBuffers, &clearOpts);
254}
255angle::Result FramebufferMtl::clearBufferiv(const gl::Context *context,
256                                            GLenum buffer,
257                                            GLint drawbuffer,
258                                            const GLint *values)
259{
260    if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer()))
261    {
262        ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(),
263                            gl::Command::Clear));
264    }
265
266    mtl::ClearRectParams clearOpts;
267
268    gl::DrawBufferMask clearColorBuffers;
269    if (buffer == GL_STENCIL)
270    {
271        clearOpts.clearStencil = values[0] & mtl::kStencilMaskAll;
272    }
273    else
274    {
275        clearColorBuffers.set(drawbuffer);
276        clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]);
277    }
278
279    return clearImpl(context, clearColorBuffers, &clearOpts);
280}
281angle::Result FramebufferMtl::clearBufferfi(const gl::Context *context,
282                                            GLenum buffer,
283                                            GLint drawbuffer,
284                                            GLfloat depth,
285                                            GLint stencil)
286{
287    mtl::ClearRectParams clearOpts;
288    clearOpts.clearDepth   = depth;
289    clearOpts.clearStencil = stencil & mtl::kStencilMaskAll;
290
291    return clearImpl(context, gl::DrawBufferMask(), &clearOpts);
292}
293
294const gl::InternalFormat &FramebufferMtl::getImplementationColorReadFormat(
295    const gl::Context *context) const
296{
297    return GetReadAttachmentInfo(context, getColorReadRenderTargetNoCache(context));
298}
299
300angle::Result FramebufferMtl::readPixels(const gl::Context *context,
301                                         const gl::Rectangle &area,
302                                         GLenum format,
303                                         GLenum type,
304                                         const gl::PixelPackState &pack,
305                                         gl::Buffer *packBuffer,
306                                         void *pixels)
307{
308    // Clip read area to framebuffer.
309    const gl::Extents &fbSize = getState().getReadAttachment()->getSize();
310    const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
311
312    gl::Rectangle clippedArea;
313    if (!ClipRectangle(area, fbRect, &clippedArea))
314    {
315        // nothing to read
316        return angle::Result::Continue;
317    }
318    gl::Rectangle flippedArea = getCorrectFlippedReadArea(context, clippedArea);
319
320    ContextMtl *contextMtl = mtl::GetImpl(context);
321
322    const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type);
323
324    GLuint outputPitch = 0;
325    ANGLE_CHECK_GL_MATH(contextMtl,
326                        sizedFormatInfo.computeRowPitch(type, area.width, pack.alignment,
327                                                        pack.rowLength, &outputPitch));
328    GLuint outputSkipBytes = 0;
329    ANGLE_CHECK_GL_MATH(contextMtl, sizedFormatInfo.computeSkipBytes(type, outputPitch, 0, pack,
330                                                                     false, &outputSkipBytes));
331
332    outputSkipBytes += (clippedArea.x - area.x) * sizedFormatInfo.pixelBytes +
333                       (clippedArea.y - area.y) * outputPitch;
334
335    const angle::Format &angleFormat = GetFormatFromFormatType(format, type);
336
337    PackPixelsParams params(flippedArea, angleFormat, outputPitch, pack.reverseRowOrder, packBuffer,
338                            0);
339
340    if (params.packBuffer)
341    {
342        // If PBO is active, pixels is treated as offset.
343        params.offset = reinterpret_cast<ptrdiff_t>(pixels) + outputSkipBytes;
344    }
345
346    if (mFlipY)
347    {
348        params.reverseRowOrder = !params.reverseRowOrder;
349    }
350
351    ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(context),
352                             static_cast<uint8_t *>(pixels) + outputSkipBytes));
353
354    return angle::Result::Continue;
355}
356
357namespace
358{
359
360using FloatRectangle = gl::RectangleImpl<float>;
361
362float clamp0Max(float v, float max)
363{
364    return std::max(0.0f, std::min(max, v));
365}
366
367void ClampToBoundsAndAdjustCorrespondingValue(float a,
368                                              float originalASize,
369                                              float maxSize,
370                                              float b,
371                                              float originalBSize,
372                                              float *newA,
373                                              float *newB)
374{
375    float clippedA = clamp0Max(a, maxSize);
376    float delta    = clippedA - a;
377    *newA          = clippedA;
378    *newB          = b + delta * originalBSize / originalASize;
379}
380
381void ClipRectToBoundsAndAdjustCorrespondingRect(const FloatRectangle &a,
382                                                const gl::Rectangle &originalA,
383                                                const gl::Rectangle &clipDimensions,
384                                                const FloatRectangle &b,
385                                                const gl::Rectangle &originalB,
386                                                FloatRectangle *newA,
387                                                FloatRectangle *newB)
388{
389    float newAValues[4];
390    float newBValues[4];
391    ClampToBoundsAndAdjustCorrespondingValue(a.x0(), originalA.width, clipDimensions.width, b.x0(),
392                                             originalB.width, &newAValues[0], &newBValues[0]);
393    ClampToBoundsAndAdjustCorrespondingValue(a.y0(), originalA.height, clipDimensions.height,
394                                             b.y0(), originalB.height, &newAValues[1],
395                                             &newBValues[1]);
396    ClampToBoundsAndAdjustCorrespondingValue(a.x1(), originalA.width, clipDimensions.width, b.x1(),
397                                             originalB.width, &newAValues[2], &newBValues[2]);
398    ClampToBoundsAndAdjustCorrespondingValue(a.y1(), originalA.height, clipDimensions.height,
399                                             b.y1(), originalB.height, &newAValues[3],
400                                             &newBValues[3]);
401
402    *newA = FloatRectangle(newAValues);
403    *newB = FloatRectangle(newBValues);
404}
405
406void ClipRectsToBoundsAndAdjustCorrespondingRect(const FloatRectangle &a,
407                                                 const gl::Rectangle &originalA,
408                                                 const gl::Rectangle &aClipDimensions,
409                                                 const FloatRectangle &b,
410                                                 const gl::Rectangle &originalB,
411                                                 const gl::Rectangle &bClipDimensions,
412                                                 FloatRectangle *newA,
413                                                 FloatRectangle *newB)
414{
415    FloatRectangle tempA;
416    FloatRectangle tempB;
417    ClipRectToBoundsAndAdjustCorrespondingRect(a, originalA, aClipDimensions, b, originalB, &tempA,
418                                               &tempB);
419    ClipRectToBoundsAndAdjustCorrespondingRect(tempB, originalB, bClipDimensions, tempA, originalA,
420                                               newB, newA);
421}
422
423void RoundValueAndAdjustCorrespondingValue(float a,
424                                           float originalASize,
425                                           float b,
426                                           float originalBSize,
427                                           int *newA,
428                                           float *newB)
429{
430    float roundedA = std::round(a);
431    float delta    = roundedA - a;
432    *newA          = static_cast<int>(roundedA);
433    *newB          = b + delta * originalBSize / originalASize;
434}
435
436gl::Rectangle RoundRectToPixelsAndAdjustCorrespondingRectToMatch(const FloatRectangle &a,
437                                                                 const gl::Rectangle &originalA,
438                                                                 const FloatRectangle &b,
439                                                                 const gl::Rectangle &originalB,
440                                                                 FloatRectangle *newB)
441{
442    int newAValues[4];
443    float newBValues[4];
444    RoundValueAndAdjustCorrespondingValue(a.x0(), originalA.width, b.x0(), originalB.width,
445                                          &newAValues[0], &newBValues[0]);
446    RoundValueAndAdjustCorrespondingValue(a.y0(), originalA.height, b.y0(), originalB.height,
447                                          &newAValues[1], &newBValues[1]);
448    RoundValueAndAdjustCorrespondingValue(a.x1(), originalA.width, b.x1(), originalB.width,
449                                          &newAValues[2], &newBValues[2]);
450    RoundValueAndAdjustCorrespondingValue(a.y1(), originalA.height, b.y1(), originalB.height,
451                                          &newAValues[3], &newBValues[3]);
452
453    *newB = FloatRectangle(newBValues);
454    return gl::Rectangle(newAValues[0], newAValues[1], newAValues[2] - newAValues[0],
455                         newAValues[3] - newAValues[1]);
456}
457
458}  // namespace
459
460angle::Result FramebufferMtl::blit(const gl::Context *context,
461                                   const gl::Rectangle &sourceAreaIn,
462                                   const gl::Rectangle &destAreaIn,
463                                   GLbitfield mask,
464                                   GLenum filter)
465{
466    bool blitColorBuffer   = (mask & GL_COLOR_BUFFER_BIT) != 0;
467    bool blitDepthBuffer   = (mask & GL_DEPTH_BUFFER_BIT) != 0;
468    bool blitStencilBuffer = (mask & GL_STENCIL_BUFFER_BIT) != 0;
469
470    const gl::State &glState                = context->getState();
471    const gl::Framebuffer *glSrcFramebuffer = glState.getReadFramebuffer();
472
473    FramebufferMtl *srcFrameBuffer = mtl::GetImpl(glSrcFramebuffer);
474
475    blitColorBuffer =
476        blitColorBuffer && srcFrameBuffer->getColorReadRenderTarget(context) != nullptr;
477    blitDepthBuffer   = blitDepthBuffer && srcFrameBuffer->getDepthRenderTarget() != nullptr;
478    blitStencilBuffer = blitStencilBuffer && srcFrameBuffer->getStencilRenderTarget() != nullptr;
479
480    if (!blitColorBuffer && !blitDepthBuffer && !blitStencilBuffer)
481    {
482        // No-op
483        return angle::Result::Continue;
484    }
485
486    if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer()))
487    {
488        ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(),
489                            gl::Command::Blit));
490    }
491
492    const gl::Rectangle srcFramebufferDimensions = srcFrameBuffer->getCompleteRenderArea();
493    const gl::Rectangle dstFramebufferDimensions = this->getCompleteRenderArea();
494
495    FloatRectangle srcRect(sourceAreaIn);
496    FloatRectangle dstRect(destAreaIn);
497
498    FloatRectangle clippedSrcRect;
499    FloatRectangle clippedDstRect;
500    ClipRectsToBoundsAndAdjustCorrespondingRect(srcRect, sourceAreaIn, srcFramebufferDimensions,
501                                                dstRect, destAreaIn, dstFramebufferDimensions,
502                                                &clippedSrcRect, &clippedDstRect);
503
504    FloatRectangle adjustedSrcRect;
505    gl::Rectangle srcClippedDestArea = RoundRectToPixelsAndAdjustCorrespondingRectToMatch(
506        clippedDstRect, destAreaIn, clippedSrcRect, sourceAreaIn, &adjustedSrcRect);
507
508    if (srcFrameBuffer->flipY())
509    {
510        adjustedSrcRect.y =
511            srcFramebufferDimensions.height - adjustedSrcRect.y - adjustedSrcRect.height;
512        adjustedSrcRect = adjustedSrcRect.flip(false, true);
513    }
514
515    // If the destination is flipped in either direction, we will flip the source instead so that
516    // the destination area is always unflipped.
517    adjustedSrcRect =
518        adjustedSrcRect.flip(srcClippedDestArea.isReversedX(), srcClippedDestArea.isReversedY());
519    srcClippedDestArea = srcClippedDestArea.removeReversal();
520
521    // Clip the destination area to the framebuffer size and scissor.
522    gl::Rectangle scissoredDestArea;
523    if (!gl::ClipRectangle(ClipRectToScissor(glState, dstFramebufferDimensions, false),
524                           srcClippedDestArea, &scissoredDestArea))
525    {
526        return angle::Result::Continue;
527    }
528
529    // Use blit with draw
530    mtl::BlitParams baseParams;
531    baseParams.dstTextureSize =
532        gl::Extents(dstFramebufferDimensions.width, dstFramebufferDimensions.height, 1);
533    baseParams.dstRect        = srcClippedDestArea;
534    baseParams.dstScissorRect = scissoredDestArea;
535    baseParams.dstFlipY       = this->flipY();
536
537    baseParams.srcNormalizedCoords =
538        mtl::NormalizedCoords(adjustedSrcRect.x, adjustedSrcRect.y, adjustedSrcRect.width,
539                              adjustedSrcRect.height, srcFramebufferDimensions);
540    // This flag is for auto flipping the rect inside RenderUtils. Since we already flip it using
541    // getCorrectFlippedReadArea(). This flag is not needed.
542    baseParams.srcYFlipped = false;
543    baseParams.unpackFlipX = false;
544    baseParams.unpackFlipY = false;
545
546    return blitWithDraw(context, srcFrameBuffer, blitColorBuffer, blitDepthBuffer,
547                        blitStencilBuffer, filter, baseParams);
548}
549
550angle::Result FramebufferMtl::blitWithDraw(const gl::Context *context,
551                                           FramebufferMtl *srcFrameBuffer,
552                                           bool blitColorBuffer,
553                                           bool blitDepthBuffer,
554                                           bool blitStencilBuffer,
555                                           GLenum filter,
556                                           const mtl::BlitParams &baseParams)
557{
558    ContextMtl *contextMtl = mtl::GetImpl(context);
559    // Use blit with draw
560    mtl::RenderCommandEncoder *renderEncoder = nullptr;
561
562    // Blit Depth & stencil
563    if (blitDepthBuffer || blitStencilBuffer)
564    {
565        mtl::DepthStencilBlitParams dsBlitParams;
566        memcpy(&dsBlitParams, &baseParams, sizeof(baseParams));
567        RenderTargetMtl *srcDepthRt   = srcFrameBuffer->getDepthRenderTarget();
568        RenderTargetMtl *srcStencilRt = srcFrameBuffer->getStencilRenderTarget();
569
570        if (blitDepthBuffer)
571        {
572            dsBlitParams.src      = srcDepthRt->getTexture();
573            dsBlitParams.srcLevel = srcDepthRt->getLevelIndex();
574            dsBlitParams.srcLayer = srcDepthRt->getLayerIndex();
575        }
576
577        if (blitStencilBuffer && srcStencilRt->getTexture())
578        {
579            dsBlitParams.srcStencil = srcStencilRt->getTexture()->getStencilView();
580            dsBlitParams.srcLevel   = srcStencilRt->getLevelIndex();
581            dsBlitParams.srcLayer   = srcStencilRt->getLayerIndex();
582
583            if (!contextMtl->getDisplay()->getFeatures().hasShaderStencilOutput.enabled &&
584                mStencilRenderTarget)
585            {
586                // Directly writing to stencil in shader is not supported, use temporary copy buffer
587                // work around. This is a compute pass.
588                mtl::StencilBlitViaBufferParams stencilOnlyBlitParams = dsBlitParams;
589                stencilOnlyBlitParams.dstStencil      = mStencilRenderTarget->getTexture();
590                stencilOnlyBlitParams.dstStencilLayer = mStencilRenderTarget->getLayerIndex();
591                stencilOnlyBlitParams.dstStencilLevel = mStencilRenderTarget->getLevelIndex();
592                stencilOnlyBlitParams.dstPackedDepthStencilFormat =
593                    mStencilRenderTarget->getFormat().hasDepthAndStencilBits();
594
595                ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitStencilViaCopyBuffer(
596                    context, stencilOnlyBlitParams));
597
598                // Prevent the stencil to be blitted with draw again
599                dsBlitParams.srcStencil = nullptr;
600            }
601        }
602
603        // The actual blitting of depth and/or stencil
604        renderEncoder = ensureRenderPassStarted(context);
605        ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitDepthStencilWithDraw(
606            context, renderEncoder, dsBlitParams));
607    }  // if (blitDepthBuffer || blitStencilBuffer)
608    else
609    {
610        renderEncoder = ensureRenderPassStarted(context);
611    }
612
613    // Blit color
614    if (blitColorBuffer)
615    {
616        mtl::ColorBlitParams colorBlitParams;
617        memcpy(&colorBlitParams, &baseParams, sizeof(baseParams));
618
619        RenderTargetMtl *srcColorRt = srcFrameBuffer->getColorReadRenderTarget(context);
620        ASSERT(srcColorRt);
621
622        colorBlitParams.src      = srcColorRt->getTexture();
623        colorBlitParams.srcLevel = srcColorRt->getLevelIndex();
624        colorBlitParams.srcLayer = srcColorRt->getLayerIndex();
625
626        colorBlitParams.enabledBuffers = getState().getEnabledDrawBuffers();
627        colorBlitParams.filter         = filter;
628        colorBlitParams.dstLuminance   = srcColorRt->getFormat().actualAngleFormat().isLUMA();
629
630        ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(
631            context, renderEncoder, srcColorRt->getFormat().actualAngleFormat(), colorBlitParams));
632    }
633
634    return angle::Result::Continue;
635}
636
637bool FramebufferMtl::totalBitsUsedIsLessThanOrEqualToMaxBitsSupported(
638    const gl::Context *context) const
639{
640    ContextMtl *contextMtl = mtl::GetImpl(context);
641
642    uint32_t bitsUsed = 0;
643    for (const gl::FramebufferAttachment &attachment : mState.getColorAttachments())
644    {
645        if (attachment.isAttached())
646        {
647            bitsUsed += attachment.getRedSize() + attachment.getGreenSize() +
648                        attachment.getBlueSize() + attachment.getAlphaSize();
649        }
650    }
651
652    return bitsUsed <= contextMtl->getDisplay()->getMaxColorTargetBits();
653}
654
655gl::FramebufferStatus FramebufferMtl::checkStatus(const gl::Context *context) const
656{
657    ContextMtl *contextMtl = mtl::GetImpl(context);
658    if (!contextMtl->getDisplay()->getFeatures().allowSeparateDepthStencilBuffers.enabled &&
659        mState.hasSeparateDepthAndStencilAttachments())
660    {
661        return gl::FramebufferStatus::Incomplete(
662            GL_FRAMEBUFFER_UNSUPPORTED,
663            gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
664    }
665
666    if (mState.getDepthAttachment() && mState.getDepthAttachment()->getFormat().info->depthBits &&
667        mState.getDepthAttachment()->getFormat().info->stencilBits)
668    {
669        return checkPackedDepthStencilAttachment();
670    }
671
672    if (mState.getStencilAttachment() &&
673        mState.getStencilAttachment()->getFormat().info->depthBits &&
674        mState.getStencilAttachment()->getFormat().info->stencilBits)
675    {
676        return checkPackedDepthStencilAttachment();
677    }
678
679    if (!totalBitsUsedIsLessThanOrEqualToMaxBitsSupported(context))
680    {
681        return gl::FramebufferStatus::Incomplete(
682            GL_FRAMEBUFFER_UNSUPPORTED,
683            gl::err::kFramebufferIncompleteColorBitsUsedExceedsMaxColorBitsSupported);
684    }
685
686    return gl::FramebufferStatus::Complete();
687}
688
689gl::FramebufferStatus FramebufferMtl::checkPackedDepthStencilAttachment() const
690{
691    if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.1, 12.0))
692    {
693        // If depth/stencil attachment has depth & stencil bits, then depth & stencil must not have
694        // separate attachment. i.e. They must be the same texture or one of them has no
695        // attachment.
696        if (mState.hasSeparateDepthAndStencilAttachments())
697        {
698            WARN() << "Packed depth stencil texture/buffer must not be mixed with other "
699                      "texture/buffer.";
700            return gl::FramebufferStatus::Incomplete(
701                GL_FRAMEBUFFER_UNSUPPORTED,
702                gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
703        }
704    }
705    else
706    {
707        // Metal 2.0 and below doesn't allow packed depth stencil texture to be attached only as
708        // depth or stencil buffer. i.e. None of the depth & stencil attachment can be null.
709        if (!mState.getDepthStencilAttachment())
710        {
711            WARN() << "Packed depth stencil texture/buffer must be bound to both depth & stencil "
712                      "attachment point.";
713            return gl::FramebufferStatus::Incomplete(
714                GL_FRAMEBUFFER_UNSUPPORTED,
715                gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
716        }
717    }
718    return gl::FramebufferStatus::Complete();
719}
720
721angle::Result FramebufferMtl::syncState(const gl::Context *context,
722                                        GLenum binding,
723                                        const gl::Framebuffer::DirtyBits &dirtyBits,
724                                        gl::Command command)
725{
726    ContextMtl *contextMtl = mtl::GetImpl(context);
727    bool mustNotifyContext = false;
728    // Cache old mRenderPassDesc before update*RenderTarget() invalidate it.
729    mtl::RenderPassDesc oldRenderPassDesc = mRenderPassDesc;
730
731    for (size_t dirtyBit : dirtyBits)
732    {
733        switch (dirtyBit)
734        {
735            case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
736                ANGLE_TRY(updateDepthRenderTarget(context));
737                break;
738            case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
739                ANGLE_TRY(updateStencilRenderTarget(context));
740                break;
741            case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS:
742                // Restore depth attachment load action as its content may have been updated
743                // after framebuffer invalidation.
744                mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
745                break;
746            case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS:
747                // Restore stencil attachment load action as its content may have been updated
748                // after framebuffer invalidation.
749                mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
750                break;
751            case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
752                mustNotifyContext = true;
753                break;
754            case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
755            case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
756            case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
757            case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
758            case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
759                break;
760            default:
761            {
762                static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
763                if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
764                {
765                    size_t colorIndexGL = static_cast<size_t>(
766                        dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
767                    ANGLE_TRY(updateColorRenderTarget(context, colorIndexGL));
768                }
769                else
770                {
771                    ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 &&
772                           dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX);
773                    // NOTE: might need to notify context.
774
775                    // Restore color attachment load action as its content may have been updated
776                    // after framebuffer invalidation.
777                    size_t colorIndexGL = static_cast<size_t>(
778                        dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0);
779                    mRenderPassDesc.colorAttachments[colorIndexGL].loadAction = MTLLoadActionLoad;
780                }
781                break;
782            }
783        }
784    }
785
786    // If attachments have been changed and this is the current draw framebuffer,
787    // update the Metal context's incompatible attachments cache before preparing a render pass.
788    static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
789    constexpr gl::Framebuffer::DirtyBits kAttachmentsMask =
790        gl::Framebuffer::DirtyBits::Mask(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
791    if (mustNotifyContext || (dirtyBits & kAttachmentsMask).any())
792    {
793        const gl::State &glState = context->getState();
794        if (mtl::GetImpl(glState.getDrawFramebuffer()) == this)
795        {
796            contextMtl->updateIncompatibleAttachments(glState);
797        }
798    }
799
800    ANGLE_TRY(prepareRenderPass(context, &mRenderPassDesc, command));
801    bool renderPassChanged = !oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc);
802
803    if (mustNotifyContext || renderPassChanged)
804    {
805        FramebufferMtl *currentDrawFramebuffer =
806            mtl::GetImpl(context->getState().getDrawFramebuffer());
807        if (currentDrawFramebuffer == this)
808        {
809            contextMtl->onDrawFrameBufferChangedState(context, this, renderPassChanged);
810        }
811
812        // Recreate pixel reading buffer if needed in future.
813        mReadPixelBuffer = nullptr;
814    }
815
816    return angle::Result::Continue;
817}
818
819angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context,
820                                                size_t index,
821                                                GLfloat *xy) const
822{
823    UNIMPLEMENTED();
824    return angle::Result::Stop;
825}
826
827bool FramebufferMtl::prepareForUse(const gl::Context *context) const
828{
829    if (mBackbuffer)
830    {
831        // Backbuffer might obtain new drawable, which means it might change the
832        // the native texture used as the target of the render pass.
833        // We need to call this before creating render encoder.
834        if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context)))
835        {
836            return false;
837        }
838
839        if (mBackbuffer->hasRobustResourceInit())
840        {
841            (void)mBackbuffer->initializeContents(context, GL_BACK, gl::ImageIndex::Make2D(0));
842            if (mBackbuffer->hasDepthStencil())
843            {
844                (void)mBackbuffer->initializeContents(context, GL_DEPTH, gl::ImageIndex::Make2D(0));
845            }
846        }
847    }
848    return true;
849}
850
851RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget(const gl::Context *context) const
852{
853    if (mState.getReadIndex() >= mColorRenderTargets.size())
854    {
855        return nullptr;
856    }
857
858    if (!prepareForUse(context))
859    {
860        return nullptr;
861    }
862
863    return mColorRenderTargets[mState.getReadIndex()];
864}
865
866RenderTargetMtl *FramebufferMtl::getColorReadRenderTargetNoCache(const gl::Context *context) const
867{
868    if (mState.getReadIndex() >= mColorRenderTargets.size())
869    {
870        return nullptr;
871    }
872
873    if (mBackbuffer)
874    {
875        // If we have a backbuffer/window surface, we can take the old path here and return
876        // the cached color render target.
877        return getColorReadRenderTarget(context);
878    }
879    // If we have no backbuffer, get the attachment from state color attachments, as it may have
880    // changed before syncing.
881    const gl::FramebufferAttachment *attachment = mState.getColorAttachment(mState.getReadIndex());
882    RenderTargetMtl *currentTarget              = nullptr;
883    if (attachment->getRenderTarget(context, attachment->getRenderToTextureSamples(),
884                                    &currentTarget) == angle::Result::Stop)
885    {
886        return nullptr;
887    }
888    return currentTarget;
889}
890
891int FramebufferMtl::getSamples() const
892{
893    return mRenderPassDesc.sampleCount;
894}
895
896gl::Rectangle FramebufferMtl::getCompleteRenderArea() const
897{
898    return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
899}
900
901bool FramebufferMtl::renderPassHasStarted(ContextMtl *contextMtl) const
902{
903    return contextMtl->hasStartedRenderPass(mRenderPassDesc);
904}
905
906mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context)
907{
908    return ensureRenderPassStarted(context, mRenderPassDesc);
909}
910
911mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context,
912                                                                   const mtl::RenderPassDesc &desc)
913{
914    ContextMtl *contextMtl = mtl::GetImpl(context);
915
916    if (!prepareForUse(context))
917    {
918        return nullptr;
919    }
920
921    // Only support ensureRenderPassStarted() with different load & store options only. The
922    // texture, level, slice must be the same.
923    ASSERT(desc.equalIgnoreLoadStoreOptions(mRenderPassDesc));
924
925    mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(desc);
926
927    if (mRenderPassCleanStart)
928    {
929        // After a clean start we should reset the loadOp to MTLLoadActionLoad in case this render
930        // pass could be interrupted by a conversion compute shader pass then being resumed later.
931        mRenderPassCleanStart = false;
932        for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
933        {
934            colorAttachment.loadAction = MTLLoadActionLoad;
935        }
936        mRenderPassDesc.depthAttachment.loadAction   = MTLLoadActionLoad;
937        mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
938    }
939
940    return encoder;
941}
942
943void FramebufferMtl::setLoadStoreActionOnRenderPassFirstStart(
944    mtl::RenderPassAttachmentDesc *attachmentOut,
945    const bool forceDepthStencilMultisampleLoad)
946{
947    ASSERT(mRenderPassCleanStart);
948
949    mtl::RenderPassAttachmentDesc &attachment = *attachmentOut;
950
951    if (!forceDepthStencilMultisampleLoad && attachment.storeAction == MTLStoreActionDontCare)
952    {
953        // If we previously discarded attachment's content, then don't need to load it.
954        attachment.loadAction = MTLLoadActionDontCare;
955    }
956    else
957    {
958        attachment.loadAction = MTLLoadActionLoad;
959    }
960
961    if (attachment.hasImplicitMSTexture())
962    {
963        attachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
964    }
965    else
966    {
967        attachment.storeAction = MTLStoreActionStore;  // Default action is store
968    }
969}
970
971void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context)
972{
973    mRenderPassCleanStart = true;
974
975    // If any of the render targets need to load their multisample textures, we should do the same
976    // for depth/stencil.
977    bool forceDepthStencilMultisampleLoad = false;
978
979    // Compute loadOp based on previous storeOp and reset storeOp flags:
980    for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
981    {
982        forceDepthStencilMultisampleLoad |=
983            colorAttachment.storeAction == MTLStoreActionStoreAndMultisampleResolve;
984        setLoadStoreActionOnRenderPassFirstStart(&colorAttachment, false);
985    }
986    // Depth load/store
987    setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.depthAttachment,
988                                             forceDepthStencilMultisampleLoad);
989
990    // Stencil load/store
991    setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.stencilAttachment,
992                                             forceDepthStencilMultisampleLoad);
993}
994
995void FramebufferMtl::onFrameEnd(const gl::Context *context)
996{
997    if (!mBackbuffer || mBackbuffer->preserveBuffer())
998    {
999        return;
1000    }
1001
1002    ContextMtl *contextMtl = mtl::GetImpl(context);
1003    // Always discard default FBO's depth stencil & multisample buffers at the end of the frame:
1004    if (this->renderPassHasStarted(contextMtl))
1005    {
1006        mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder();
1007
1008        constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
1009        (void)invalidateImpl(context, 2, dsAttachments);
1010        if (mBackbuffer->getSamples() > 1)
1011        {
1012            encoder->setColorStoreAction(MTLStoreActionMultisampleResolve, 0);
1013        }
1014
1015        contextMtl->endEncoding(false);
1016
1017        // Reset discard flag.
1018        onStartedDrawingToFrameBuffer(context);
1019    }
1020}
1021
1022angle::Result FramebufferMtl::updateColorRenderTarget(const gl::Context *context,
1023                                                      size_t colorIndexGL)
1024{
1025    ASSERT(colorIndexGL < mColorRenderTargets.size());
1026    // Reset load store action
1027    mRenderPassDesc.colorAttachments[colorIndexGL].reset();
1028    return updateCachedRenderTarget(context, mState.getColorAttachment(colorIndexGL),
1029                                    &mColorRenderTargets[colorIndexGL]);
1030}
1031
1032angle::Result FramebufferMtl::updateDepthRenderTarget(const gl::Context *context)
1033{
1034    // Reset load store action
1035    mRenderPassDesc.depthAttachment.reset();
1036    return updateCachedRenderTarget(context, mState.getDepthAttachment(), &mDepthRenderTarget);
1037}
1038
1039angle::Result FramebufferMtl::updateStencilRenderTarget(const gl::Context *context)
1040{
1041    // Reset load store action
1042    mRenderPassDesc.stencilAttachment.reset();
1043    return updateCachedRenderTarget(context, mState.getStencilAttachment(), &mStencilRenderTarget);
1044}
1045
1046angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *context,
1047                                                       const gl::FramebufferAttachment *attachment,
1048                                                       RenderTargetMtl **cachedRenderTarget)
1049{
1050    RenderTargetMtl *newRenderTarget = nullptr;
1051    if (attachment)
1052    {
1053        ASSERT(attachment->isAttached());
1054        ANGLE_TRY(attachment->getRenderTarget(context, attachment->getRenderToTextureSamples(),
1055                                              &newRenderTarget));
1056    }
1057    *cachedRenderTarget = newRenderTarget;
1058    return angle::Result::Continue;
1059}
1060
1061angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
1062                                                mtl::RenderPassDesc *pDescOut,
1063                                                gl::Command command)
1064{
1065    // Skip incompatible attachments for draw ops to avoid triggering Metal runtime failures.
1066    const gl::DrawBufferMask incompatibleAttachments =
1067        (command == gl::Command::Draw) ? mtl::GetImpl(context)->getIncompatibleAttachments()
1068                                       : gl::DrawBufferMask();
1069    const gl::DrawBufferMask enabledDrawBuffers =
1070        getState().getEnabledDrawBuffers() & ~incompatibleAttachments;
1071
1072    mtl::RenderPassDesc &desc = *pDescOut;
1073
1074    mRenderPassFirstColorAttachmentFormat = nullptr;
1075    mRenderPassAttachmentsSameColorType   = true;
1076    uint32_t maxColorAttachments = static_cast<uint32_t>(mState.getColorAttachments().size());
1077    desc.numColorAttachments     = 0;
1078    desc.sampleCount             = 1;
1079    for (uint32_t colorIndexGL = 0; colorIndexGL < maxColorAttachments; ++colorIndexGL)
1080    {
1081        ASSERT(colorIndexGL < mColorRenderTargets.size());
1082
1083        mtl::RenderPassColorAttachmentDesc &colorAttachment = desc.colorAttachments[colorIndexGL];
1084        const RenderTargetMtl *colorRenderTarget            = mColorRenderTargets[colorIndexGL];
1085
1086        // GL allows data types of fragment shader color outputs to be incompatible with disabled
1087        // color attachments. To prevent various Metal validation issues, assign textures only to
1088        // enabled attachments.
1089        if (colorRenderTarget && enabledDrawBuffers.test(colorIndexGL))
1090        {
1091            colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
1092
1093            desc.numColorAttachments = std::max(desc.numColorAttachments, colorIndexGL + 1);
1094            desc.sampleCount = std::max(desc.sampleCount, colorRenderTarget->getRenderSamples());
1095
1096            if (!mRenderPassFirstColorAttachmentFormat)
1097            {
1098                mRenderPassFirstColorAttachmentFormat = &colorRenderTarget->getFormat();
1099            }
1100            else
1101            {
1102                if (mRenderPassFirstColorAttachmentFormat->actualAngleFormat().isSint() !=
1103                        colorRenderTarget->getFormat().actualAngleFormat().isSint() ||
1104                    mRenderPassFirstColorAttachmentFormat->actualAngleFormat().isUint() !=
1105                        colorRenderTarget->getFormat().actualAngleFormat().isUint())
1106                {
1107                    mRenderPassAttachmentsSameColorType = false;
1108                }
1109            }
1110        }
1111        else
1112        {
1113            colorAttachment.reset();
1114        }
1115    }
1116
1117    if (mDepthRenderTarget)
1118    {
1119        mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment);
1120        desc.sampleCount = std::max(desc.sampleCount, mDepthRenderTarget->getRenderSamples());
1121    }
1122    else
1123    {
1124        desc.depthAttachment.reset();
1125    }
1126
1127    if (mStencilRenderTarget)
1128    {
1129        mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment);
1130        desc.sampleCount = std::max(desc.sampleCount, mStencilRenderTarget->getRenderSamples());
1131    }
1132    else
1133    {
1134        desc.stencilAttachment.reset();
1135    }
1136
1137    if (desc.numColorAttachments == 0 && mDepthRenderTarget == nullptr &&
1138        mStencilRenderTarget == nullptr)
1139    {
1140        desc.defaultWidth  = mState.getDefaultWidth();
1141        desc.defaultHeight = mState.getDefaultHeight();
1142    }
1143
1144    return angle::Result::Continue;
1145}
1146
1147angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
1148                                              gl::DrawBufferMask clearColorBuffers,
1149                                              const mtl::ClearRectParams &clearOpts)
1150{
1151    ContextMtl *contextMtl             = mtl::GetImpl(context);
1152    bool startedRenderPass             = contextMtl->hasStartedRenderPass(mRenderPassDesc);
1153    mtl::RenderCommandEncoder *encoder = nullptr;
1154
1155    if (startedRenderPass)
1156    {
1157        encoder = ensureRenderPassStarted(context);
1158        if (encoder->hasDrawCalls())
1159        {
1160            // Render pass already has draw calls recorded, it is better to use clear with draw
1161            // operation.
1162            return clearWithDraw(context, clearColorBuffers, clearOpts);
1163        }
1164        else
1165        {
1166            // If render pass has started but there is no draw call yet. It is OK to change the
1167            // loadOp.
1168            return clearWithLoadOpRenderPassStarted(context, clearColorBuffers, clearOpts, encoder);
1169        }
1170    }
1171    else
1172    {
1173        return clearWithLoadOpRenderPassNotStarted(context, clearColorBuffers, clearOpts);
1174    }
1175}
1176
1177angle::Result FramebufferMtl::clearWithLoadOpRenderPassNotStarted(
1178    const gl::Context *context,
1179    gl::DrawBufferMask clearColorBuffers,
1180    const mtl::ClearRectParams &clearOpts)
1181{
1182    mtl::RenderPassDesc tempDesc = mRenderPassDesc;
1183
1184    for (uint32_t colorIndexGL = 0; colorIndexGL < tempDesc.numColorAttachments; ++colorIndexGL)
1185    {
1186        ASSERT(colorIndexGL < tempDesc.colorAttachments.size());
1187
1188        mtl::RenderPassColorAttachmentDesc &colorAttachment =
1189            tempDesc.colorAttachments[colorIndexGL];
1190        const mtl::TextureRef &texture = colorAttachment.texture;
1191
1192        if (clearColorBuffers.test(colorIndexGL))
1193        {
1194            colorAttachment.loadAction = MTLLoadActionClear;
1195            OverrideMTLClearColor(texture, clearOpts.clearColor.value(),
1196                                  &colorAttachment.clearColor);
1197        }
1198        else
1199        {
1200            colorAttachment.loadAction = MTLLoadActionLoad;
1201        }
1202
1203        if (colorAttachment.hasImplicitMSTexture())
1204        {
1205            colorAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
1206        }
1207        else
1208        {
1209            colorAttachment.storeAction = MTLStoreActionStore;
1210        }
1211    }
1212
1213    if (clearOpts.clearDepth.valid())
1214    {
1215        tempDesc.depthAttachment.loadAction = MTLLoadActionClear;
1216        tempDesc.depthAttachment.clearDepth = clearOpts.clearDepth.value();
1217    }
1218    else
1219    {
1220        tempDesc.depthAttachment.loadAction = MTLLoadActionLoad;
1221    }
1222
1223    if (tempDesc.depthAttachment.hasImplicitMSTexture())
1224    {
1225        tempDesc.depthAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
1226    }
1227    else
1228    {
1229        tempDesc.depthAttachment.storeAction = MTLStoreActionStore;
1230    }
1231
1232    if (clearOpts.clearStencil.valid())
1233    {
1234        tempDesc.stencilAttachment.loadAction   = MTLLoadActionClear;
1235        tempDesc.stencilAttachment.clearStencil = clearOpts.clearStencil.value();
1236    }
1237    else
1238    {
1239        tempDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
1240    }
1241
1242    if (tempDesc.stencilAttachment.hasImplicitMSTexture())
1243    {
1244        tempDesc.stencilAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
1245    }
1246    else
1247    {
1248        tempDesc.stencilAttachment.storeAction = MTLStoreActionStore;
1249    }
1250
1251    // Start new render encoder with loadOp=Clear
1252    ensureRenderPassStarted(context, tempDesc);
1253
1254    return angle::Result::Continue;
1255}
1256
1257angle::Result FramebufferMtl::clearWithLoadOpRenderPassStarted(
1258    const gl::Context *context,
1259    gl::DrawBufferMask clearColorBuffers,
1260    const mtl::ClearRectParams &clearOpts,
1261    mtl::RenderCommandEncoder *encoder)
1262{
1263    ASSERT(!encoder->hasDrawCalls());
1264
1265    for (uint32_t colorIndexGL = 0; colorIndexGL < mRenderPassDesc.numColorAttachments;
1266         ++colorIndexGL)
1267    {
1268        ASSERT(colorIndexGL < mRenderPassDesc.colorAttachments.size());
1269
1270        mtl::RenderPassColorAttachmentDesc &colorAttachment =
1271            mRenderPassDesc.colorAttachments[colorIndexGL];
1272        const mtl::TextureRef &texture = colorAttachment.texture;
1273
1274        if (clearColorBuffers.test(colorIndexGL))
1275        {
1276            MTLClearColor clearVal;
1277            OverrideMTLClearColor(texture, clearOpts.clearColor.value(), &clearVal);
1278
1279            encoder->setColorLoadAction(MTLLoadActionClear, clearVal, colorIndexGL);
1280        }
1281    }
1282
1283    if (clearOpts.clearDepth.valid())
1284    {
1285        encoder->setDepthLoadAction(MTLLoadActionClear, clearOpts.clearDepth.value());
1286    }
1287
1288    if (clearOpts.clearStencil.valid())
1289    {
1290        encoder->setStencilLoadAction(MTLLoadActionClear, clearOpts.clearStencil.value());
1291    }
1292
1293    return angle::Result::Continue;
1294}
1295
1296angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context,
1297                                            gl::DrawBufferMask clearColorBuffers,
1298                                            const mtl::ClearRectParams &clearOpts)
1299{
1300    ContextMtl *contextMtl = mtl::GetImpl(context);
1301    DisplayMtl *display    = contextMtl->getDisplay();
1302
1303    if (mRenderPassAttachmentsSameColorType)
1304    {
1305        // Start new render encoder if not already.
1306        mtl::RenderCommandEncoder *encoder = ensureRenderPassStarted(context, mRenderPassDesc);
1307
1308        return display->getUtils().clearWithDraw(context, encoder, clearOpts);
1309    }
1310
1311    // Not all attachments have the same color type.
1312    mtl::ClearRectParams overrideClearOps = clearOpts;
1313    overrideClearOps.enabledBuffers.reset();
1314
1315    // First clear depth/stencil without color attachment
1316    if (clearOpts.clearDepth.valid() || clearOpts.clearStencil.valid())
1317    {
1318        mtl::RenderPassDesc dsOnlyDesc     = mRenderPassDesc;
1319        dsOnlyDesc.numColorAttachments     = 0;
1320        mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(dsOnlyDesc);
1321
1322        ANGLE_TRY(display->getUtils().clearWithDraw(context, encoder, overrideClearOps));
1323    }
1324
1325    // Clear the color attachment one by one.
1326    overrideClearOps.enabledBuffers.set(0);
1327    for (size_t drawbuffer : clearColorBuffers)
1328    {
1329        if (drawbuffer >= mRenderPassDesc.numColorAttachments)
1330        {
1331            // Iteration over drawbuffer indices always goes in ascending order
1332            break;
1333        }
1334        RenderTargetMtl *renderTarget = mColorRenderTargets[drawbuffer];
1335        if (!renderTarget || !renderTarget->getTexture())
1336        {
1337            continue;
1338        }
1339        const mtl::Format &format     = renderTarget->getFormat();
1340        mtl::PixelType clearColorType = overrideClearOps.clearColor.value().getType();
1341        if ((clearColorType == mtl::PixelType::Int && !format.actualAngleFormat().isSint()) ||
1342            (clearColorType == mtl::PixelType::UInt && !format.actualAngleFormat().isUint()) ||
1343            (clearColorType == mtl::PixelType::Float && format.actualAngleFormat().isInt()))
1344        {
1345            continue;
1346        }
1347
1348        overrideClearOps.clearWriteMaskArray[0] = overrideClearOps.clearWriteMaskArray[drawbuffer];
1349
1350        mtl::RenderCommandEncoder *encoder =
1351            contextMtl->getRenderTargetCommandEncoder(*renderTarget);
1352        ANGLE_TRY(display->getUtils().clearWithDraw(context, encoder, overrideClearOps));
1353    }
1354
1355    return angle::Result::Continue;
1356}
1357
1358angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
1359                                        gl::DrawBufferMask clearColorBuffers,
1360                                        mtl::ClearRectParams *pClearOpts)
1361{
1362    auto &clearOpts = *pClearOpts;
1363
1364    if (!clearOpts.clearColor.valid() && !clearOpts.clearDepth.valid() &&
1365        !clearOpts.clearStencil.valid())
1366    {
1367        // No Op.
1368        return angle::Result::Continue;
1369    }
1370
1371    ContextMtl *contextMtl = mtl::GetImpl(context);
1372    const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
1373                                   mState.getDimensions().height);
1374
1375    clearOpts.colorFormat    = mRenderPassFirstColorAttachmentFormat;
1376    clearOpts.dstTextureSize = mState.getExtents();
1377    clearOpts.clearArea      = ClipRectToScissor(contextMtl->getState(), renderArea, false);
1378    clearOpts.flipY          = mFlipY;
1379
1380    // Discard clear altogether if scissor has 0 width or height.
1381    if (clearOpts.clearArea.width == 0 || clearOpts.clearArea.height == 0)
1382    {
1383        return angle::Result::Continue;
1384    }
1385
1386    clearOpts.clearWriteMaskArray = contextMtl->getWriteMaskArray();
1387    uint32_t stencilMask          = contextMtl->getStencilMask();
1388    if (!contextMtl->getDepthMask())
1389    {
1390        // Disable depth clearing, since depth write is disable
1391        clearOpts.clearDepth.reset();
1392    }
1393
1394    // Only clear enabled buffers
1395    clearOpts.enabledBuffers = clearColorBuffers;
1396
1397    bool allBuffersUnmasked = true;
1398    for (size_t enabledBuffer : clearColorBuffers)
1399    {
1400        if (clearOpts.clearWriteMaskArray[enabledBuffer] != MTLColorWriteMaskAll)
1401        {
1402            allBuffersUnmasked = false;
1403            break;
1404        }
1405    }
1406
1407    if (clearOpts.clearArea == renderArea &&
1408        (!clearOpts.clearColor.valid() || allBuffersUnmasked) &&
1409        (!clearOpts.clearStencil.valid() ||
1410         (stencilMask & mtl::kStencilMaskAll) == mtl::kStencilMaskAll))
1411    {
1412        return clearWithLoadOp(context, clearColorBuffers, clearOpts);
1413    }
1414
1415    return clearWithDraw(context, clearColorBuffers, clearOpts);
1416}
1417
1418angle::Result FramebufferMtl::invalidateImpl(const gl::Context *context,
1419                                             size_t count,
1420                                             const GLenum *attachments)
1421{
1422    ContextMtl *contextMtl = mtl::GetImpl(context);
1423    gl::DrawBufferMask invalidateColorBuffers;
1424    bool invalidateDepthBuffer   = false;
1425    bool invalidateStencilBuffer = false;
1426
1427    for (size_t i = 0; i < count; ++i)
1428    {
1429        const GLenum attachment = attachments[i];
1430
1431        switch (attachment)
1432        {
1433            case GL_DEPTH:
1434            case GL_DEPTH_ATTACHMENT:
1435                invalidateDepthBuffer = true;
1436                break;
1437            case GL_STENCIL:
1438            case GL_STENCIL_ATTACHMENT:
1439                invalidateStencilBuffer = true;
1440                break;
1441            case GL_DEPTH_STENCIL_ATTACHMENT:
1442                invalidateDepthBuffer   = true;
1443                invalidateStencilBuffer = true;
1444                break;
1445            default:
1446                ASSERT(
1447                    (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) ||
1448                    (attachment == GL_COLOR));
1449
1450                invalidateColorBuffers.set(
1451                    attachment == GL_COLOR ? 0u : (attachment - GL_COLOR_ATTACHMENT0));
1452        }
1453    }
1454
1455    // Set the appropriate storeOp for attachments.
1456    // If we already start the render pass, then need to set the store action now.
1457    bool renderPassStarted = contextMtl->hasStartedRenderPass(mRenderPassDesc);
1458    mtl::RenderCommandEncoder *encoder =
1459        renderPassStarted ? contextMtl->getRenderCommandEncoder() : nullptr;
1460
1461    for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
1462    {
1463        if (invalidateColorBuffers.test(i))
1464        {
1465            // Some opaque formats, like RGB8, are emulated as RGBA with alpha channel initialized
1466            // to 1.0. Invalidating such attachments may lead to random values in their alpha
1467            // channel, so skip invalidation in this case.
1468            RenderTargetMtl *renderTarget = mColorRenderTargets[i];
1469            if (renderTarget && renderTarget->getTexture())
1470            {
1471                const mtl::Format &mtlFormat        = renderTarget->getFormat();
1472                const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat();
1473                const angle::Format &actualFormat   = mtlFormat.actualAngleFormat();
1474                if (intendedFormat.alphaBits == 0 && actualFormat.alphaBits)
1475                {
1476                    continue;
1477                }
1478            }
1479
1480            mtl::RenderPassColorAttachmentDesc &colorAttachment =
1481                mRenderPassDesc.colorAttachments[i];
1482            colorAttachment.storeAction = MTLStoreActionDontCare;
1483            if (renderPassStarted)
1484            {
1485                encoder->setColorStoreAction(MTLStoreActionDontCare, i);
1486            }
1487        }
1488    }
1489
1490    if (invalidateDepthBuffer && mDepthRenderTarget)
1491    {
1492        mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare;
1493        if (renderPassStarted)
1494        {
1495            encoder->setDepthStoreAction(MTLStoreActionDontCare);
1496        }
1497    }
1498
1499    if (invalidateStencilBuffer && mStencilRenderTarget)
1500    {
1501        mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare;
1502        if (renderPassStarted)
1503        {
1504            encoder->setStencilStoreAction(MTLStoreActionDontCare);
1505        }
1506    }
1507
1508    // Do not encode any further commands in this render pass which can affect the
1509    // framebuffer, or their effects will be lost.
1510    contextMtl->endEncoding(false);
1511    // Reset discard flag.
1512    onStartedDrawingToFrameBuffer(context);
1513
1514    return angle::Result::Continue;
1515}
1516
1517gl::Rectangle FramebufferMtl::getCorrectFlippedReadArea(const gl::Context *context,
1518                                                        const gl::Rectangle &glArea) const
1519{
1520    RenderTargetMtl *readRT = getColorReadRenderTarget(context);
1521    if (!readRT)
1522    {
1523        readRT = mDepthRenderTarget;
1524    }
1525    if (!readRT)
1526    {
1527        readRT = mStencilRenderTarget;
1528    }
1529    ASSERT(readRT);
1530    gl::Rectangle flippedArea = glArea;
1531    if (mFlipY)
1532    {
1533        flippedArea.y = readRT->getTexture()->height(readRT->getLevelIndex()) - flippedArea.y -
1534                        flippedArea.height;
1535    }
1536
1537    return flippedArea;
1538}
1539
1540namespace
1541{
1542
1543angle::Result readPixelsCopyImpl(
1544    const gl::Context *context,
1545    const gl::Rectangle &area,
1546    const PackPixelsParams &packPixelsParams,
1547    const RenderTargetMtl *renderTarget,
1548    const std::function<angle::Result(const gl::Rectangle &region, const uint8_t *&src)> &getDataFn,
1549    uint8_t *pixels)
1550{
1551    const mtl::Format &readFormat        = renderTarget->getFormat();
1552    const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
1553
1554    auto packPixelsRowParams = packPixelsParams;
1555    gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1);
1556    int bufferRowPitch = area.width * readAngleFormat.pixelBytes;
1557
1558    int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1;
1559    int startRow  = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y;
1560
1561    // Copy pixels row by row
1562    packPixelsRowParams.area.height     = 1;
1563    packPixelsRowParams.reverseRowOrder = false;
1564    for (int r = startRow, i = 0; i < area.height;
1565         ++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch)
1566    {
1567        srcRowRegion.y             = r;
1568        packPixelsRowParams.area.y = packPixelsParams.area.y + i;
1569
1570        const uint8_t *src;
1571        ANGLE_TRY(getDataFn(srcRowRegion, src));
1572        PackPixels(packPixelsRowParams, readAngleFormat, bufferRowPitch, src, pixels);
1573    }
1574
1575    return angle::Result::Continue;
1576}
1577
1578}  // namespace
1579
1580angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
1581                                             const gl::Rectangle &area,
1582                                             const PackPixelsParams &packPixelsParams,
1583                                             const RenderTargetMtl *renderTarget,
1584                                             uint8_t *pixels) const
1585{
1586    ContextMtl *contextMtl             = mtl::GetImpl(context);
1587    const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures();
1588
1589    if (!renderTarget)
1590    {
1591        return angle::Result::Continue;
1592    }
1593
1594    if (packPixelsParams.packBuffer)
1595    {
1596        return readPixelsToPBO(context, area, packPixelsParams, renderTarget);
1597    }
1598
1599    mtl::TextureRef texture;
1600    if (mBackbuffer)
1601    {
1602        // Backbuffer might have MSAA texture as render target, needs to obtain the
1603        // resolved texture to be able to read pixels.
1604        ANGLE_TRY(mBackbuffer->ensureColorTextureReadyForReadPixels(context));
1605        texture = mBackbuffer->getColorTexture();
1606    }
1607    else
1608    {
1609        texture = renderTarget->getTexture();
1610        // For non-default framebuffer, MSAA read pixels is disallowed.
1611        if (!texture)
1612        {
1613            return angle::Result::Stop;
1614        }
1615        ANGLE_MTL_CHECK(contextMtl, texture->samples() == 1, GL_INVALID_OPERATION);
1616    }
1617
1618    const mtl::Format &readFormat        = renderTarget->getFormat();
1619    const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
1620
1621    if (features.copyIOSurfaceToNonIOSurfaceForReadOptimization.enabled &&
1622        texture->hasIOSurface() && texture->mipmapLevels() == 1 &&
1623        texture->textureType() == MTLTextureType2D)
1624    {
1625        // Reading a texture may be slow if it's an IOSurface because metal has to lock/unlock the
1626        // surface, whereas copying the texture to non IOSurface texture and then reading from that
1627        // may be fast depending on the GPU/driver.
1628        ANGLE_TRY(Copy2DTextureSlice0Level0ToTempTexture(context, texture, &texture));
1629    }
1630
1631    if (features.copyTextureToBufferForReadOptimization.enabled)
1632    {
1633        mtl::BufferRef buffer;
1634        ANGLE_TRY(CopyTextureSliceLevelToTempBuffer(context, texture, renderTarget->getLevelIndex(),
1635                                                    renderTarget->getLayerIndex(), &buffer));
1636
1637        int bufferRowPitch =
1638            texture->width(renderTarget->getLevelIndex()) * readAngleFormat.pixelBytes;
1639
1640        buffer->syncContent(contextMtl, contextMtl->getBlitCommandEncoder());
1641        const uint8_t *bufferData = buffer->mapReadOnly(contextMtl);
1642
1643        angle::Result result = readPixelsCopyImpl(
1644            context, area, packPixelsParams, renderTarget,
1645            [&](const gl::Rectangle &region, const uint8_t *&src) {
1646                src =
1647                    bufferData + region.y * bufferRowPitch + region.x * readAngleFormat.pixelBytes;
1648                return angle::Result::Continue;
1649            },
1650            pixels);
1651
1652        buffer->unmap(contextMtl);
1653
1654        return result;
1655    }
1656
1657    if (texture->isBeingUsedByGPU(contextMtl))
1658    {
1659        contextMtl->flushCommandBuffer(mtl::WaitUntilFinished);
1660    }
1661
1662    angle::MemoryBuffer readPixelRowBuffer;
1663    int bufferRowPitch = area.width * readAngleFormat.pixelBytes;
1664    ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(bufferRowPitch));
1665    return readPixelsCopyImpl(
1666        context, area, packPixelsParams, renderTarget,
1667        [&](const gl::Rectangle &region, const uint8_t *&src) {
1668            // Read the pixels data to the row buffer
1669            ANGLE_TRY(mtl::ReadTexturePerSliceBytes(
1670                context, texture, bufferRowPitch, region, renderTarget->getLevelIndex(),
1671                renderTarget->getLayerIndex(), readPixelRowBuffer.data()));
1672            src = readPixelRowBuffer.data();
1673            return angle::Result::Continue;
1674        },
1675        pixels);
1676}
1677
1678angle::Result FramebufferMtl::readPixelsToPBO(const gl::Context *context,
1679                                              const gl::Rectangle &area,
1680                                              const PackPixelsParams &packPixelsParams,
1681                                              const RenderTargetMtl *renderTarget) const
1682{
1683    ASSERT(packPixelsParams.packBuffer);
1684    ASSERT(renderTarget);
1685
1686    ContextMtl *contextMtl = mtl::GetImpl(context);
1687
1688    ANGLE_MTL_CHECK(contextMtl, packPixelsParams.offset <= std::numeric_limits<uint32_t>::max(),
1689                    GL_INVALID_OPERATION);
1690    uint32_t offset = static_cast<uint32_t>(packPixelsParams.offset);
1691
1692    BufferMtl *packBufferMtl = mtl::GetImpl(packPixelsParams.packBuffer);
1693    mtl::BufferRef dstBuffer = packBufferMtl->getCurrentBuffer();
1694
1695    return readPixelsToBuffer(context, area, renderTarget, packPixelsParams.reverseRowOrder,
1696                              *packPixelsParams.destFormat, offset, packPixelsParams.outputPitch,
1697                              &dstBuffer);
1698}
1699
1700angle::Result FramebufferMtl::readPixelsToBuffer(const gl::Context *context,
1701                                                 const gl::Rectangle &area,
1702                                                 const RenderTargetMtl *renderTarget,
1703                                                 bool reverseRowOrder,
1704                                                 const angle::Format &dstAngleFormat,
1705                                                 uint32_t dstBufferOffset,
1706                                                 uint32_t dstBufferRowPitch,
1707                                                 const mtl::BufferRef *pDstBuffer) const
1708{
1709    ASSERT(renderTarget);
1710
1711    ContextMtl *contextMtl = mtl::GetImpl(context);
1712
1713    const mtl::Format &readFormat        = renderTarget->getFormat();
1714    const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
1715
1716    mtl::TextureRef texture = renderTarget->getTexture();
1717
1718    const mtl::BufferRef &dstBuffer = *pDstBuffer;
1719
1720    if (dstAngleFormat.id != readAngleFormat.id || texture->samples() > 1 ||
1721        (dstBufferOffset % dstAngleFormat.pixelBytes) ||
1722        (dstBufferOffset % mtl::kTextureToBufferBlittingAlignment))
1723    {
1724        const angle::Format *actualDstAngleFormat;
1725
1726        // SRGB is special case: We need to write sRGB values to buffer, not linear values.
1727        switch (readAngleFormat.id)
1728        {
1729            case angle::FormatID::B8G8R8A8_UNORM_SRGB:
1730            case angle::FormatID::R8G8B8_UNORM_SRGB:
1731            case angle::FormatID::R8G8B8A8_UNORM_SRGB:
1732                if (dstAngleFormat.id != readAngleFormat.id)
1733                {
1734                    switch (dstAngleFormat.id)
1735                    {
1736                        case angle::FormatID::B8G8R8A8_UNORM:
1737                            actualDstAngleFormat =
1738                                &angle::Format::Get(angle::FormatID::B8G8R8A8_UNORM_SRGB);
1739                            break;
1740                        case angle::FormatID::R8G8B8A8_UNORM:
1741                            actualDstAngleFormat =
1742                                &angle::Format::Get(angle::FormatID::R8G8B8A8_UNORM_SRGB);
1743                            break;
1744                        default:
1745                            // Unsupported format.
1746                            ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_ENUM);
1747                    }
1748                    break;
1749                }
1750                OS_FALLTHROUGH;
1751            default:
1752                actualDstAngleFormat = &dstAngleFormat;
1753        }
1754
1755        // Use compute shader
1756        mtl::CopyPixelsToBufferParams params;
1757        params.buffer            = dstBuffer;
1758        params.bufferStartOffset = dstBufferOffset;
1759        params.bufferRowPitch    = dstBufferRowPitch;
1760
1761        params.texture                = texture;
1762        params.textureArea            = area;
1763        params.textureLevel           = renderTarget->getLevelIndex();
1764        params.textureSliceOrDeph     = renderTarget->getLayerIndex();
1765        params.reverseTextureRowOrder = reverseRowOrder;
1766
1767        ANGLE_TRY(contextMtl->getDisplay()->getUtils().packPixelsFromTextureToBuffer(
1768            contextMtl, *actualDstAngleFormat, params));
1769    }
1770    else
1771    {
1772        // Use blit command encoder
1773        if (!reverseRowOrder)
1774        {
1775            ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(
1776                context, texture, dstBufferRowPitch, area, renderTarget->getLevelIndex(),
1777                renderTarget->getLayerIndex(), dstBufferOffset, dstBuffer));
1778        }
1779        else
1780        {
1781            gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1);
1782
1783            int startRow = area.y1() - 1;
1784
1785            uint32_t bufferRowOffset = dstBufferOffset;
1786            // Copy pixels row by row
1787            for (int r = startRow, copiedRows = 0; copiedRows < area.height;
1788                 ++copiedRows, --r, bufferRowOffset += dstBufferRowPitch)
1789            {
1790                srcRowRegion.y = r;
1791
1792                // Read the pixels data to the buffer's row
1793                ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(
1794                    context, texture, dstBufferRowPitch, srcRowRegion,
1795                    renderTarget->getLevelIndex(), renderTarget->getLayerIndex(), bufferRowOffset,
1796                    dstBuffer));
1797            }
1798        }
1799    }
1800
1801    return angle::Result::Continue;
1802}
1803
1804}  // namespace rx
1805