• 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// SurfaceMtl.mm:
7//    Implements the class methods for SurfaceMtl.
8//
9
10#include "libANGLE/renderer/metal/SurfaceMtl.h"
11
12#include "common/platform.h"
13#include "libANGLE/Display.h"
14#include "libANGLE/ErrorStrings.h"
15#include "libANGLE/Surface.h"
16#include "libANGLE/renderer/metal/ContextMtl.h"
17#include "libANGLE/renderer/metal/DisplayMtl.h"
18#include "libANGLE/renderer/metal/FrameBufferMtl.h"
19#include "libANGLE/renderer/metal/mtl_format_utils.h"
20#include "libANGLE/renderer/metal/mtl_utils.h"
21#include "mtl_command_buffer.h"
22
23// Compiler can turn on programmatical frame capture in release build by defining
24// ANGLE_METAL_FRAME_CAPTURE flag.
25#if defined(NDEBUG) && !defined(ANGLE_METAL_FRAME_CAPTURE)
26#    define ANGLE_METAL_FRAME_CAPTURE_ENABLED 0
27#else
28#    define ANGLE_METAL_FRAME_CAPTURE_ENABLED 1
29#endif
30
31namespace rx
32{
33
34namespace
35{
36
37constexpr angle::FormatID kDefaultFrameBufferDepthFormatId   = angle::FormatID::D32_FLOAT;
38constexpr angle::FormatID kDefaultFrameBufferStencilFormatId = angle::FormatID::S8_UINT;
39constexpr angle::FormatID kDefaultFrameBufferDepthStencilFormatId =
40    angle::FormatID::D24_UNORM_S8_UINT;
41
42angle::Result CreateOrResizeTexture(const gl::Context *context,
43                                    const mtl::Format &format,
44                                    uint32_t width,
45                                    uint32_t height,
46                                    uint32_t samples,
47                                    bool renderTargetOnly,
48                                    mtl::TextureRef *textureOut)
49{
50    ContextMtl *contextMtl = mtl::GetImpl(context);
51    bool allowFormatView   = format.hasDepthAndStencilBits();
52    if (*textureOut)
53    {
54        ANGLE_TRY((*textureOut)->resize(contextMtl, width, height));
55        size_t resourceSize = EstimateTextureSizeInBytes(format, width, height, 1, samples, 1);
56        if (*textureOut)
57        {
58            (*textureOut)->setEstimatedByteSize(resourceSize);
59        }
60    }
61    else if (samples > 1)
62    {
63        ANGLE_TRY(mtl::Texture::Make2DMSTexture(contextMtl, format, width, height, samples,
64                                                /** renderTargetOnly */ renderTargetOnly,
65                                                /** allowFormatView */ allowFormatView,
66                                                textureOut));
67    }
68    else
69    {
70        ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, format, width, height, 1,
71                                              /** renderTargetOnly */ renderTargetOnly,
72                                              /** allowFormatView */ allowFormatView, textureOut));
73    }
74    return angle::Result::Continue;
75}
76
77}  // anonymous namespace
78
79// SurfaceMtl implementation
80SurfaceMtl::SurfaceMtl(DisplayMtl *display,
81                       const egl::SurfaceState &state,
82                       const egl::AttributeMap &attribs)
83    : SurfaceImpl(state)
84{
85    mRobustResourceInit =
86        attribs.get(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE;
87    mColorFormat = display->getPixelFormat(angle::FormatID::B8G8R8A8_UNORM);
88
89    mSamples = state.config->samples;
90
91    int depthBits   = 0;
92    int stencilBits = 0;
93    if (state.config)
94    {
95        depthBits   = state.config->depthSize;
96        stencilBits = state.config->stencilSize;
97    }
98
99    if (depthBits && stencilBits)
100    {
101        if (display->getFeatures().allowSeparateDepthStencilBuffers.enabled)
102        {
103            mDepthFormat   = display->getPixelFormat(kDefaultFrameBufferDepthFormatId);
104            mStencilFormat = display->getPixelFormat(kDefaultFrameBufferStencilFormatId);
105        }
106        else
107        {
108            // We must use packed depth stencil
109            mUsePackedDepthStencil = true;
110            mDepthFormat   = display->getPixelFormat(kDefaultFrameBufferDepthStencilFormatId);
111            mStencilFormat = mDepthFormat;
112        }
113    }
114    else if (depthBits)
115    {
116        mDepthFormat = display->getPixelFormat(kDefaultFrameBufferDepthFormatId);
117    }
118    else if (stencilBits)
119    {
120        mStencilFormat = display->getPixelFormat(kDefaultFrameBufferStencilFormatId);
121    }
122}
123
124SurfaceMtl::~SurfaceMtl() {}
125
126void SurfaceMtl::destroy(const egl::Display *display)
127{
128    mColorTexture   = nullptr;
129    mDepthTexture   = nullptr;
130    mStencilTexture = nullptr;
131
132    mMSColorTexture = nullptr;
133
134    mColorRenderTarget.reset();
135    mColorManualResolveRenderTarget.reset();
136    mDepthRenderTarget.reset();
137    mStencilRenderTarget.reset();
138}
139
140egl::Error SurfaceMtl::initialize(const egl::Display *display)
141{
142    return egl::NoError();
143}
144
145egl::Error SurfaceMtl::makeCurrent(const gl::Context *context)
146{
147    ContextMtl *contextMtl = mtl::GetImpl(context);
148    StartFrameCapture(contextMtl);
149
150    return egl::NoError();
151}
152
153egl::Error SurfaceMtl::unMakeCurrent(const gl::Context *context)
154{
155    ContextMtl *contextMtl = mtl::GetImpl(context);
156    contextMtl->flushCommandBuffer(mtl::WaitUntilScheduled);
157
158    StopFrameCapture();
159    return egl::NoError();
160}
161
162egl::Error SurfaceMtl::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
163{
164    return egl::NoError();
165}
166
167egl::Error SurfaceMtl::postSubBuffer(const gl::Context *context,
168                                     EGLint x,
169                                     EGLint y,
170                                     EGLint width,
171                                     EGLint height)
172{
173    UNIMPLEMENTED();
174    return egl::Error(EGL_BAD_ACCESS);
175}
176
177egl::Error SurfaceMtl::querySurfacePointerANGLE(EGLint attribute, void **value)
178{
179    UNIMPLEMENTED();
180    return egl::Error(EGL_BAD_ACCESS);
181}
182
183egl::Error SurfaceMtl::bindTexImage(const gl::Context *context, gl::Texture *texture, EGLint buffer)
184{
185    UNIMPLEMENTED();
186    return egl::Error(EGL_BAD_ACCESS);
187}
188
189egl::Error SurfaceMtl::releaseTexImage(const gl::Context *context, EGLint buffer)
190{
191    UNIMPLEMENTED();
192    return egl::Error(EGL_BAD_ACCESS);
193}
194
195egl::Error SurfaceMtl::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
196{
197    UNIMPLEMENTED();
198    return egl::Error(EGL_BAD_ACCESS);
199}
200
201egl::Error SurfaceMtl::getMscRate(EGLint *numerator, EGLint *denominator)
202{
203    UNIMPLEMENTED();
204    return egl::Error(EGL_BAD_ACCESS);
205}
206
207void SurfaceMtl::setSwapInterval(const egl::Display *display, EGLint interval) {}
208
209void SurfaceMtl::setFixedWidth(EGLint width)
210{
211    UNIMPLEMENTED();
212}
213
214void SurfaceMtl::setFixedHeight(EGLint height)
215{
216    UNIMPLEMENTED();
217}
218
219EGLint SurfaceMtl::getWidth() const
220{
221    if (mColorTexture)
222    {
223        return static_cast<EGLint>(mColorTexture->widthAt0());
224    }
225    return 0;
226}
227
228EGLint SurfaceMtl::getHeight() const
229{
230    if (mColorTexture)
231    {
232        return static_cast<EGLint>(mColorTexture->heightAt0());
233    }
234    return 0;
235}
236
237EGLint SurfaceMtl::isPostSubBufferSupported() const
238{
239    return EGL_FALSE;
240}
241
242EGLint SurfaceMtl::getSwapBehavior() const
243{
244    // dEQP-EGL.functional.query_surface.* requires that for a surface with swap
245    // behavior=EGL_BUFFER_PRESERVED, config.surfaceType must contain
246    // EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
247    // Since we don't support EGL_SWAP_BEHAVIOR_PRESERVED_BIT in egl::Config for now, let's just use
248    // EGL_BUFFER_DESTROYED as default swap behavior.
249    return EGL_BUFFER_DESTROYED;
250}
251
252angle::Result SurfaceMtl::initializeContents(const gl::Context *context,
253                                             GLenum binding,
254                                             const gl::ImageIndex &imageIndex)
255{
256    ASSERT(mColorTexture);
257
258    ContextMtl *contextMtl = mtl::GetImpl(context);
259
260    // Use loadAction=clear
261    mtl::RenderPassDesc rpDesc;
262    rpDesc.rasterSampleCount = mColorTexture->samples();
263
264    switch (binding)
265    {
266        case GL_BACK:
267        {
268            if (mColorTextureInitialized)
269            {
270                return angle::Result::Continue;
271            }
272            rpDesc.numColorAttachments = 1;
273            mColorRenderTarget.toRenderPassAttachmentDesc(&rpDesc.colorAttachments[0]);
274            rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
275            MTLClearColor black                   = {};
276            rpDesc.colorAttachments[0].clearColor =
277                mtl::EmulatedAlphaClearColor(black, mColorTexture->getColorWritableMask());
278            mColorTextureInitialized = true;
279            break;
280        }
281        case GL_DEPTH:
282        case GL_STENCIL:
283        {
284            if (mDepthStencilTexturesInitialized)
285            {
286                return angle::Result::Continue;
287            }
288            if (mDepthTexture)
289            {
290                mDepthRenderTarget.toRenderPassAttachmentDesc(&rpDesc.depthAttachment);
291                rpDesc.depthAttachment.loadAction = MTLLoadActionClear;
292            }
293            if (mStencilTexture)
294            {
295                mStencilRenderTarget.toRenderPassAttachmentDesc(&rpDesc.stencilAttachment);
296                rpDesc.stencilAttachment.loadAction = MTLLoadActionClear;
297            }
298            mDepthStencilTexturesInitialized = true;
299            break;
300        }
301        default:
302            UNREACHABLE();
303            break;
304    }
305    mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(rpDesc);
306    encoder->setStoreAction(MTLStoreActionStore);
307
308    return angle::Result::Continue;
309}
310
311angle::Result SurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
312                                                    GLenum binding,
313                                                    const gl::ImageIndex &imageIndex,
314                                                    GLsizei samples,
315                                                    FramebufferAttachmentRenderTarget **rtOut)
316{
317    ASSERT(mColorTexture);
318
319    switch (binding)
320    {
321        case GL_BACK:
322            *rtOut = &mColorRenderTarget;
323            break;
324        case GL_DEPTH:
325            *rtOut = mDepthFormat.valid() ? &mDepthRenderTarget : nullptr;
326            break;
327        case GL_STENCIL:
328            *rtOut = mStencilFormat.valid() ? &mStencilRenderTarget : nullptr;
329            break;
330        case GL_DEPTH_STENCIL:
331            // NOTE(hqle): ES 3.0 feature
332            UNREACHABLE();
333            break;
334    }
335
336    return angle::Result::Continue;
337}
338
339egl::Error SurfaceMtl::attachToFramebuffer(const gl::Context *context, gl::Framebuffer *framebuffer)
340{
341    return egl::NoError();
342}
343
344egl::Error SurfaceMtl::detachFromFramebuffer(const gl::Context *context,
345                                             gl::Framebuffer *framebuffer)
346{
347    return egl::NoError();
348}
349
350angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context,
351                                                             const gl::Extents &size)
352{
353    ContextMtl *contextMtl = mtl::GetImpl(context);
354
355    ASSERT(mColorTexture);
356
357    if (mSamples > 1 && (!mMSColorTexture || mMSColorTexture->sizeAt0() != size))
358    {
359        mAutoResolveMSColorTexture =
360            contextMtl->getDisplay()->getFeatures().allowMultisampleStoreAndResolve.enabled;
361        ANGLE_TRY(CreateOrResizeTexture(context, mColorFormat, size.width, size.height, mSamples,
362                                        /** renderTargetOnly */ mAutoResolveMSColorTexture,
363                                        &mMSColorTexture));
364
365        if (mAutoResolveMSColorTexture)
366        {
367            // Use auto MSAA resolve at the end of render pass.
368            mColorRenderTarget.setImplicitMSTexture(mMSColorTexture);
369        }
370        else
371        {
372            mColorRenderTarget.setTexture(mMSColorTexture);
373        }
374    }
375
376    if (mDepthFormat.valid() && (!mDepthTexture || mDepthTexture->sizeAt0() != size))
377    {
378        ANGLE_TRY(CreateOrResizeTexture(context, mDepthFormat, size.width, size.height, mSamples,
379                                        /** renderTargetOnly */ false, &mDepthTexture));
380
381        mDepthRenderTarget.set(mDepthTexture, mtl::kZeroNativeMipLevel, 0, mDepthFormat);
382        // Robust resource init: should initialize depth to 1.0.
383        mDepthStencilTexturesInitialized = false;
384    }
385
386    if (mStencilFormat.valid() && (!mStencilTexture || mStencilTexture->sizeAt0() != size))
387    {
388        if (mUsePackedDepthStencil)
389        {
390            mStencilTexture = mDepthTexture;
391        }
392        else
393        {
394            ANGLE_TRY(CreateOrResizeTexture(context, mStencilFormat, size.width, size.height,
395                                            mSamples,
396                                            /** renderTargetOnly */ false, &mStencilTexture));
397        }
398
399        mStencilRenderTarget.set(mStencilTexture, mtl::kZeroNativeMipLevel, 0, mStencilFormat);
400        // Robust resource init: should initialize stencil to zero.
401        mDepthStencilTexturesInitialized = false;
402    }
403
404    return angle::Result::Continue;
405}
406
407angle::Result SurfaceMtl::resolveColorTextureIfNeeded(const gl::Context *context)
408{
409    ASSERT(mMSColorTexture);
410    if (!mAutoResolveMSColorTexture)
411    {
412        // Manually resolve texture
413        ContextMtl *contextMtl = mtl::GetImpl(context);
414
415        mColorManualResolveRenderTarget.set(mColorTexture, mtl::kZeroNativeMipLevel, 0,
416                                            mColorFormat);
417        mtl::RenderCommandEncoder *encoder =
418            contextMtl->getRenderTargetCommandEncoder(mColorManualResolveRenderTarget);
419        ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(
420            context, encoder, mColorFormat.actualAngleFormat(), mMSColorTexture));
421        contextMtl->endEncoding(true);
422        mColorManualResolveRenderTarget.reset();
423    }
424    return angle::Result::Continue;
425}
426
427// WindowSurfaceMtl implementation.
428WindowSurfaceMtl::WindowSurfaceMtl(DisplayMtl *display,
429                                   const egl::SurfaceState &state,
430                                   EGLNativeWindowType window,
431                                   const egl::AttributeMap &attribs)
432    : SurfaceMtl(display, state, attribs), mLayer((__bridge CALayer *)(window))
433{
434    // NOTE(hqle): Width and height attributes is ignored for now.
435    mCurrentKnownDrawableSize = CGSizeMake(0, 0);
436}
437
438WindowSurfaceMtl::~WindowSurfaceMtl() {}
439
440void WindowSurfaceMtl::destroy(const egl::Display *display)
441{
442    SurfaceMtl::destroy(display);
443
444    mCurrentDrawable = nil;
445    if (mMetalLayer && mMetalLayer.get() != mLayer)
446    {
447        // If we created metal layer in WindowSurfaceMtl::initialize(),
448        // we need to detach it from super layer now.
449        [mMetalLayer.get() removeFromSuperlayer];
450    }
451    mMetalLayer = nil;
452}
453
454egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
455{
456    egl::Error re = SurfaceMtl::initialize(display);
457    if (re.isError())
458    {
459        return re;
460    }
461
462    DisplayMtl *displayMtl    = mtl::GetImpl(display);
463    id<MTLDevice> metalDevice = displayMtl->getMetalDevice();
464
465    StartFrameCapture(metalDevice, displayMtl->cmdQueue().get());
466
467    ANGLE_MTL_OBJC_SCOPE
468    {
469        if ([mLayer isKindOfClass:CAMetalLayer.class])
470        {
471            mMetalLayer = static_cast<CAMetalLayer *>(mLayer);
472        }
473        else
474        {
475            mMetalLayer             = angle::adoptObjCPtr([[CAMetalLayer alloc] init]);
476            mMetalLayer.get().frame = mLayer.frame;
477        }
478
479        mMetalLayer.get().device          = metalDevice;
480        mMetalLayer.get().pixelFormat     = mColorFormat.metalFormat;
481        mMetalLayer.get().framebufferOnly = NO;  // Support blitting and glReadPixels
482
483#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
484        // Autoresize with parent layer.
485        mMetalLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
486#endif
487        if (mMetalLayer.get() != mLayer)
488        {
489            mMetalLayer.get().contentsScale = mLayer.contentsScale;
490
491            [mLayer addSublayer:mMetalLayer.get()];
492        }
493
494        // ensure drawableSize is set to correct value:
495        mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = calcExpectedDrawableSize();
496    }
497
498    return egl::NoError();
499}
500
501egl::Error WindowSurfaceMtl::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
502{
503    ANGLE_TO_EGL_TRY(swapImpl(context));
504
505    return egl::NoError();
506}
507
508void WindowSurfaceMtl::setSwapInterval(const egl::Display *display, EGLint interval)
509{
510#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
511    mMetalLayer.get().displaySyncEnabled = interval != 0;
512#endif
513}
514
515// width and height can change with client window resizing
516EGLint WindowSurfaceMtl::getWidth() const
517{
518    return static_cast<EGLint>(mCurrentKnownDrawableSize.width);
519}
520
521EGLint WindowSurfaceMtl::getHeight() const
522{
523    return static_cast<EGLint>(mCurrentKnownDrawableSize.height);
524}
525
526EGLint WindowSurfaceMtl::getSwapBehavior() const
527{
528    return EGL_BUFFER_DESTROYED;
529}
530
531angle::Result WindowSurfaceMtl::initializeContents(const gl::Context *context,
532                                                   GLenum binding,
533                                                   const gl::ImageIndex &imageIndex)
534{
535    ANGLE_TRY(ensureCurrentDrawableObtained(context));
536    return SurfaceMtl::initializeContents(context, binding, imageIndex);
537}
538
539angle::Result WindowSurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
540                                                          GLenum binding,
541                                                          const gl::ImageIndex &imageIndex,
542                                                          GLsizei samples,
543                                                          FramebufferAttachmentRenderTarget **rtOut)
544{
545    ANGLE_TRY(ensureCurrentDrawableObtained(context));
546    ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
547
548    return SurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
549}
550
551egl::Error WindowSurfaceMtl::attachToFramebuffer(const gl::Context *context,
552                                                 gl::Framebuffer *framebuffer)
553{
554    FramebufferMtl *framebufferMtl = GetImplAs<FramebufferMtl>(framebuffer);
555    ASSERT(!framebufferMtl->getBackbuffer());
556    framebufferMtl->setBackbuffer(this);
557    framebufferMtl->setFlipY(true);
558    return egl::NoError();
559}
560
561egl::Error WindowSurfaceMtl::detachFromFramebuffer(const gl::Context *context,
562                                                   gl::Framebuffer *framebuffer)
563{
564    FramebufferMtl *framebufferMtl = GetImplAs<FramebufferMtl>(framebuffer);
565    ASSERT(framebufferMtl->getBackbuffer() == this);
566    framebufferMtl->setBackbuffer(nullptr);
567    framebufferMtl->setFlipY(false);
568    return egl::NoError();
569}
570
571angle::Result WindowSurfaceMtl::ensureCurrentDrawableObtained(const gl::Context *context)
572{
573    if (!mCurrentDrawable)
574    {
575        ANGLE_TRY(obtainNextDrawable(context));
576    }
577
578    return angle::Result::Continue;
579}
580
581angle::Result WindowSurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context)
582{
583    ASSERT(mMetalLayer);
584
585    gl::Extents size(static_cast<int>(mMetalLayer.get().drawableSize.width),
586                     static_cast<int>(mMetalLayer.get().drawableSize.height), 1);
587
588    ANGLE_TRY(SurfaceMtl::ensureCompanionTexturesSizeCorrect(context, size));
589
590    return angle::Result::Continue;
591}
592
593angle::Result WindowSurfaceMtl::ensureColorTextureReadyForReadPixels(const gl::Context *context)
594{
595    ANGLE_TRY(ensureCurrentDrawableObtained(context));
596
597    if (mMSColorTexture)
598    {
599        if (mMSColorTexture->isCPUReadMemNeedSync())
600        {
601            ANGLE_TRY(resolveColorTextureIfNeeded(context));
602            mMSColorTexture->resetCPUReadMemNeedSync();
603        }
604    }
605
606    return angle::Result::Continue;
607}
608
609CGSize WindowSurfaceMtl::calcExpectedDrawableSize() const
610{
611    CGSize currentLayerSize           = mMetalLayer.get().bounds.size;
612    CGFloat currentLayerContentsScale = mMetalLayer.get().contentsScale;
613    CGSize expectedDrawableSize = CGSizeMake(currentLayerSize.width * currentLayerContentsScale,
614                                             currentLayerSize.height * currentLayerContentsScale);
615
616    return expectedDrawableSize;
617}
618
619bool WindowSurfaceMtl::checkIfLayerResized(const gl::Context *context)
620{
621    if (mMetalLayer.get() != mLayer)
622    {
623        if (mMetalLayer.get().contentsScale != mLayer.contentsScale)
624        {
625            // Parent layer's content scale has changed, update Metal layer's scale factor.
626            mMetalLayer.get().contentsScale = mLayer.contentsScale;
627        }
628#if !TARGET_OS_OSX && !TARGET_OS_MACCATALYST
629        // Only macOS supports autoresizing mask. Thus, the metal layer has to be manually
630        // updated.
631        if (!CGRectEqualToRect(mMetalLayer.get().bounds, mLayer.bounds))
632        {
633            // Parent layer's bounds has changed, update the Metal layer's bounds as well.
634            mMetalLayer.get().bounds = mLayer.bounds;
635        }
636#endif
637    }
638
639    CGSize currentLayerDrawableSize = mMetalLayer.get().drawableSize;
640    CGSize expectedDrawableSize     = calcExpectedDrawableSize();
641
642    // NOTE(hqle): We need to compare the size against mCurrentKnownDrawableSize also.
643    // That is because metal framework might internally change the drawableSize property of
644    // metal layer, and it might become equal to expectedDrawableSize. If that happens, we cannot
645    // know whether the layer has been resized or not.
646    if (currentLayerDrawableSize.width != expectedDrawableSize.width ||
647        currentLayerDrawableSize.height != expectedDrawableSize.height ||
648        mCurrentKnownDrawableSize.width != expectedDrawableSize.width ||
649        mCurrentKnownDrawableSize.height != expectedDrawableSize.height)
650    {
651        // Resize the internal drawable texture.
652        mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = expectedDrawableSize;
653
654        return true;
655    }
656
657    return false;
658}
659
660angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
661{
662    ANGLE_MTL_OBJC_SCOPE
663    {
664        ContextMtl *contextMtl = mtl::GetImpl(context);
665        ANGLE_CHECK(contextMtl, mMetalLayer, gl::err::kInternalError, GL_INVALID_OPERATION);
666
667        // Check if layer was resized
668        if (checkIfLayerResized(context))
669        {
670            contextMtl->onBackbufferResized(context, this);
671        }
672
673        mCurrentDrawable = [mMetalLayer nextDrawable];
674        if (!mCurrentDrawable)
675        {
676            // The GPU might be taking too long finishing its rendering to the previous frame.
677            // Try again, indefinitely wait until the previous frame render finishes.
678            // TODO: this may wait forever here
679            mMetalLayer.get().allowsNextDrawableTimeout = NO;
680            mCurrentDrawable                            = [mMetalLayer nextDrawable];
681            mMetalLayer.get().allowsNextDrawableTimeout = YES;
682        }
683
684        if (!mColorTexture)
685        {
686            mColorTexture = mtl::Texture::MakeFromMetal(mCurrentDrawable.get().texture);
687            ASSERT(!mColorRenderTarget.getTexture());
688            mColorRenderTarget.setWithImplicitMSTexture(mColorTexture, mMSColorTexture,
689                                                        mtl::kZeroNativeMipLevel, 0, mColorFormat);
690        }
691        else
692        {
693            mColorTexture->set(mCurrentDrawable.get().texture);
694        }
695        mColorTextureInitialized = false;
696
697        ANGLE_MTL_LOG("Current metal drawable size=%d,%d", mColorTexture->width(),
698                      mColorTexture->height());
699
700        // Now we have to resize depth stencil buffers if required.
701        ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
702
703        return angle::Result::Continue;
704    }
705}
706
707angle::Result WindowSurfaceMtl::swapImpl(const gl::Context *context)
708{
709    if (mCurrentDrawable)
710    {
711        ASSERT(mColorTexture);
712
713        ContextMtl *contextMtl = mtl::GetImpl(context);
714
715        if (mMSColorTexture)
716        {
717            ANGLE_TRY(resolveColorTextureIfNeeded(context));
718        }
719
720        contextMtl->present(context, mCurrentDrawable);
721
722        StopFrameCapture();
723        StartFrameCapture(contextMtl);
724
725        // Invalidate current drawable
726        mColorTexture->set(nil);
727        mCurrentDrawable = nil;
728    }
729    // Robust resource init: should initialize stencil zero and depth to 1.0 after swap.
730    if (mDepthTexture || mStencilTexture)
731    {
732        mDepthStencilTexturesInitialized = false;
733    }
734    return angle::Result::Continue;
735}
736
737// OffscreenSurfaceMtl implementation
738OffscreenSurfaceMtl::OffscreenSurfaceMtl(DisplayMtl *display,
739                                         const egl::SurfaceState &state,
740                                         const egl::AttributeMap &attribs)
741    : SurfaceMtl(display, state, attribs)
742{
743    mSize = gl::Extents(attribs.getAsInt(EGL_WIDTH, 1), attribs.getAsInt(EGL_HEIGHT, 1), 1);
744}
745
746OffscreenSurfaceMtl::~OffscreenSurfaceMtl() {}
747
748void OffscreenSurfaceMtl::destroy(const egl::Display *display)
749{
750    SurfaceMtl::destroy(display);
751}
752
753EGLint OffscreenSurfaceMtl::getWidth() const
754{
755    return mSize.width;
756}
757
758EGLint OffscreenSurfaceMtl::getHeight() const
759{
760    return mSize.height;
761}
762
763egl::Error OffscreenSurfaceMtl::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
764{
765    // Check for surface resize.
766    ANGLE_TO_EGL_TRY(ensureTexturesSizeCorrect(context));
767
768    return egl::NoError();
769}
770
771egl::Error OffscreenSurfaceMtl::bindTexImage(const gl::Context *context,
772                                             gl::Texture *texture,
773                                             EGLint buffer)
774{
775    ContextMtl *contextMtl = mtl::GetImpl(context);
776    contextMtl->flushCommandBuffer(mtl::NoWait);
777
778    // Initialize offscreen textures if needed:
779    ANGLE_TO_EGL_TRY(ensureTexturesSizeCorrect(context));
780
781    return egl::NoError();
782}
783
784egl::Error OffscreenSurfaceMtl::releaseTexImage(const gl::Context *context, EGLint buffer)
785{
786    ContextMtl *contextMtl = mtl::GetImpl(context);
787
788    if (mMSColorTexture)
789    {
790        ANGLE_TO_EGL_TRY(resolveColorTextureIfNeeded(context));
791    }
792
793    // NOTE(hqle): Should we finishCommandBuffer or flush is enough?
794    contextMtl->flushCommandBuffer(mtl::NoWait);
795    return egl::NoError();
796}
797
798angle::Result OffscreenSurfaceMtl::getAttachmentRenderTarget(
799    const gl::Context *context,
800    GLenum binding,
801    const gl::ImageIndex &imageIndex,
802    GLsizei samples,
803    FramebufferAttachmentRenderTarget **rtOut)
804{
805    // Initialize offscreen textures if needed:
806    ANGLE_TRY(ensureTexturesSizeCorrect(context));
807
808    return SurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
809}
810
811angle::Result OffscreenSurfaceMtl::ensureTexturesSizeCorrect(const gl::Context *context)
812{
813    if (!mColorTexture || mColorTexture->sizeAt0() != mSize)
814    {
815        ANGLE_TRY(CreateOrResizeTexture(context, mColorFormat, mSize.width, mSize.height, 1,
816                                        /** renderTargetOnly */ false, &mColorTexture));
817
818        mColorRenderTarget.set(mColorTexture, mtl::kZeroNativeMipLevel, 0, mColorFormat);
819    }
820
821    return ensureCompanionTexturesSizeCorrect(context, mSize);
822}
823
824// PBufferSurfaceMtl implementation
825PBufferSurfaceMtl::PBufferSurfaceMtl(DisplayMtl *display,
826                                     const egl::SurfaceState &state,
827                                     const egl::AttributeMap &attribs)
828    : OffscreenSurfaceMtl(display, state, attribs)
829{}
830
831void PBufferSurfaceMtl::setFixedWidth(EGLint width)
832{
833    mSize.width = width;
834}
835
836void PBufferSurfaceMtl::setFixedHeight(EGLint height)
837{
838    mSize.height = height;
839}
840
841}  // namespace rx
842