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