xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/SurfaceMtl.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 1
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(const egl::Display *display, 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    ContextMtl *contextMtl = mtl::GetImpl(context);
258
259    // Use loadAction=clear
260    mtl::RenderPassDesc rpDesc;
261    rpDesc.rasterSampleCount = mColorTexture->samples();
262
263    switch (binding)
264    {
265        case GL_BACK:
266        {
267            if (mColorTextureInitialized)
268            {
269                return angle::Result::Continue;
270            }
271            rpDesc.numColorAttachments = 1;
272            mColorRenderTarget.toRenderPassAttachmentDesc(&rpDesc.colorAttachments[0]);
273            rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
274            MTLClearColor black                   = {};
275            rpDesc.colorAttachments[0].clearColor =
276                mtl::EmulatedAlphaClearColor(black, mColorTexture->getColorWritableMask());
277            mColorTextureInitialized = true;
278            break;
279        }
280        case GL_DEPTH:
281        case GL_STENCIL:
282        {
283            if (mDepthStencilTexturesInitialized)
284            {
285                return angle::Result::Continue;
286            }
287            if (mDepthTexture)
288            {
289                mDepthRenderTarget.toRenderPassAttachmentDesc(&rpDesc.depthAttachment);
290                rpDesc.depthAttachment.loadAction = MTLLoadActionClear;
291            }
292            if (mStencilTexture)
293            {
294                mStencilRenderTarget.toRenderPassAttachmentDesc(&rpDesc.stencilAttachment);
295                rpDesc.stencilAttachment.loadAction = MTLLoadActionClear;
296            }
297            mDepthStencilTexturesInitialized = true;
298            break;
299        }
300        default:
301            UNREACHABLE();
302            break;
303    }
304    mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(rpDesc);
305    encoder->setStoreAction(MTLStoreActionStore);
306
307    return angle::Result::Continue;
308}
309
310angle::Result SurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
311                                                    GLenum binding,
312                                                    const gl::ImageIndex &imageIndex,
313                                                    GLsizei samples,
314                                                    FramebufferAttachmentRenderTarget **rtOut)
315{
316    ASSERT(mColorTexture);
317
318    switch (binding)
319    {
320        case GL_BACK:
321            *rtOut = &mColorRenderTarget;
322            break;
323        case GL_DEPTH:
324            *rtOut = mDepthFormat.valid() ? &mDepthRenderTarget : nullptr;
325            break;
326        case GL_STENCIL:
327            *rtOut = mStencilFormat.valid() ? &mStencilRenderTarget : nullptr;
328            break;
329        case GL_DEPTH_STENCIL:
330            // NOTE(hqle): ES 3.0 feature
331            UNREACHABLE();
332            break;
333    }
334
335    return angle::Result::Continue;
336}
337
338egl::Error SurfaceMtl::attachToFramebuffer(const gl::Context *context, gl::Framebuffer *framebuffer)
339{
340    return egl::NoError();
341}
342
343egl::Error SurfaceMtl::detachFromFramebuffer(const gl::Context *context,
344                                             gl::Framebuffer *framebuffer)
345{
346    return egl::NoError();
347}
348
349angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context,
350                                                             const gl::Extents &size)
351{
352    ContextMtl *contextMtl = mtl::GetImpl(context);
353
354    ASSERT(mColorTexture);
355
356    if (mSamples > 1 && (!mMSColorTexture || mMSColorTexture->sizeAt0() != size))
357    {
358        mAutoResolveMSColorTexture =
359            contextMtl->getDisplay()->getFeatures().allowMultisampleStoreAndResolve.enabled;
360        ANGLE_TRY(CreateOrResizeTexture(context, mColorFormat, size.width, size.height, mSamples,
361                                        /** renderTargetOnly */ mAutoResolveMSColorTexture,
362                                        &mMSColorTexture));
363
364        if (mAutoResolveMSColorTexture)
365        {
366            // Use auto MSAA resolve at the end of render pass.
367            mColorRenderTarget.setImplicitMSTexture(mMSColorTexture);
368        }
369        else
370        {
371            mColorRenderTarget.setTexture(mMSColorTexture);
372        }
373    }
374
375    if (mDepthFormat.valid() && (!mDepthTexture || mDepthTexture->sizeAt0() != size))
376    {
377        ANGLE_TRY(CreateOrResizeTexture(context, mDepthFormat, size.width, size.height, mSamples,
378                                        /** renderTargetOnly */ false, &mDepthTexture));
379
380        mDepthRenderTarget.set(mDepthTexture, mtl::kZeroNativeMipLevel, 0, mDepthFormat);
381        // Robust resource init: should initialize depth to 1.0.
382        mDepthStencilTexturesInitialized = false;
383    }
384
385    if (mStencilFormat.valid() && (!mStencilTexture || mStencilTexture->sizeAt0() != size))
386    {
387        if (mUsePackedDepthStencil)
388        {
389            mStencilTexture = mDepthTexture;
390        }
391        else
392        {
393            ANGLE_TRY(CreateOrResizeTexture(context, mStencilFormat, size.width, size.height,
394                                            mSamples,
395                                            /** renderTargetOnly */ false, &mStencilTexture));
396        }
397
398        mStencilRenderTarget.set(mStencilTexture, mtl::kZeroNativeMipLevel, 0, mStencilFormat);
399        // Robust resource init: should initialize stencil to zero.
400        mDepthStencilTexturesInitialized = false;
401    }
402
403    return angle::Result::Continue;
404}
405
406angle::Result SurfaceMtl::resolveColorTextureIfNeeded(const gl::Context *context)
407{
408    ASSERT(mMSColorTexture);
409    if (!mAutoResolveMSColorTexture)
410    {
411        // Manually resolve texture
412        ContextMtl *contextMtl = mtl::GetImpl(context);
413
414        mColorManualResolveRenderTarget.set(mColorTexture, mtl::kZeroNativeMipLevel, 0,
415                                            mColorFormat);
416        mtl::RenderCommandEncoder *encoder =
417            contextMtl->getRenderTargetCommandEncoder(mColorManualResolveRenderTarget);
418        ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(
419            context, encoder, mColorFormat.actualAngleFormat(), mMSColorTexture));
420        contextMtl->endEncoding(true);
421        mColorManualResolveRenderTarget.reset();
422    }
423    return angle::Result::Continue;
424}
425
426// WindowSurfaceMtl implementation.
427WindowSurfaceMtl::WindowSurfaceMtl(DisplayMtl *display,
428                                   const egl::SurfaceState &state,
429                                   EGLNativeWindowType window,
430                                   const egl::AttributeMap &attribs)
431    : SurfaceMtl(display, state, attribs), mLayer((__bridge CALayer *)(window))
432{
433    // NOTE(hqle): Width and height attributes is ignored for now.
434    mCurrentKnownDrawableSize = CGSizeMake(0, 0);
435}
436
437WindowSurfaceMtl::~WindowSurfaceMtl() {}
438
439void WindowSurfaceMtl::destroy(const egl::Display *display)
440{
441    SurfaceMtl::destroy(display);
442
443    mCurrentDrawable = nil;
444    if (mMetalLayer && mMetalLayer.get() != mLayer)
445    {
446        // If we created metal layer in WindowSurfaceMtl::initialize(),
447        // we need to detach it from super layer now.
448        [mMetalLayer.get() removeFromSuperlayer];
449    }
450    mMetalLayer = nil;
451}
452
453egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
454{
455    egl::Error re = SurfaceMtl::initialize(display);
456    if (re.isError())
457    {
458        return re;
459    }
460
461    DisplayMtl *displayMtl    = mtl::GetImpl(display);
462    id<MTLDevice> metalDevice = displayMtl->getMetalDevice();
463
464    StartFrameCapture(metalDevice, displayMtl->cmdQueue().get());
465
466    ANGLE_MTL_OBJC_SCOPE
467    {
468        if ([mLayer isKindOfClass:CAMetalLayer.class])
469        {
470            mMetalLayer.retainAssign(static_cast<CAMetalLayer *>(mLayer));
471        }
472        else
473        {
474            mMetalLayer             = [[[CAMetalLayer alloc] init] ANGLE_MTL_AUTORELEASE];
475            mMetalLayer.get().frame = mLayer.frame;
476        }
477
478        mMetalLayer.get().device          = metalDevice;
479        mMetalLayer.get().pixelFormat     = mColorFormat.metalFormat;
480        mMetalLayer.get().framebufferOnly = NO;  // Support blitting and glReadPixels
481
482#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
483        // Autoresize with parent layer.
484        mMetalLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
485#endif
486        if (mMetalLayer.get() != mLayer)
487        {
488            mMetalLayer.get().contentsScale = mLayer.contentsScale;
489
490            [mLayer addSublayer:mMetalLayer.get()];
491        }
492
493        // ensure drawableSize is set to correct value:
494        mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = calcExpectedDrawableSize();
495    }
496
497    return egl::NoError();
498}
499
500egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
501{
502    ANGLE_TO_EGL_TRY(swapImpl(context));
503
504    return egl::NoError();
505}
506
507void WindowSurfaceMtl::setSwapInterval(const egl::Display *display, EGLint interval)
508{
509#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
510    mMetalLayer.get().displaySyncEnabled = interval != 0;
511#endif
512}
513
514// width and height can change with client window resizing
515EGLint WindowSurfaceMtl::getWidth() const
516{
517    return static_cast<EGLint>(mCurrentKnownDrawableSize.width);
518}
519
520EGLint WindowSurfaceMtl::getHeight() const
521{
522    return static_cast<EGLint>(mCurrentKnownDrawableSize.height);
523}
524
525EGLint WindowSurfaceMtl::getSwapBehavior() const
526{
527    return EGL_BUFFER_DESTROYED;
528}
529
530angle::Result WindowSurfaceMtl::initializeContents(const gl::Context *context,
531                                                   GLenum binding,
532                                                   const gl::ImageIndex &imageIndex)
533{
534    ANGLE_TRY(ensureCurrentDrawableObtained(context));
535    return SurfaceMtl::initializeContents(context, binding, imageIndex);
536}
537
538angle::Result WindowSurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
539                                                          GLenum binding,
540                                                          const gl::ImageIndex &imageIndex,
541                                                          GLsizei samples,
542                                                          FramebufferAttachmentRenderTarget **rtOut)
543{
544    ANGLE_TRY(ensureCurrentDrawableObtained(context));
545    ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
546
547    return SurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
548}
549
550egl::Error WindowSurfaceMtl::attachToFramebuffer(const gl::Context *context,
551                                                 gl::Framebuffer *framebuffer)
552{
553    FramebufferMtl *framebufferMtl = GetImplAs<FramebufferMtl>(framebuffer);
554    ASSERT(!framebufferMtl->getBackbuffer());
555    framebufferMtl->setBackbuffer(this);
556    framebufferMtl->setFlipY(true);
557    return egl::NoError();
558}
559
560egl::Error WindowSurfaceMtl::detachFromFramebuffer(const gl::Context *context,
561                                                   gl::Framebuffer *framebuffer)
562{
563    FramebufferMtl *framebufferMtl = GetImplAs<FramebufferMtl>(framebuffer);
564    ASSERT(framebufferMtl->getBackbuffer() == this);
565    framebufferMtl->setBackbuffer(nullptr);
566    framebufferMtl->setFlipY(false);
567    return egl::NoError();
568}
569
570angle::Result WindowSurfaceMtl::ensureCurrentDrawableObtained(const gl::Context *context)
571{
572    if (!mCurrentDrawable)
573    {
574        ANGLE_TRY(obtainNextDrawable(context));
575    }
576
577    return angle::Result::Continue;
578}
579
580angle::Result WindowSurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context)
581{
582    ASSERT(mMetalLayer);
583
584    gl::Extents size(static_cast<int>(mMetalLayer.get().drawableSize.width),
585                     static_cast<int>(mMetalLayer.get().drawableSize.height), 1);
586
587    ANGLE_TRY(SurfaceMtl::ensureCompanionTexturesSizeCorrect(context, size));
588
589    return angle::Result::Continue;
590}
591
592angle::Result WindowSurfaceMtl::ensureColorTextureReadyForReadPixels(const gl::Context *context)
593{
594    ANGLE_TRY(ensureCurrentDrawableObtained(context));
595
596    if (mMSColorTexture)
597    {
598        if (mMSColorTexture->isCPUReadMemNeedSync())
599        {
600            ANGLE_TRY(resolveColorTextureIfNeeded(context));
601            mMSColorTexture->resetCPUReadMemNeedSync();
602        }
603    }
604
605    return angle::Result::Continue;
606}
607
608CGSize WindowSurfaceMtl::calcExpectedDrawableSize() const
609{
610    CGSize currentLayerSize           = mMetalLayer.get().bounds.size;
611    CGFloat currentLayerContentsScale = mMetalLayer.get().contentsScale;
612    CGSize expectedDrawableSize = CGSizeMake(currentLayerSize.width * currentLayerContentsScale,
613                                             currentLayerSize.height * currentLayerContentsScale);
614
615    return expectedDrawableSize;
616}
617
618bool WindowSurfaceMtl::checkIfLayerResized(const gl::Context *context)
619{
620    if (mMetalLayer.get() != mLayer)
621    {
622        if (mMetalLayer.get().contentsScale != mLayer.contentsScale)
623        {
624            // Parent layer's content scale has changed, update Metal layer's scale factor.
625            mMetalLayer.get().contentsScale = mLayer.contentsScale;
626        }
627#if !TARGET_OS_OSX && !TARGET_OS_MACCATALYST
628        // Only macOS supports autoresizing mask. Thus, the metal layer has to be manually
629        // updated.
630        if (!CGRectEqualToRect(mMetalLayer.get().bounds, mLayer.bounds))
631        {
632            // Parent layer's bounds has changed, update the Metal layer's bounds as well.
633            mMetalLayer.get().bounds = mLayer.bounds;
634        }
635#endif
636    }
637
638    CGSize currentLayerDrawableSize = mMetalLayer.get().drawableSize;
639    CGSize expectedDrawableSize     = calcExpectedDrawableSize();
640
641    // NOTE(hqle): We need to compare the size against mCurrentKnownDrawableSize also.
642    // That is because metal framework might internally change the drawableSize property of
643    // metal layer, and it might become equal to expectedDrawableSize. If that happens, we cannot
644    // know whether the layer has been resized or not.
645    if (currentLayerDrawableSize.width != expectedDrawableSize.width ||
646        currentLayerDrawableSize.height != expectedDrawableSize.height ||
647        mCurrentKnownDrawableSize.width != expectedDrawableSize.width ||
648        mCurrentKnownDrawableSize.height != expectedDrawableSize.height)
649    {
650        // Resize the internal drawable texture.
651        mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = expectedDrawableSize;
652
653        return true;
654    }
655
656    return false;
657}
658
659angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
660{
661    ANGLE_MTL_OBJC_SCOPE
662    {
663        ContextMtl *contextMtl = mtl::GetImpl(context);
664
665        ANGLE_MTL_TRY(contextMtl, mMetalLayer);
666
667        // Check if layer was resized
668        if (checkIfLayerResized(context))
669        {
670            contextMtl->onBackbufferResized(context, this);
671        }
672
673        mCurrentDrawable.retainAssign([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.retainAssign([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)
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