• 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    return display->getUtils().clearWithDraw(context, encoder, clearOpts);
538}
539
540angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
541                                        gl::DrawBufferMask clearColorBuffers,
542                                        mtl::ClearRectParams *pClearOpts)
543{
544    auto &clearOpts = *pClearOpts;
545
546    if (!clearOpts.clearColor.valid() && !clearOpts.clearDepth.valid() &&
547        !clearOpts.clearStencil.valid())
548    {
549        // No Op.
550        return angle::Result::Continue;
551    }
552
553    ContextMtl *contextMtl = mtl::GetImpl(context);
554    const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
555                                   mState.getDimensions().height);
556
557    clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false);
558    clearOpts.flipY     = mFlipY;
559
560    // Discard clear altogether if scissor has 0 width or height.
561    if (clearOpts.clearArea.width == 0 || clearOpts.clearArea.height == 0)
562    {
563        return angle::Result::Continue;
564    }
565
566    MTLColorWriteMask colorMask = contextMtl->getColorMask();
567    uint32_t stencilMask        = contextMtl->getStencilMask();
568    if (!contextMtl->getDepthMask())
569    {
570        // Disable depth clearing, since depth write is disable
571        clearOpts.clearDepth.reset();
572    }
573
574    if (clearOpts.clearArea == renderArea &&
575        (!clearOpts.clearColor.valid() || colorMask == MTLColorWriteMaskAll) &&
576        (!clearOpts.clearStencil.valid() ||
577         (stencilMask & mtl::kStencilMaskAll) == mtl::kStencilMaskAll))
578    {
579        return clearWithLoadOp(context, clearColorBuffers, clearOpts);
580    }
581
582    return clearWithDraw(context, clearColorBuffers, clearOpts);
583}
584
585angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl,
586                                             size_t count,
587                                             const GLenum *attachments)
588{
589    gl::DrawBufferMask invalidateColorBuffers;
590    bool invalidateDepthBuffer   = false;
591    bool invalidateStencilBuffer = false;
592
593    for (size_t i = 0; i < count; ++i)
594    {
595        const GLenum attachment = attachments[i];
596
597        switch (attachment)
598        {
599            case GL_DEPTH:
600            case GL_DEPTH_ATTACHMENT:
601                invalidateDepthBuffer = true;
602                break;
603            case GL_STENCIL:
604            case GL_STENCIL_ATTACHMENT:
605                invalidateStencilBuffer = true;
606                break;
607            case GL_DEPTH_STENCIL_ATTACHMENT:
608                invalidateDepthBuffer   = true;
609                invalidateStencilBuffer = true;
610                break;
611            default:
612                ASSERT(
613                    (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) ||
614                    (attachment == GL_COLOR));
615
616                invalidateColorBuffers.set(
617                    attachment == GL_COLOR ? 0u : (attachment - GL_COLOR_ATTACHMENT0));
618        }
619    }
620
621    // Set the appropriate storeOp for attachments.
622    // If we already start the render pass, then need to set the store action now.
623    bool renderPassStarted = contextMtl->hasStartedRenderPass(mRenderPassDesc);
624    mtl::RenderCommandEncoder *encoder =
625        renderPassStarted ? contextMtl->getRenderCommandEncoder() : nullptr;
626
627    for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
628    {
629        if (invalidateColorBuffers.test(i))
630        {
631            mtl::RenderPassColorAttachmentDesc &colorAttachment =
632                mRenderPassDesc.colorAttachments[i];
633            colorAttachment.storeAction = MTLStoreActionDontCare;
634            if (renderPassStarted)
635            {
636                encoder->setColorStoreAction(MTLStoreActionDontCare, i);
637            }
638        }
639    }
640
641    if (invalidateDepthBuffer && mDepthRenderTarget)
642    {
643        mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare;
644        if (renderPassStarted)
645        {
646            encoder->setDepthStoreAction(MTLStoreActionDontCare);
647        }
648    }
649
650    if (invalidateStencilBuffer && mStencilRenderTarget)
651    {
652        mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare;
653        if (renderPassStarted)
654        {
655            encoder->setStencilStoreAction(MTLStoreActionDontCare);
656        }
657    }
658
659    return angle::Result::Continue;
660}
661
662gl::Rectangle FramebufferMtl::getReadPixelArea(const gl::Rectangle &glArea)
663{
664    RenderTargetMtl *colorReadRT = getColorReadRenderTarget();
665    ASSERT(colorReadRT);
666    gl::Rectangle flippedArea = glArea;
667    if (mFlipY)
668    {
669        flippedArea.y =
670            colorReadRT->getTexture()->height(static_cast<uint32_t>(colorReadRT->getLevelIndex())) -
671            flippedArea.y - flippedArea.height;
672    }
673
674    return flippedArea;
675}
676
677angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
678                                             const gl::Rectangle &area,
679                                             const PackPixelsParams &packPixelsParams,
680                                             RenderTargetMtl *renderTarget,
681                                             uint8_t *pixels)
682{
683    ContextMtl *contextMtl = mtl::GetImpl(context);
684    if (packPixelsParams.packBuffer)
685    {
686        // NOTE(hqle): PBO is not supported atm
687        ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
688    }
689    if (!renderTarget)
690    {
691        return angle::Result::Continue;
692    }
693    const mtl::TextureRef &texture = renderTarget->getTexture();
694
695    if (!texture)
696    {
697        return angle::Result::Continue;
698    }
699
700    const mtl::Format &readFormat        = *renderTarget->getFormat();
701    const angle::Format &readAngleFormat = readFormat.actualAngleFormat();
702
703    // NOTE(hqle): resolve MSAA texture before readback
704    int srcRowPitch = area.width * readAngleFormat.pixelBytes;
705    angle::MemoryBuffer readPixelRowBuffer;
706    ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(srcRowPitch));
707
708    auto packPixelsRowParams  = packPixelsParams;
709    MTLRegion mtlSrcRowRegion = MTLRegionMake2D(area.x, area.y, area.width, 1);
710
711    int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1;
712    int startRow  = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y;
713
714    // Copy pixels row by row
715    packPixelsRowParams.area.height     = 1;
716    packPixelsRowParams.reverseRowOrder = false;
717    for (int r = startRow, i = 0; i < area.height;
718         ++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch)
719    {
720        mtlSrcRowRegion.origin.y   = r;
721        packPixelsRowParams.area.y = packPixelsParams.area.y + i;
722
723        // Read the pixels data to the row buffer
724        texture->getBytes(contextMtl, srcRowPitch, mtlSrcRowRegion,
725                          static_cast<uint32_t>(renderTarget->getLevelIndex()),
726                          readPixelRowBuffer.data());
727
728        // Convert to destination format
729        PackPixels(packPixelsRowParams, readAngleFormat, srcRowPitch, readPixelRowBuffer.data(),
730                   pixels);
731    }
732
733    return angle::Result::Continue;
734}
735}
736