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