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