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