• 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/renderer/metal/ContextMtl.h"
11
12#include <TargetConditionals.h>
13
14#include "common/MemoryBuffer.h"
15#include "common/angleutils.h"
16#include "common/debug.h"
17#include "libANGLE/renderer/metal/DisplayMtl.h"
18#include "libANGLE/renderer/metal/FrameBufferMtl.h"
19#include "libANGLE/renderer/metal/SurfaceMtl.h"
20#include "libANGLE/renderer/metal/mtl_utils.h"
21#include "libANGLE/renderer/renderer_utils.h"
22
23namespace rx
24{
25// FramebufferMtl implementation
26FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, bool flipY)
27    : FramebufferImpl(state), mFlipY(flipY)
28{
29    reset();
30}
31
32FramebufferMtl::~FramebufferMtl() {}
33
34void FramebufferMtl::reset()
35{
36    for (auto &rt : mColorRenderTargets)
37    {
38        rt = nullptr;
39    }
40    mDepthRenderTarget = mStencilRenderTarget = nullptr;
41}
42
43void FramebufferMtl::destroy(const gl::Context *context)
44{
45    reset();
46}
47
48angle::Result FramebufferMtl::discard(const gl::Context *context,
49                                      size_t count,
50                                      const GLenum *attachments)
51{
52    return invalidate(context, count, attachments);
53}
54
55angle::Result FramebufferMtl::invalidate(const gl::Context *context,
56                                         size_t count,
57                                         const GLenum *attachments)
58{
59    return invalidateImpl(mtl::GetImpl(context), count, attachments);
60}
61
62angle::Result FramebufferMtl::invalidateSub(const gl::Context *context,
63                                            size_t count,
64                                            const GLenum *attachments,
65                                            const gl::Rectangle &area)
66{
67    // NOTE(hqle): ES 3.0 feature.
68    UNIMPLEMENTED();
69    return angle::Result::Stop;
70}
71
72angle::Result FramebufferMtl::clear(const gl::Context *context, GLbitfield mask)
73{
74    ContextMtl *contextMtl = mtl::GetImpl(context);
75
76    mtl::ClearRectParams clearOpts;
77
78    bool clearColor   = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT));
79    bool clearDepth   = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT));
80    bool clearStencil = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_STENCIL_BUFFER_BIT));
81
82    gl::DrawBufferMask clearColorBuffers;
83    if (clearColor)
84    {
85        clearColorBuffers    = mState.getEnabledDrawBuffers();
86        clearOpts.clearColor = contextMtl->getClearColorValue();
87    }
88    if (clearDepth)
89    {
90        clearOpts.clearDepth = contextMtl->getClearDepthValue();
91    }
92    if (clearStencil)
93    {
94        clearOpts.clearStencil = contextMtl->getClearStencilValue();
95    }
96
97    return clearImpl(context, clearColorBuffers, &clearOpts);
98}
99
100angle::Result FramebufferMtl::clearBufferfv(const gl::Context *context,
101                                            GLenum buffer,
102                                            GLint drawbuffer,
103                                            const GLfloat *values)
104{
105    // NOTE(hqle): ES 3.0 feature.
106    UNIMPLEMENTED();
107    return angle::Result::Stop;
108}
109angle::Result FramebufferMtl::clearBufferuiv(const gl::Context *context,
110                                             GLenum buffer,
111                                             GLint drawbuffer,
112                                             const GLuint *values)
113{
114    // NOTE(hqle): ES 3.0 feature.
115    UNIMPLEMENTED();
116    return angle::Result::Stop;
117}
118angle::Result FramebufferMtl::clearBufferiv(const gl::Context *context,
119                                            GLenum buffer,
120                                            GLint drawbuffer,
121                                            const GLint *values)
122{
123    // NOTE(hqle): ES 3.0 feature.
124    UNIMPLEMENTED();
125    return angle::Result::Stop;
126}
127angle::Result FramebufferMtl::clearBufferfi(const gl::Context *context,
128                                            GLenum buffer,
129                                            GLint drawbuffer,
130                                            GLfloat depth,
131                                            GLint stencil)
132{
133    // NOTE(hqle): ES 3.0 feature.
134    UNIMPLEMENTED();
135    return angle::Result::Stop;
136}
137
138const gl::InternalFormat &FramebufferMtl::getImplementationColorReadFormat(
139    const gl::Context *context) const
140{
141    ContextMtl *contextMtl   = mtl::GetImpl(context);
142    GLenum sizedFormat       = mState.getReadAttachment()->getFormat().info->sizedInternalFormat;
143    angle::FormatID formatID = angle::Format::InternalFormatToID(sizedFormat);
144    const mtl::Format &mtlFormat = contextMtl->getDisplay()->getPixelFormat(formatID);
145    GLenum implFormat            = mtlFormat.actualAngleFormat().fboImplementationInternalFormat;
146    return gl::GetSizedInternalFormatInfo(implFormat);
147}
148
149angle::Result FramebufferMtl::readPixels(const gl::Context *context,
150                                         const gl::Rectangle &area,
151                                         GLenum format,
152                                         GLenum type,
153                                         void *pixels)
154{
155    // Clip read area to framebuffer.
156    const gl::Extents &fbSize = getState().getReadAttachment()->getSize();
157    const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
158
159    gl::Rectangle clippedArea;
160    if (!ClipRectangle(area, fbRect, &clippedArea))
161    {
162        // nothing to read
163        return angle::Result::Continue;
164    }
165    gl::Rectangle flippedArea = getReadPixelArea(clippedArea);
166
167    ContextMtl *contextMtl              = mtl::GetImpl(context);
168    const gl::State &glState            = context->getState();
169    const gl::PixelPackState &packState = glState.getPackState();
170
171    const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type);
172
173    GLuint outputPitch = 0;
174    ANGLE_CHECK_GL_MATH(contextMtl,
175                        sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment,
176                                                        packState.rowLength, &outputPitch));
177    GLuint outputSkipBytes = 0;
178    ANGLE_CHECK_GL_MATH(contextMtl, sizedFormatInfo.computeSkipBytes(
179                                        type, outputPitch, 0, packState, false, &outputSkipBytes));
180
181    outputSkipBytes += (clippedArea.x - area.x) * sizedFormatInfo.pixelBytes +
182                       (clippedArea.y - area.y) * outputPitch;
183
184    const angle::Format &angleFormat = GetFormatFromFormatType(format, type);
185
186    PackPixelsParams params(flippedArea, angleFormat, outputPitch, packState.reverseRowOrder,
187                            glState.getTargetBuffer(gl::BufferBinding::PixelPack), 0);
188    if (mFlipY)
189    {
190        params.reverseRowOrder = !params.reverseRowOrder;
191    }
192
193    ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(),
194                             static_cast<uint8_t *>(pixels) + outputSkipBytes));
195
196    return angle::Result::Continue;
197}
198
199angle::Result FramebufferMtl::blit(const gl::Context *context,
200                                   const gl::Rectangle &sourceArea,
201                                   const gl::Rectangle &destArea,
202                                   GLbitfield mask,
203                                   GLenum filter)
204{
205    // NOTE(hqle): MSAA feature.
206    UNIMPLEMENTED();
207    return angle::Result::Stop;
208}
209
210bool FramebufferMtl::checkStatus(const gl::Context *context) const
211{
212    if (!mState.attachmentsHaveSameDimensions())
213    {
214        return false;
215    }
216
217    ContextMtl *contextMtl = mtl::GetImpl(context);
218    if (!contextMtl->getDisplay()->getFeatures().allowSeparatedDepthStencilBuffers.enabled &&
219        mState.hasSeparateDepthAndStencilAttachments())
220    {
221        return false;
222    }
223
224    return true;
225}
226
227angle::Result FramebufferMtl::syncState(const gl::Context *context,
228                                        GLenum binding,
229                                        const gl::Framebuffer::DirtyBits &dirtyBits)
230{
231    ContextMtl *contextMtl = mtl::GetImpl(context);
232    ASSERT(dirtyBits.any());
233    for (size_t dirtyBit : dirtyBits)
234    {
235        switch (dirtyBit)
236        {
237            case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
238                ANGLE_TRY(updateDepthRenderTarget(context));
239                break;
240            case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
241                ANGLE_TRY(updateStencilRenderTarget(context));
242                break;
243            case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS:
244            case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS:
245                // NOTE(hqle): What are we supposed to do?
246                break;
247            case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
248            case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
249            case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
250            case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
251            case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
252            case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
253                break;
254            default:
255            {
256                static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
257                if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
258                {
259                    size_t colorIndexGL = static_cast<size_t>(
260                        dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
261                    ANGLE_TRY(updateColorRenderTarget(context, colorIndexGL));
262                }
263                else
264                {
265                    ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 &&
266                           dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX);
267                    // NOTE: might need to notify context.
268                }
269                break;
270            }
271        }
272    }
273
274    auto oldRenderPassDesc = mRenderPassDesc;
275
276    ANGLE_TRY(prepareRenderPass(context, mState.getEnabledDrawBuffers(), &mRenderPassDesc));
277
278    if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc))
279    {
280        FramebufferMtl *currentDrawFramebuffer =
281            mtl::GetImpl(context->getState().getDrawFramebuffer());
282        if (currentDrawFramebuffer == this)
283        {
284            contextMtl->onDrawFrameBufferChange(context, this);
285        }
286    }
287
288    return angle::Result::Continue;
289}
290
291angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context,
292                                                size_t index,
293                                                GLfloat *xy) const
294{
295    UNIMPLEMENTED();
296    return angle::Result::Stop;
297}
298
299RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget() const
300{
301    if (mState.getReadIndex() >= mColorRenderTargets.size())
302    {
303        return nullptr;
304    }
305    return mColorRenderTargets[mState.getReadIndex()];
306}
307
308gl::Rectangle FramebufferMtl::getCompleteRenderArea() const
309{
310    return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
311}
312
313const mtl::RenderPassDesc &FramebufferMtl::getRenderPassDesc(ContextMtl *context)
314{
315    return mRenderPassDesc;
316}
317
318void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context)
319{
320    // Compute loadOp based on previous storeOp and reset storeOp flags:
321    for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
322    {
323        if (colorAttachment.storeAction == MTLStoreActionDontCare)
324        {
325            // If we previously discarded attachment's content, then don't need to load it.
326            colorAttachment.loadAction = MTLLoadActionDontCare;
327        }
328        else
329        {
330            colorAttachment.loadAction = MTLLoadActionLoad;
331        }
332        colorAttachment.storeAction = MTLStoreActionStore;  // Default action is store
333    }
334    // Depth load/store
335    if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionDontCare)
336    {
337        mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionDontCare;
338    }
339    else
340    {
341        mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
342    }
343    mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionStore;
344
345    // Stencil load/store
346    if (mRenderPassDesc.stencilAttachment.storeAction == MTLStoreActionDontCare)
347    {
348        mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionDontCare;
349    }
350    else
351    {
352        mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
353    }
354    mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionStore;
355}
356
357angle::Result FramebufferMtl::updateColorRenderTarget(const gl::Context *context,
358                                                      size_t colorIndexGL)
359{
360    ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
361    // Reset load store action
362    mRenderPassDesc.colorAttachments[colorIndexGL].reset();
363    return updateCachedRenderTarget(context, mState.getColorAttachment(colorIndexGL),
364                                    &mColorRenderTargets[colorIndexGL]);
365}
366
367angle::Result FramebufferMtl::updateDepthRenderTarget(const gl::Context *context)
368{
369    // Reset load store action
370    mRenderPassDesc.depthAttachment.reset();
371    return updateCachedRenderTarget(context, mState.getDepthAttachment(), &mDepthRenderTarget);
372}
373
374angle::Result FramebufferMtl::updateStencilRenderTarget(const gl::Context *context)
375{
376    // Reset load store action
377    mRenderPassDesc.stencilAttachment.reset();
378    return updateCachedRenderTarget(context, mState.getStencilAttachment(), &mStencilRenderTarget);
379}
380
381angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *context,
382                                                       const gl::FramebufferAttachment *attachment,
383                                                       RenderTargetMtl **cachedRenderTarget)
384{
385    RenderTargetMtl *newRenderTarget = nullptr;
386    if (attachment)
387    {
388        ASSERT(attachment->isAttached());
389        ANGLE_TRY(attachment->getRenderTarget(context, attachment->getRenderToTextureSamples(),
390                                              &newRenderTarget));
391    }
392    *cachedRenderTarget = newRenderTarget;
393    return angle::Result::Continue;
394}
395
396angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
397                                                gl::DrawBufferMask drawColorBuffers,
398                                                mtl::RenderPassDesc *pDescOut)
399{
400    auto &desc = *pDescOut;
401
402    desc.numColorAttachments = static_cast<uint32_t>(drawColorBuffers.count());
403    size_t attachmentIdx     = 0;
404
405    for (size_t colorIndexGL : drawColorBuffers)
406    {
407        if (colorIndexGL >= mtl::kMaxRenderTargets)
408        {
409            continue;
410        }
411        const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
412        ASSERT(colorRenderTarget);
413
414        mtl::RenderPassColorAttachmentDesc &colorAttachment =
415            desc.colorAttachments[attachmentIdx++];
416        colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
417    }
418
419    if (mDepthRenderTarget)
420    {
421        mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment);
422    }
423
424    if (mStencilRenderTarget)
425    {
426        mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment);
427    }
428
429    return angle::Result::Continue;
430}
431
432// Override clear color based on texture's write mask
433void FramebufferMtl::overrideClearColor(const mtl::TextureRef &texture,
434                                        MTLClearColor clearColor,
435                                        MTLClearColor *colorOut)
436{
437    *colorOut = mtl::EmulatedAlphaClearColor(clearColor, texture->getColorWritableMask());
438}
439
440angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
441                                              gl::DrawBufferMask clearColorBuffers,
442                                              const mtl::ClearRectParams &clearOpts)
443{
444    ContextMtl *contextMtl             = mtl::GetImpl(context);
445    bool startedRenderPass             = contextMtl->hasStartedRenderPass(mRenderPassDesc);
446    mtl::RenderCommandEncoder *encoder = nullptr;
447    mtl::RenderPassDesc tempDesc       = mRenderPassDesc;
448
449    if (startedRenderPass)
450    {
451        encoder = contextMtl->getRenderCommandEncoder();
452    }
453
454    size_t attachmentCount = 0;
455    for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
456    {
457        ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
458
459        uint32_t attachmentIdx = static_cast<uint32_t>(attachmentCount++);
460        mtl::RenderPassColorAttachmentDesc &colorAttachment =
461            tempDesc.colorAttachments[attachmentIdx];
462        const mtl::TextureRef &texture = colorAttachment.texture;
463
464        if (clearColorBuffers.test(colorIndexGL))
465        {
466            if (startedRenderPass)
467            {
468                // Render pass already started, and we want to clear this buffer,
469                // then discard its content before clearing.
470                encoder->setColorStoreAction(MTLStoreActionDontCare, attachmentIdx);
471            }
472            colorAttachment.loadAction = MTLLoadActionClear;
473            overrideClearColor(texture, clearOpts.clearColor.value(), &colorAttachment.clearColor);
474        }
475        else if (startedRenderPass)
476        {
477            // If render pass already started and we don't want to clear this buffer,
478            // then store it with current render encoder and load it before clearing step
479            encoder->setColorStoreAction(MTLStoreActionStore, attachmentIdx);
480            colorAttachment.loadAction = MTLLoadActionLoad;
481        }
482    }
483
484    MTLStoreAction preClearDethpStoreAction   = MTLStoreActionStore,
485                   preClearStencilStoreAction = MTLStoreActionStore;
486    if (clearOpts.clearDepth.valid())
487    {
488        preClearDethpStoreAction            = MTLStoreActionDontCare;
489        tempDesc.depthAttachment.loadAction = MTLLoadActionClear;
490        tempDesc.depthAttachment.clearDepth = clearOpts.clearDepth.value();
491    }
492    else if (startedRenderPass)
493    {
494        // If render pass already started and we don't want to clear this buffer,
495        // then store it with current render encoder and load it before clearing step
496        preClearDethpStoreAction            = MTLStoreActionStore;
497        tempDesc.depthAttachment.loadAction = MTLLoadActionLoad;
498    }
499
500    if (clearOpts.clearStencil.valid())
501    {
502        preClearStencilStoreAction              = MTLStoreActionDontCare;
503        tempDesc.stencilAttachment.loadAction   = MTLLoadActionClear;
504        tempDesc.stencilAttachment.clearStencil = clearOpts.clearStencil.value();
505    }
506    else if (startedRenderPass)
507    {
508        // If render pass already started and we don't want to clear this buffer,
509        // then store it with current render encoder and load it before clearing step
510        preClearStencilStoreAction            = MTLStoreActionStore;
511        tempDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
512    }
513
514    // End current render encoder.
515    if (startedRenderPass)
516    {
517        encoder->setDepthStencilStoreAction(preClearDethpStoreAction, preClearStencilStoreAction);
518        contextMtl->endEncoding(encoder);
519    }
520
521    // Start new render encoder with loadOp=Clear
522    contextMtl->getRenderCommandEncoder(tempDesc);
523
524    return angle::Result::Continue;
525}
526
527angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context,
528                                            gl::DrawBufferMask clearColorBuffers,
529                                            const mtl::ClearRectParams &clearOpts)
530{
531    ContextMtl *contextMtl = mtl::GetImpl(context);
532    DisplayMtl *display    = contextMtl->getDisplay();
533
534    // Start new render encoder if not already.
535    mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(mRenderPassDesc);
536
537    display->getUtils().clearWithDraw(context, encoder, clearOpts);
538
539    return angle::Result::Continue;
540}
541
542angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
543                                        gl::DrawBufferMask clearColorBuffers,
544                                        mtl::ClearRectParams *pClearOpts)
545{
546    auto &clearOpts = *pClearOpts;
547
548    if (!clearOpts.clearColor.valid() && !clearOpts.clearDepth.valid() &&
549        !clearOpts.clearStencil.valid())
550    {
551        // No Op.
552        return angle::Result::Continue;
553    }
554
555    ContextMtl *contextMtl = mtl::GetImpl(context);
556    const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
557                                   mState.getDimensions().height);
558
559    clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false);
560    clearOpts.flipY     = mFlipY;
561
562    // Discard clear altogether if scissor has 0 width or height.
563    if (clearOpts.clearArea.width == 0 || clearOpts.clearArea.height == 0)
564    {
565        return angle::Result::Continue;
566    }
567
568    MTLColorWriteMask colorMask = contextMtl->getColorMask();
569    uint32_t stencilMask        = contextMtl->getStencilMask();
570    if (!contextMtl->getDepthMask())
571    {
572        // Disable depth clearing, since depth write is disable
573        clearOpts.clearDepth.reset();
574    }
575
576    if (clearOpts.clearArea == renderArea &&
577        (!clearOpts.clearColor.valid() || colorMask == MTLColorWriteMaskAll) &&
578        (!clearOpts.clearStencil.valid() ||
579         (stencilMask & mtl::kStencilMaskAll) == mtl::kStencilMaskAll))
580    {
581        return clearWithLoadOp(context, clearColorBuffers, clearOpts);
582    }
583
584    return clearWithDraw(context, clearColorBuffers, clearOpts);
585}
586
587angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl,
588                                             size_t count,
589                                             const GLenum *attachments)
590{
591    gl::DrawBufferMask invalidateColorBuffers;
592    bool invalidateDepthBuffer   = false;
593    bool invalidateStencilBuffer = false;
594
595    for (size_t i = 0; i < count; ++i)
596    {
597        const GLenum attachment = attachments[i];
598
599        switch (attachment)
600        {
601            case GL_DEPTH:
602            case GL_DEPTH_ATTACHMENT:
603                invalidateDepthBuffer = true;
604                break;
605            case GL_STENCIL:
606            case GL_STENCIL_ATTACHMENT:
607                invalidateStencilBuffer = true;
608                break;
609            case GL_DEPTH_STENCIL_ATTACHMENT:
610                invalidateDepthBuffer   = true;
611                invalidateStencilBuffer = true;
612                break;
613            default:
614                ASSERT(
615                    (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) ||
616                    (attachment == GL_COLOR));
617
618                invalidateColorBuffers.set(
619                    attachment == GL_COLOR ? 0u : (attachment - GL_COLOR_ATTACHMENT0));
620        }
621    }
622
623    // Set the appropriate storeOp for attachments.
624    // If we already start the render pass, then need to set the store action now.
625    bool renderPassStarted = contextMtl->hasStartedRenderPass(mRenderPassDesc);
626    mtl::RenderCommandEncoder *encoder =
627        renderPassStarted ? contextMtl->getRenderCommandEncoder() : nullptr;
628
629    for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
630    {
631        if (invalidateColorBuffers.test(i))
632        {
633            mtl::RenderPassColorAttachmentDesc &colorAttachment =
634                mRenderPassDesc.colorAttachments[i];
635            colorAttachment.storeAction = MTLStoreActionDontCare;
636            if (renderPassStarted)
637            {
638                encoder->setColorStoreAction(MTLStoreActionDontCare, i);
639            }
640        }
641    }
642
643    if (invalidateDepthBuffer && mDepthRenderTarget)
644    {
645        mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare;
646        if (renderPassStarted)
647        {
648            encoder->setDepthStoreAction(MTLStoreActionDontCare);
649        }
650    }
651
652    if (invalidateStencilBuffer && mStencilRenderTarget)
653    {
654        mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare;
655        if (renderPassStarted)
656        {
657            encoder->setStencilStoreAction(MTLStoreActionDontCare);
658        }
659    }
660
661    return angle::Result::Continue;
662}
663
664gl::Rectangle FramebufferMtl::getReadPixelArea(const gl::Rectangle &glArea)
665{
666    RenderTargetMtl *colorReadRT = getColorReadRenderTarget();
667    ASSERT(colorReadRT);
668    gl::Rectangle flippedArea = glArea;
669    if (mFlipY)
670    {
671        flippedArea.y =
672            colorReadRT->getTexture()->height(static_cast<uint32_t>(colorReadRT->getLevelIndex())) -
673            flippedArea.y - flippedArea.height;
674    }
675
676    return flippedArea;
677}
678
679angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
680                                             const gl::Rectangle &area,
681                                             const PackPixelsParams &packPixelsParams,
682                                             RenderTargetMtl *renderTarget,
683                                             uint8_t *pixels)
684{
685    ContextMtl *contextMtl = mtl::GetImpl(context);
686    if (packPixelsParams.packBuffer)
687    {
688        // NOTE(hqle): PBO is not supported atm
689        ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
690    }
691    if (!renderTarget)
692    {
693        return angle::Result::Continue;
694    }
695    const mtl::TextureRef &texture = renderTarget->getTexture();
696
697    if (!texture)
698    {
699        return angle::Result::Continue;
700    }
701
702    const mtl::Format &readFormat        = *renderTarget->getFormat();
703    const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
704
705    // NOTE(hqle): resolve MSAA texture before readback
706    int srcRowPitch = area.width * readAngleFormat.pixelBytes;
707    angle::MemoryBuffer readPixelRowBuffer;
708    ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(srcRowPitch));
709
710    auto packPixelsRowParams  = packPixelsParams;
711    MTLRegion mtlSrcRowRegion = MTLRegionMake2D(area.x, area.y, area.width, 1);
712
713    int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1;
714    int startRow  = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y;
715
716    // Copy pixels row by row
717    packPixelsRowParams.area.height     = 1;
718    packPixelsRowParams.reverseRowOrder = false;
719    for (int r = startRow, i = 0; i < area.height;
720         ++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch)
721    {
722        mtlSrcRowRegion.origin.y   = r;
723        packPixelsRowParams.area.y = packPixelsParams.area.y + i;
724
725        // Read the pixels data to the row buffer
726        texture->getBytes(contextMtl, srcRowPitch, mtlSrcRowRegion,
727                          static_cast<uint32_t>(renderTarget->getLevelIndex()),
728                          readPixelRowBuffer.data());
729
730        // Convert to destination format
731        PackPixels(packPixelsRowParams, readAngleFormat, srcRowPitch, readPixelRowBuffer.data(),
732                   pixels);
733    }
734
735    return angle::Result::Continue;
736}
737}
738