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