1// 2// Copyright 2019 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6// FramebufferMtl.mm: 7// Implements the class methods for FramebufferMtl. 8// 9 10#include "libANGLE/renderer/metal/ContextMtl.h" 11 12#include <TargetConditionals.h> 13 14#include "common/MemoryBuffer.h" 15#include "common/angleutils.h" 16#include "common/debug.h" 17#include "libANGLE/renderer/metal/DisplayMtl.h" 18#include "libANGLE/renderer/metal/FrameBufferMtl.h" 19#include "libANGLE/renderer/metal/SurfaceMtl.h" 20#include "libANGLE/renderer/metal/mtl_utils.h" 21#include "libANGLE/renderer/renderer_utils.h" 22 23namespace rx 24{ 25 26namespace 27{ 28 29const gl::InternalFormat &GetReadAttachmentInfo(const gl::Context *context, 30 RenderTargetMtl *renderTarget) 31{ 32 GLenum implFormat; 33 34 if (renderTarget && renderTarget->getFormat()) 35 { 36 implFormat = renderTarget->getFormat()->actualAngleFormat().fboImplementationInternalFormat; 37 } 38 else 39 { 40 implFormat = GL_NONE; 41 } 42 43 return gl::GetSizedInternalFormatInfo(implFormat); 44} 45} 46 47// FramebufferMtl implementation 48FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, bool flipY) 49 : FramebufferImpl(state), mFlipY(flipY) 50{ 51 reset(); 52} 53 54FramebufferMtl::~FramebufferMtl() {} 55 56void FramebufferMtl::reset() 57{ 58 for (auto &rt : mColorRenderTargets) 59 { 60 rt = nullptr; 61 } 62 mDepthRenderTarget = mStencilRenderTarget = nullptr; 63} 64 65void FramebufferMtl::destroy(const gl::Context *context) 66{ 67 reset(); 68} 69 70angle::Result FramebufferMtl::discard(const gl::Context *context, 71 size_t count, 72 const GLenum *attachments) 73{ 74 return invalidate(context, count, attachments); 75} 76 77angle::Result FramebufferMtl::invalidate(const gl::Context *context, 78 size_t count, 79 const GLenum *attachments) 80{ 81 return invalidateImpl(mtl::GetImpl(context), count, attachments); 82} 83 84angle::Result FramebufferMtl::invalidateSub(const gl::Context *context, 85 size_t count, 86 const GLenum *attachments, 87 const gl::Rectangle &area) 88{ 89 // NOTE(hqle): ES 3.0 feature. 90 UNIMPLEMENTED(); 91 return angle::Result::Stop; 92} 93 94angle::Result FramebufferMtl::clear(const gl::Context *context, GLbitfield mask) 95{ 96 ContextMtl *contextMtl = mtl::GetImpl(context); 97 98 mtl::ClearRectParams clearOpts; 99 100 bool clearColor = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT)); 101 bool clearDepth = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT)); 102 bool clearStencil = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_STENCIL_BUFFER_BIT)); 103 104 gl::DrawBufferMask clearColorBuffers; 105 if (clearColor) 106 { 107 clearColorBuffers = mState.getEnabledDrawBuffers(); 108 clearOpts.clearColor = contextMtl->getClearColorValue(); 109 } 110 if (clearDepth) 111 { 112 clearOpts.clearDepth = contextMtl->getClearDepthValue(); 113 } 114 if (clearStencil) 115 { 116 clearOpts.clearStencil = contextMtl->getClearStencilValue(); 117 } 118 119 return clearImpl(context, clearColorBuffers, &clearOpts); 120} 121 122angle::Result FramebufferMtl::clearBufferfv(const gl::Context *context, 123 GLenum buffer, 124 GLint drawbuffer, 125 const GLfloat *values) 126{ 127 // NOTE(hqle): ES 3.0 feature. 128 UNIMPLEMENTED(); 129 return angle::Result::Stop; 130} 131angle::Result FramebufferMtl::clearBufferuiv(const gl::Context *context, 132 GLenum buffer, 133 GLint drawbuffer, 134 const GLuint *values) 135{ 136 // NOTE(hqle): ES 3.0 feature. 137 UNIMPLEMENTED(); 138 return angle::Result::Stop; 139} 140angle::Result FramebufferMtl::clearBufferiv(const gl::Context *context, 141 GLenum buffer, 142 GLint drawbuffer, 143 const GLint *values) 144{ 145 // NOTE(hqle): ES 3.0 feature. 146 UNIMPLEMENTED(); 147 return angle::Result::Stop; 148} 149angle::Result FramebufferMtl::clearBufferfi(const gl::Context *context, 150 GLenum buffer, 151 GLint drawbuffer, 152 GLfloat depth, 153 GLint stencil) 154{ 155 // NOTE(hqle): ES 3.0 feature. 156 UNIMPLEMENTED(); 157 return angle::Result::Stop; 158} 159 160GLenum FramebufferMtl::getImplementationColorReadFormat(const gl::Context *context) const 161{ 162 return GetReadAttachmentInfo(context, getColorReadRenderTarget()).format; 163} 164 165GLenum FramebufferMtl::getImplementationColorReadType(const gl::Context *context) const 166{ 167 return GetReadAttachmentInfo(context, getColorReadRenderTarget()).type; 168} 169 170angle::Result FramebufferMtl::readPixels(const gl::Context *context, 171 const gl::Rectangle &area, 172 GLenum format, 173 GLenum type, 174 void *pixels) 175{ 176 // Clip read area to framebuffer. 177 const gl::Extents &fbSize = getState().getReadAttachment()->getSize(); 178 const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height); 179 180 gl::Rectangle clippedArea; 181 if (!ClipRectangle(area, fbRect, &clippedArea)) 182 { 183 // nothing to read 184 return angle::Result::Continue; 185 } 186 gl::Rectangle flippedArea = getReadPixelArea(clippedArea); 187 188 ContextMtl *contextMtl = mtl::GetImpl(context); 189 const gl::State &glState = context->getState(); 190 const gl::PixelPackState &packState = glState.getPackState(); 191 192 const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type); 193 194 GLuint outputPitch = 0; 195 ANGLE_CHECK_GL_MATH(contextMtl, 196 sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment, 197 packState.rowLength, &outputPitch)); 198 GLuint outputSkipBytes = 0; 199 ANGLE_CHECK_GL_MATH(contextMtl, sizedFormatInfo.computeSkipBytes( 200 type, outputPitch, 0, packState, false, &outputSkipBytes)); 201 202 outputSkipBytes += (clippedArea.x - area.x) * sizedFormatInfo.pixelBytes + 203 (clippedArea.y - area.y) * outputPitch; 204 205 const angle::Format &angleFormat = GetFormatFromFormatType(format, type); 206 207 PackPixelsParams params(flippedArea, angleFormat, outputPitch, packState.reverseRowOrder, 208 glState.getTargetBuffer(gl::BufferBinding::PixelPack), 0); 209 if (mFlipY) 210 { 211 params.reverseRowOrder = !params.reverseRowOrder; 212 } 213 214 ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(), 215 static_cast<uint8_t *>(pixels) + outputSkipBytes)); 216 217 return angle::Result::Continue; 218} 219 220angle::Result FramebufferMtl::blit(const gl::Context *context, 221 const gl::Rectangle &sourceArea, 222 const gl::Rectangle &destArea, 223 GLbitfield mask, 224 GLenum filter) 225{ 226 // NOTE(hqle): MSAA feature. 227 UNIMPLEMENTED(); 228 return angle::Result::Stop; 229} 230 231bool FramebufferMtl::checkStatus(const gl::Context *context) const 232{ 233 if (!mState.attachmentsHaveSameDimensions()) 234 { 235 return false; 236 } 237 238 ContextMtl *contextMtl = mtl::GetImpl(context); 239 if (!contextMtl->getDisplay()->getFeatures().allowSeparatedDepthStencilBuffers.enabled && 240 mState.hasSeparateDepthAndStencilAttachments()) 241 { 242 return false; 243 } 244 245 return true; 246} 247 248angle::Result FramebufferMtl::syncState(const gl::Context *context, 249 const gl::Framebuffer::DirtyBits &dirtyBits) 250{ 251 ContextMtl *contextMtl = mtl::GetImpl(context); 252 ASSERT(dirtyBits.any()); 253 for (size_t dirtyBit : dirtyBits) 254 { 255 switch (dirtyBit) 256 { 257 case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: 258 ANGLE_TRY(updateDepthRenderTarget(context)); 259 break; 260 case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: 261 ANGLE_TRY(updateStencilRenderTarget(context)); 262 break; 263 case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS: 264 case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS: 265 // NOTE(hqle): What are we supposed to do? 266 break; 267 case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS: 268 case gl::Framebuffer::DIRTY_BIT_READ_BUFFER: 269 case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH: 270 case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT: 271 case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES: 272 case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS: 273 break; 274 default: 275 { 276 static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits"); 277 if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) 278 { 279 size_t colorIndexGL = static_cast<size_t>( 280 dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); 281 ANGLE_TRY(updateColorRenderTarget(context, colorIndexGL)); 282 } 283 else 284 { 285 ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 && 286 dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX); 287 // NOTE: might need to notify context. 288 } 289 break; 290 } 291 } 292 } 293 294 auto oldRenderPassDesc = mRenderPassDesc; 295 296 ANGLE_TRY(prepareRenderPass(context, mState.getEnabledDrawBuffers(), &mRenderPassDesc)); 297 298 if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc)) 299 { 300 FramebufferMtl *currentDrawFramebuffer = 301 mtl::GetImpl(context->getState().getDrawFramebuffer()); 302 if (currentDrawFramebuffer == this) 303 { 304 contextMtl->onDrawFrameBufferChange(context, this); 305 } 306 } 307 308 return angle::Result::Continue; 309} 310 311angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context, 312 size_t index, 313 GLfloat *xy) const 314{ 315 UNIMPLEMENTED(); 316 return angle::Result::Stop; 317} 318 319RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget() const 320{ 321 if (mState.getReadIndex() >= mColorRenderTargets.size()) 322 { 323 return nullptr; 324 } 325 return mColorRenderTargets[mState.getReadIndex()]; 326} 327 328gl::Rectangle FramebufferMtl::getCompleteRenderArea() const 329{ 330 return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height); 331} 332 333const mtl::RenderPassDesc &FramebufferMtl::getRenderPassDesc(ContextMtl *context) 334{ 335 return mRenderPassDesc; 336} 337 338void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context) 339{ 340 // Compute loadOp based on previous storeOp and reset storeOp flags: 341 for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments) 342 { 343 if (colorAttachment.storeAction == MTLStoreActionDontCare) 344 { 345 // If we previously discarded attachment's content, then don't need to load it. 346 colorAttachment.loadAction = MTLLoadActionDontCare; 347 } 348 else 349 { 350 colorAttachment.loadAction = MTLLoadActionLoad; 351 } 352 colorAttachment.storeAction = MTLStoreActionStore; // Default action is store 353 } 354 // Depth load/store 355 if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionDontCare) 356 { 357 mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionDontCare; 358 } 359 else 360 { 361 mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad; 362 } 363 mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionStore; 364 365 // Stencil load/store 366 if (mRenderPassDesc.stencilAttachment.storeAction == MTLStoreActionDontCare) 367 { 368 mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionDontCare; 369 } 370 else 371 { 372 mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 373 } 374 mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionStore; 375} 376 377angle::Result FramebufferMtl::updateColorRenderTarget(const gl::Context *context, 378 size_t colorIndexGL) 379{ 380 ASSERT(colorIndexGL < mtl::kMaxRenderTargets); 381 // Reset load store action 382 mRenderPassDesc.colorAttachments[colorIndexGL].reset(); 383 return updateCachedRenderTarget(context, mState.getColorAttachment(colorIndexGL), 384 &mColorRenderTargets[colorIndexGL]); 385} 386 387angle::Result FramebufferMtl::updateDepthRenderTarget(const gl::Context *context) 388{ 389 // Reset load store action 390 mRenderPassDesc.depthAttachment.reset(); 391 return updateCachedRenderTarget(context, mState.getDepthAttachment(), &mDepthRenderTarget); 392} 393 394angle::Result FramebufferMtl::updateStencilRenderTarget(const gl::Context *context) 395{ 396 // Reset load store action 397 mRenderPassDesc.stencilAttachment.reset(); 398 return updateCachedRenderTarget(context, mState.getStencilAttachment(), &mStencilRenderTarget); 399} 400 401angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *context, 402 const gl::FramebufferAttachment *attachment, 403 RenderTargetMtl **cachedRenderTarget) 404{ 405 RenderTargetMtl *newRenderTarget = nullptr; 406 if (attachment) 407 { 408 ASSERT(attachment->isAttached()); 409 ANGLE_TRY(attachment->getRenderTarget(context, attachment->getRenderToTextureSamples(), 410 &newRenderTarget)); 411 } 412 *cachedRenderTarget = newRenderTarget; 413 return angle::Result::Continue; 414} 415 416angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context, 417 gl::DrawBufferMask drawColorBuffers, 418 mtl::RenderPassDesc *pDescOut) 419{ 420 auto &desc = *pDescOut; 421 422 desc.numColorAttachments = static_cast<uint32_t>(drawColorBuffers.count()); 423 size_t attachmentIdx = 0; 424 425 for (size_t colorIndexGL : drawColorBuffers) 426 { 427 if (colorIndexGL >= mtl::kMaxRenderTargets) 428 { 429 continue; 430 } 431 const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL]; 432 ASSERT(colorRenderTarget); 433 434 mtl::RenderPassColorAttachmentDesc &colorAttachment = 435 desc.colorAttachments[attachmentIdx++]; 436 colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment); 437 } 438 439 if (mDepthRenderTarget) 440 { 441 mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment); 442 } 443 444 if (mStencilRenderTarget) 445 { 446 mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment); 447 } 448 449 return angle::Result::Continue; 450} 451 452// Override clear color based on texture's write mask 453void FramebufferMtl::overrideClearColor(const mtl::TextureRef &texture, 454 MTLClearColor clearColor, 455 MTLClearColor *colorOut) 456{ 457 *colorOut = mtl::EmulatedAlphaClearColor(clearColor, texture->getColorWritableMask()); 458} 459 460angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context, 461 gl::DrawBufferMask clearColorBuffers, 462 const mtl::ClearRectParams &clearOpts) 463{ 464 ContextMtl *contextMtl = mtl::GetImpl(context); 465 bool startedRenderPass = contextMtl->hasStartedRenderPass(mRenderPassDesc); 466 mtl::RenderCommandEncoder *encoder = nullptr; 467 mtl::RenderPassDesc tempDesc = mRenderPassDesc; 468 469 if (startedRenderPass) 470 { 471 encoder = contextMtl->getRenderCommandEncoder(); 472 } 473 474 size_t attachmentCount = 0; 475 for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) 476 { 477 ASSERT(colorIndexGL < mtl::kMaxRenderTargets); 478 479 uint32_t attachmentIdx = static_cast<uint32_t>(attachmentCount++); 480 mtl::RenderPassColorAttachmentDesc &colorAttachment = 481 tempDesc.colorAttachments[attachmentIdx]; 482 const mtl::TextureRef &texture = colorAttachment.texture; 483 484 if (clearColorBuffers.test(colorIndexGL)) 485 { 486 if (startedRenderPass) 487 { 488 // Render pass already started, and we want to clear this buffer, 489 // then discard its content before clearing. 490 encoder->setColorStoreAction(MTLStoreActionDontCare, attachmentIdx); 491 } 492 colorAttachment.loadAction = MTLLoadActionClear; 493 overrideClearColor(texture, clearOpts.clearColor.value(), &colorAttachment.clearColor); 494 } 495 else if (startedRenderPass) 496 { 497 // If render pass already started and we don't want to clear this buffer, 498 // then store it with current render encoder and load it before clearing step 499 encoder->setColorStoreAction(MTLStoreActionStore, attachmentIdx); 500 colorAttachment.loadAction = MTLLoadActionLoad; 501 } 502 } 503 504 MTLStoreAction preClearDethpStoreAction = MTLStoreActionStore, 505 preClearStencilStoreAction = MTLStoreActionStore; 506 if (clearOpts.clearDepth.valid()) 507 { 508 preClearDethpStoreAction = MTLStoreActionDontCare; 509 tempDesc.depthAttachment.loadAction = MTLLoadActionClear; 510 tempDesc.depthAttachment.clearDepth = clearOpts.clearDepth.value(); 511 } 512 else if (startedRenderPass) 513 { 514 // If render pass already started and we don't want to clear this buffer, 515 // then store it with current render encoder and load it before clearing step 516 preClearDethpStoreAction = MTLStoreActionStore; 517 tempDesc.depthAttachment.loadAction = MTLLoadActionLoad; 518 } 519 520 if (clearOpts.clearStencil.valid()) 521 { 522 preClearStencilStoreAction = MTLStoreActionDontCare; 523 tempDesc.stencilAttachment.loadAction = MTLLoadActionClear; 524 tempDesc.stencilAttachment.clearStencil = clearOpts.clearStencil.value(); 525 } 526 else if (startedRenderPass) 527 { 528 // If render pass already started and we don't want to clear this buffer, 529 // then store it with current render encoder and load it before clearing step 530 preClearStencilStoreAction = MTLStoreActionStore; 531 tempDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 532 } 533 534 // End current render encoder. 535 if (startedRenderPass) 536 { 537 encoder->setDepthStencilStoreAction(preClearDethpStoreAction, preClearStencilStoreAction); 538 contextMtl->endEncoding(encoder); 539 } 540 541 // Start new render encoder with loadOp=Clear 542 contextMtl->getRenderCommandEncoder(tempDesc); 543 544 return angle::Result::Continue; 545} 546 547angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context, 548 gl::DrawBufferMask clearColorBuffers, 549 const mtl::ClearRectParams &clearOpts) 550{ 551 ContextMtl *contextMtl = mtl::GetImpl(context); 552 DisplayMtl *display = contextMtl->getDisplay(); 553 554 // Start new render encoder if not already. 555 mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(mRenderPassDesc); 556 557 display->getUtils().clearWithDraw(context, encoder, clearOpts); 558 559 return angle::Result::Continue; 560} 561 562angle::Result FramebufferMtl::clearImpl(const gl::Context *context, 563 gl::DrawBufferMask clearColorBuffers, 564 mtl::ClearRectParams *pClearOpts) 565{ 566 auto &clearOpts = *pClearOpts; 567 568 if (!clearOpts.clearColor.valid() && !clearOpts.clearDepth.valid() && 569 !clearOpts.clearStencil.valid()) 570 { 571 // No Op. 572 return angle::Result::Continue; 573 } 574 575 ContextMtl *contextMtl = mtl::GetImpl(context); 576 const gl::Rectangle renderArea(0, 0, mState.getDimensions().width, 577 mState.getDimensions().height); 578 579 clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false); 580 clearOpts.flipY = mFlipY; 581 582 // Discard clear altogether if scissor has 0 width or height. 583 if (clearOpts.clearArea.width == 0 || clearOpts.clearArea.height == 0) 584 { 585 return angle::Result::Continue; 586 } 587 588 MTLColorWriteMask colorMask = contextMtl->getColorMask(); 589 uint32_t stencilMask = contextMtl->getStencilMask(); 590 if (!contextMtl->getDepthMask()) 591 { 592 // Disable depth clearing, since depth write is disable 593 clearOpts.clearDepth.reset(); 594 } 595 596 if (clearOpts.clearArea == renderArea && 597 (!clearOpts.clearColor.valid() || colorMask == MTLColorWriteMaskAll) && 598 (!clearOpts.clearStencil.valid() || 599 (stencilMask & mtl::kStencilMaskAll) == mtl::kStencilMaskAll)) 600 { 601 return clearWithLoadOp(context, clearColorBuffers, clearOpts); 602 } 603 604 return clearWithDraw(context, clearColorBuffers, clearOpts); 605} 606 607angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl, 608 size_t count, 609 const GLenum *attachments) 610{ 611 gl::DrawBufferMask invalidateColorBuffers; 612 bool invalidateDepthBuffer = false; 613 bool invalidateStencilBuffer = false; 614 615 for (size_t i = 0; i < count; ++i) 616 { 617 const GLenum attachment = attachments[i]; 618 619 switch (attachment) 620 { 621 case GL_DEPTH: 622 case GL_DEPTH_ATTACHMENT: 623 invalidateDepthBuffer = true; 624 break; 625 case GL_STENCIL: 626 case GL_STENCIL_ATTACHMENT: 627 invalidateStencilBuffer = true; 628 break; 629 case GL_DEPTH_STENCIL_ATTACHMENT: 630 invalidateDepthBuffer = true; 631 invalidateStencilBuffer = true; 632 break; 633 default: 634 ASSERT( 635 (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) || 636 (attachment == GL_COLOR)); 637 638 invalidateColorBuffers.set( 639 attachment == GL_COLOR ? 0u : (attachment - GL_COLOR_ATTACHMENT0)); 640 } 641 } 642 643 // Set the appropriate storeOp for attachments. 644 // If we already start the render pass, then need to set the store action now. 645 bool renderPassStarted = contextMtl->hasStartedRenderPass(mRenderPassDesc); 646 mtl::RenderCommandEncoder *encoder = 647 renderPassStarted ? contextMtl->getRenderCommandEncoder() : nullptr; 648 649 for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i) 650 { 651 if (invalidateColorBuffers.test(i)) 652 { 653 mtl::RenderPassColorAttachmentDesc &colorAttachment = 654 mRenderPassDesc.colorAttachments[i]; 655 colorAttachment.storeAction = MTLStoreActionDontCare; 656 if (renderPassStarted) 657 { 658 encoder->setColorStoreAction(MTLStoreActionDontCare, i); 659 } 660 } 661 } 662 663 if (invalidateDepthBuffer && mDepthRenderTarget) 664 { 665 mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare; 666 if (renderPassStarted) 667 { 668 encoder->setDepthStoreAction(MTLStoreActionDontCare); 669 } 670 } 671 672 if (invalidateStencilBuffer && mStencilRenderTarget) 673 { 674 mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare; 675 if (renderPassStarted) 676 { 677 encoder->setStencilStoreAction(MTLStoreActionDontCare); 678 } 679 } 680 681 return angle::Result::Continue; 682} 683 684gl::Rectangle FramebufferMtl::getReadPixelArea(const gl::Rectangle &glArea) 685{ 686 RenderTargetMtl *colorReadRT = getColorReadRenderTarget(); 687 ASSERT(colorReadRT); 688 gl::Rectangle flippedArea = glArea; 689 if (mFlipY) 690 { 691 flippedArea.y = 692 colorReadRT->getTexture()->height(static_cast<uint32_t>(colorReadRT->getLevelIndex())) - 693 flippedArea.y - flippedArea.height; 694 } 695 696 return flippedArea; 697} 698 699angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, 700 const gl::Rectangle &area, 701 const PackPixelsParams &packPixelsParams, 702 RenderTargetMtl *renderTarget, 703 uint8_t *pixels) 704{ 705 ContextMtl *contextMtl = mtl::GetImpl(context); 706 if (packPixelsParams.packBuffer) 707 { 708 // NOTE(hqle): PBO is not supported atm 709 ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION); 710 } 711 if (!renderTarget) 712 { 713 return angle::Result::Continue; 714 } 715 const mtl::TextureRef &texture = renderTarget->getTexture(); 716 717 if (!texture) 718 { 719 return angle::Result::Continue; 720 } 721 722 const mtl::Format &readFormat = *renderTarget->getFormat(); 723 const angle::Format &readAngleFormat = readFormat.actualAngleFormat(); 724 725 // NOTE(hqle): resolve MSAA texture before readback 726 int srcRowPitch = area.width * readAngleFormat.pixelBytes; 727 angle::MemoryBuffer readPixelRowBuffer; 728 ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(srcRowPitch)); 729 730 auto packPixelsRowParams = packPixelsParams; 731 MTLRegion mtlSrcRowRegion = MTLRegionMake2D(area.x, area.y, area.width, 1); 732 733 int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1; 734 int startRow = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y; 735 736 // Copy pixels row by row 737 packPixelsRowParams.area.height = 1; 738 packPixelsRowParams.reverseRowOrder = false; 739 for (int r = startRow, i = 0; i < area.height; 740 ++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch) 741 { 742 mtlSrcRowRegion.origin.y = r; 743 packPixelsRowParams.area.y = packPixelsParams.area.y + i; 744 745 // Read the pixels data to the row buffer 746 texture->getBytes(contextMtl, srcRowPitch, mtlSrcRowRegion, 747 static_cast<uint32_t>(renderTarget->getLevelIndex()), 748 readPixelRowBuffer.data()); 749 750 // Convert to destination format 751 PackPixels(packPixelsRowParams, readAngleFormat, srcRowPitch, readPixelRowBuffer.data(), 752 pixels); 753 } 754 755 return angle::Result::Continue; 756} 757} 758