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