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/angletypes.h" 11#include "libANGLE/renderer/metal/ContextMtl.h" 12 13#include <TargetConditionals.h> 14 15#include "common/MemoryBuffer.h" 16#include "common/angleutils.h" 17#include "common/debug.h" 18#include "libANGLE/ErrorStrings.h" 19#include "libANGLE/renderer/metal/BufferMtl.h" 20#include "libANGLE/renderer/metal/DisplayMtl.h" 21#include "libANGLE/renderer/metal/FrameBufferMtl.h" 22#include "libANGLE/renderer/metal/SurfaceMtl.h" 23#include "libANGLE/renderer/metal/mtl_utils.h" 24#include "libANGLE/renderer/renderer_utils.h" 25 26namespace rx 27{ 28namespace 29{ 30// Override clear color based on texture's write mask 31void OverrideMTLClearColor(const mtl::TextureRef &texture, 32 const mtl::ClearColorValue &clearColor, 33 MTLClearColor *colorOut) 34{ 35 *colorOut = 36 mtl::EmulatedAlphaClearColor(clearColor.toMTLClearColor(), texture->getColorWritableMask()); 37} 38 39const gl::InternalFormat &GetReadAttachmentInfo(const gl::Context *context, 40 RenderTargetMtl *renderTarget) 41{ 42 GLenum implFormat; 43 44 if (renderTarget) 45 { 46 implFormat = renderTarget->getFormat().actualAngleFormat().fboImplementationInternalFormat; 47 } 48 else 49 { 50 implFormat = GL_NONE; 51 } 52 53 return gl::GetSizedInternalFormatInfo(implFormat); 54} 55 56angle::Result CopyTextureSliceLevelToTempBuffer(const gl::Context *context, 57 const mtl::TextureRef &srcTexture, 58 const mtl::MipmapNativeLevel &mipNativeLevel, 59 uint32_t layerIndex, 60 mtl::BufferRef *outBuffer) 61{ 62 ASSERT(outBuffer); 63 64 ContextMtl *contextMtl = mtl::GetImpl(context); 65 auto formatId = mtl::Format::MetalToAngleFormatID(srcTexture->pixelFormat()); 66 const mtl::Format &metalFormat = contextMtl->getPixelFormat(formatId); 67 const angle::Format &angleFormat = metalFormat.actualAngleFormat(); 68 69 uint32_t width = srcTexture->width(mipNativeLevel); 70 uint32_t height = srcTexture->height(mipNativeLevel); 71 uint32_t sizeInBytes = width * height * angleFormat.pixelBytes; 72 73 mtl::BufferRef tempBuffer; 74 ANGLE_TRY(mtl::Buffer::MakeBufferWithStorageMode( 75 contextMtl, mtl::Buffer::getStorageModeForSharedBuffer(contextMtl), sizeInBytes, nullptr, 76 &tempBuffer)); 77 78 gl::Rectangle region(0, 0, width, height); 79 uint32_t bytesPerRow = angleFormat.pixelBytes * width; 80 uint32_t destOffset = 0; 81 ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer(context, srcTexture, bytesPerRow, region, 82 mipNativeLevel, layerIndex, destOffset, 83 tempBuffer)); 84 85 *outBuffer = tempBuffer; 86 return angle::Result::Continue; 87} 88 89angle::Result Copy2DTextureSlice0Level0ToTempTexture(const gl::Context *context, 90 const mtl::TextureRef &srcTexture, 91 mtl::TextureRef *outTexture) 92{ 93 ASSERT(outTexture); 94 95 ContextMtl *contextMtl = mtl::GetImpl(context); 96 auto formatId = mtl::Format::MetalToAngleFormatID(srcTexture->pixelFormat()); 97 const auto &format = contextMtl->getPixelFormat(formatId); 98 99 mtl::TextureRef tempTexture; 100 ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, format, srcTexture->widthAt0(), 101 srcTexture->heightAt0(), srcTexture->mipmapLevels(), 102 false, true, &tempTexture)); 103 104 auto *blitEncoder = contextMtl->getBlitCommandEncoder(); 105 blitEncoder->copyTexture(srcTexture, 106 0, // srcStartSlice 107 mtl::MipmapNativeLevel(0), // MipmapNativeLevel 108 tempTexture, // dst 109 0, // dstStartSlice 110 mtl::MipmapNativeLevel(0), // dstStartLevel 111 1, // sliceCount, 112 1); // levelCount 113 114 *outTexture = tempTexture; 115 return angle::Result::Continue; 116} 117 118} // namespace 119 120// FramebufferMtl implementation 121FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, ContextMtl *context, bool flipY) 122 : FramebufferImpl(state), 123 mColorRenderTargets(context->getNativeCaps().maxColorAttachments, nullptr), 124 mBackbuffer(nullptr), 125 mFlipY(flipY) 126{ 127 reset(); 128} 129 130FramebufferMtl::~FramebufferMtl() {} 131 132void FramebufferMtl::reset() 133{ 134 for (auto &rt : mColorRenderTargets) 135 { 136 rt = nullptr; 137 } 138 mDepthRenderTarget = mStencilRenderTarget = nullptr; 139 140 mRenderPassFirstColorAttachmentFormat = nullptr; 141 142 mReadPixelBuffer = nullptr; 143} 144 145void FramebufferMtl::destroy(const gl::Context *context) 146{ 147 reset(); 148} 149 150angle::Result FramebufferMtl::discard(const gl::Context *context, 151 size_t count, 152 const GLenum *attachments) 153{ 154 return invalidate(context, count, attachments); 155} 156 157angle::Result FramebufferMtl::invalidate(const gl::Context *context, 158 size_t count, 159 const GLenum *attachments) 160{ 161 return invalidateImpl(context, count, attachments); 162} 163 164angle::Result FramebufferMtl::invalidateSub(const gl::Context *context, 165 size_t count, 166 const GLenum *attachments, 167 const gl::Rectangle &area) 168{ 169 if (area.encloses(getCompleteRenderArea())) 170 { 171 return invalidateImpl(context, count, attachments); 172 } 173 return angle::Result::Continue; 174} 175 176angle::Result FramebufferMtl::clear(const gl::Context *context, GLbitfield mask) 177{ 178 ContextMtl *contextMtl = mtl::GetImpl(context); 179 180 if (ANGLE_UNLIKELY(contextMtl->getForceResyncDrawFramebuffer())) 181 { 182 ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(), 183 gl::Command::Clear)); 184 } 185 186 mtl::ClearRectParams clearOpts; 187 188 bool clearColor = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT)); 189 bool clearDepth = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT)); 190 bool clearStencil = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_STENCIL_BUFFER_BIT)); 191 192 gl::DrawBufferMask clearColorBuffers; 193 if (clearColor) 194 { 195 clearColorBuffers = mState.getEnabledDrawBuffers(); 196 clearOpts.clearColor = contextMtl->getClearColorValue(); 197 } 198 if (clearDepth) 199 { 200 clearOpts.clearDepth = contextMtl->getClearDepthValue(); 201 } 202 if (clearStencil) 203 { 204 clearOpts.clearStencil = contextMtl->getClearStencilValue(); 205 } 206 207 return clearImpl(context, clearColorBuffers, &clearOpts); 208} 209 210angle::Result FramebufferMtl::clearBufferfv(const gl::Context *context, 211 GLenum buffer, 212 GLint drawbuffer, 213 const GLfloat *values) 214{ 215 if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer())) 216 { 217 ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(), 218 gl::Command::Clear)); 219 } 220 221 mtl::ClearRectParams clearOpts; 222 223 gl::DrawBufferMask clearColorBuffers; 224 if (buffer == GL_DEPTH) 225 { 226 clearOpts.clearDepth = values[0]; 227 } 228 else 229 { 230 clearColorBuffers.set(drawbuffer); 231 clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]); 232 } 233 234 return clearImpl(context, clearColorBuffers, &clearOpts); 235} 236angle::Result FramebufferMtl::clearBufferuiv(const gl::Context *context, 237 GLenum buffer, 238 GLint drawbuffer, 239 const GLuint *values) 240{ 241 if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer())) 242 { 243 ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(), 244 gl::Command::Clear)); 245 } 246 247 gl::DrawBufferMask clearColorBuffers; 248 clearColorBuffers.set(drawbuffer); 249 250 mtl::ClearRectParams clearOpts; 251 clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]); 252 253 return clearImpl(context, clearColorBuffers, &clearOpts); 254} 255angle::Result FramebufferMtl::clearBufferiv(const gl::Context *context, 256 GLenum buffer, 257 GLint drawbuffer, 258 const GLint *values) 259{ 260 if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer())) 261 { 262 ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(), 263 gl::Command::Clear)); 264 } 265 266 mtl::ClearRectParams clearOpts; 267 268 gl::DrawBufferMask clearColorBuffers; 269 if (buffer == GL_STENCIL) 270 { 271 clearOpts.clearStencil = values[0] & mtl::kStencilMaskAll; 272 } 273 else 274 { 275 clearColorBuffers.set(drawbuffer); 276 clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]); 277 } 278 279 return clearImpl(context, clearColorBuffers, &clearOpts); 280} 281angle::Result FramebufferMtl::clearBufferfi(const gl::Context *context, 282 GLenum buffer, 283 GLint drawbuffer, 284 GLfloat depth, 285 GLint stencil) 286{ 287 mtl::ClearRectParams clearOpts; 288 clearOpts.clearDepth = depth; 289 clearOpts.clearStencil = stencil & mtl::kStencilMaskAll; 290 291 return clearImpl(context, gl::DrawBufferMask(), &clearOpts); 292} 293 294const gl::InternalFormat &FramebufferMtl::getImplementationColorReadFormat( 295 const gl::Context *context) const 296{ 297 return GetReadAttachmentInfo(context, getColorReadRenderTargetNoCache(context)); 298} 299 300angle::Result FramebufferMtl::readPixels(const gl::Context *context, 301 const gl::Rectangle &area, 302 GLenum format, 303 GLenum type, 304 const gl::PixelPackState &pack, 305 gl::Buffer *packBuffer, 306 void *pixels) 307{ 308 // Clip read area to framebuffer. 309 const gl::Extents &fbSize = getState().getReadAttachment()->getSize(); 310 const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height); 311 312 gl::Rectangle clippedArea; 313 if (!ClipRectangle(area, fbRect, &clippedArea)) 314 { 315 // nothing to read 316 return angle::Result::Continue; 317 } 318 gl::Rectangle flippedArea = getCorrectFlippedReadArea(context, clippedArea); 319 320 ContextMtl *contextMtl = mtl::GetImpl(context); 321 322 const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type); 323 324 GLuint outputPitch = 0; 325 ANGLE_CHECK_GL_MATH(contextMtl, 326 sizedFormatInfo.computeRowPitch(type, area.width, pack.alignment, 327 pack.rowLength, &outputPitch)); 328 GLuint outputSkipBytes = 0; 329 ANGLE_CHECK_GL_MATH(contextMtl, sizedFormatInfo.computeSkipBytes(type, outputPitch, 0, pack, 330 false, &outputSkipBytes)); 331 332 outputSkipBytes += (clippedArea.x - area.x) * sizedFormatInfo.pixelBytes + 333 (clippedArea.y - area.y) * outputPitch; 334 335 const angle::Format &angleFormat = GetFormatFromFormatType(format, type); 336 337 PackPixelsParams params(flippedArea, angleFormat, outputPitch, pack.reverseRowOrder, packBuffer, 338 0); 339 340 if (params.packBuffer) 341 { 342 // If PBO is active, pixels is treated as offset. 343 params.offset = reinterpret_cast<ptrdiff_t>(pixels) + outputSkipBytes; 344 } 345 346 if (mFlipY) 347 { 348 params.reverseRowOrder = !params.reverseRowOrder; 349 } 350 351 ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(context), 352 static_cast<uint8_t *>(pixels) + outputSkipBytes)); 353 354 return angle::Result::Continue; 355} 356 357namespace 358{ 359 360using FloatRectangle = gl::RectangleImpl<float>; 361 362float clamp0Max(float v, float max) 363{ 364 return std::max(0.0f, std::min(max, v)); 365} 366 367void ClampToBoundsAndAdjustCorrespondingValue(float a, 368 float originalASize, 369 float maxSize, 370 float b, 371 float originalBSize, 372 float *newA, 373 float *newB) 374{ 375 float clippedA = clamp0Max(a, maxSize); 376 float delta = clippedA - a; 377 *newA = clippedA; 378 *newB = b + delta * originalBSize / originalASize; 379} 380 381void ClipRectToBoundsAndAdjustCorrespondingRect(const FloatRectangle &a, 382 const gl::Rectangle &originalA, 383 const gl::Rectangle &clipDimensions, 384 const FloatRectangle &b, 385 const gl::Rectangle &originalB, 386 FloatRectangle *newA, 387 FloatRectangle *newB) 388{ 389 float newAValues[4]; 390 float newBValues[4]; 391 ClampToBoundsAndAdjustCorrespondingValue(a.x0(), originalA.width, clipDimensions.width, b.x0(), 392 originalB.width, &newAValues[0], &newBValues[0]); 393 ClampToBoundsAndAdjustCorrespondingValue(a.y0(), originalA.height, clipDimensions.height, 394 b.y0(), originalB.height, &newAValues[1], 395 &newBValues[1]); 396 ClampToBoundsAndAdjustCorrespondingValue(a.x1(), originalA.width, clipDimensions.width, b.x1(), 397 originalB.width, &newAValues[2], &newBValues[2]); 398 ClampToBoundsAndAdjustCorrespondingValue(a.y1(), originalA.height, clipDimensions.height, 399 b.y1(), originalB.height, &newAValues[3], 400 &newBValues[3]); 401 402 *newA = FloatRectangle(newAValues); 403 *newB = FloatRectangle(newBValues); 404} 405 406void ClipRectsToBoundsAndAdjustCorrespondingRect(const FloatRectangle &a, 407 const gl::Rectangle &originalA, 408 const gl::Rectangle &aClipDimensions, 409 const FloatRectangle &b, 410 const gl::Rectangle &originalB, 411 const gl::Rectangle &bClipDimensions, 412 FloatRectangle *newA, 413 FloatRectangle *newB) 414{ 415 FloatRectangle tempA; 416 FloatRectangle tempB; 417 ClipRectToBoundsAndAdjustCorrespondingRect(a, originalA, aClipDimensions, b, originalB, &tempA, 418 &tempB); 419 ClipRectToBoundsAndAdjustCorrespondingRect(tempB, originalB, bClipDimensions, tempA, originalA, 420 newB, newA); 421} 422 423void RoundValueAndAdjustCorrespondingValue(float a, 424 float originalASize, 425 float b, 426 float originalBSize, 427 int *newA, 428 float *newB) 429{ 430 float roundedA = std::round(a); 431 float delta = roundedA - a; 432 *newA = static_cast<int>(roundedA); 433 *newB = b + delta * originalBSize / originalASize; 434} 435 436gl::Rectangle RoundRectToPixelsAndAdjustCorrespondingRectToMatch(const FloatRectangle &a, 437 const gl::Rectangle &originalA, 438 const FloatRectangle &b, 439 const gl::Rectangle &originalB, 440 FloatRectangle *newB) 441{ 442 int newAValues[4]; 443 float newBValues[4]; 444 RoundValueAndAdjustCorrespondingValue(a.x0(), originalA.width, b.x0(), originalB.width, 445 &newAValues[0], &newBValues[0]); 446 RoundValueAndAdjustCorrespondingValue(a.y0(), originalA.height, b.y0(), originalB.height, 447 &newAValues[1], &newBValues[1]); 448 RoundValueAndAdjustCorrespondingValue(a.x1(), originalA.width, b.x1(), originalB.width, 449 &newAValues[2], &newBValues[2]); 450 RoundValueAndAdjustCorrespondingValue(a.y1(), originalA.height, b.y1(), originalB.height, 451 &newAValues[3], &newBValues[3]); 452 453 *newB = FloatRectangle(newBValues); 454 return gl::Rectangle(newAValues[0], newAValues[1], newAValues[2] - newAValues[0], 455 newAValues[3] - newAValues[1]); 456} 457 458} // namespace 459 460angle::Result FramebufferMtl::blit(const gl::Context *context, 461 const gl::Rectangle &sourceAreaIn, 462 const gl::Rectangle &destAreaIn, 463 GLbitfield mask, 464 GLenum filter) 465{ 466 bool blitColorBuffer = (mask & GL_COLOR_BUFFER_BIT) != 0; 467 bool blitDepthBuffer = (mask & GL_DEPTH_BUFFER_BIT) != 0; 468 bool blitStencilBuffer = (mask & GL_STENCIL_BUFFER_BIT) != 0; 469 470 const gl::State &glState = context->getState(); 471 const gl::Framebuffer *glSrcFramebuffer = glState.getReadFramebuffer(); 472 473 FramebufferMtl *srcFrameBuffer = mtl::GetImpl(glSrcFramebuffer); 474 475 blitColorBuffer = 476 blitColorBuffer && srcFrameBuffer->getColorReadRenderTarget(context) != nullptr; 477 blitDepthBuffer = blitDepthBuffer && srcFrameBuffer->getDepthRenderTarget() != nullptr; 478 blitStencilBuffer = blitStencilBuffer && srcFrameBuffer->getStencilRenderTarget() != nullptr; 479 480 if (!blitColorBuffer && !blitDepthBuffer && !blitStencilBuffer) 481 { 482 // No-op 483 return angle::Result::Continue; 484 } 485 486 if (ANGLE_UNLIKELY(mtl::GetImpl(context)->getForceResyncDrawFramebuffer())) 487 { 488 ANGLE_TRY(syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(), 489 gl::Command::Blit)); 490 } 491 492 const gl::Rectangle srcFramebufferDimensions = srcFrameBuffer->getCompleteRenderArea(); 493 const gl::Rectangle dstFramebufferDimensions = this->getCompleteRenderArea(); 494 495 FloatRectangle srcRect(sourceAreaIn); 496 FloatRectangle dstRect(destAreaIn); 497 498 FloatRectangle clippedSrcRect; 499 FloatRectangle clippedDstRect; 500 ClipRectsToBoundsAndAdjustCorrespondingRect(srcRect, sourceAreaIn, srcFramebufferDimensions, 501 dstRect, destAreaIn, dstFramebufferDimensions, 502 &clippedSrcRect, &clippedDstRect); 503 504 FloatRectangle adjustedSrcRect; 505 gl::Rectangle srcClippedDestArea = RoundRectToPixelsAndAdjustCorrespondingRectToMatch( 506 clippedDstRect, destAreaIn, clippedSrcRect, sourceAreaIn, &adjustedSrcRect); 507 508 if (srcFrameBuffer->flipY()) 509 { 510 adjustedSrcRect.y = 511 srcFramebufferDimensions.height - adjustedSrcRect.y - adjustedSrcRect.height; 512 adjustedSrcRect = adjustedSrcRect.flip(false, true); 513 } 514 515 // If the destination is flipped in either direction, we will flip the source instead so that 516 // the destination area is always unflipped. 517 adjustedSrcRect = 518 adjustedSrcRect.flip(srcClippedDestArea.isReversedX(), srcClippedDestArea.isReversedY()); 519 srcClippedDestArea = srcClippedDestArea.removeReversal(); 520 521 // Clip the destination area to the framebuffer size and scissor. 522 gl::Rectangle scissoredDestArea; 523 if (!gl::ClipRectangle(ClipRectToScissor(glState, dstFramebufferDimensions, false), 524 srcClippedDestArea, &scissoredDestArea)) 525 { 526 return angle::Result::Continue; 527 } 528 529 // Use blit with draw 530 mtl::BlitParams baseParams; 531 baseParams.dstTextureSize = 532 gl::Extents(dstFramebufferDimensions.width, dstFramebufferDimensions.height, 1); 533 baseParams.dstRect = srcClippedDestArea; 534 baseParams.dstScissorRect = scissoredDestArea; 535 baseParams.dstFlipY = this->flipY(); 536 537 baseParams.srcNormalizedCoords = 538 mtl::NormalizedCoords(adjustedSrcRect.x, adjustedSrcRect.y, adjustedSrcRect.width, 539 adjustedSrcRect.height, srcFramebufferDimensions); 540 // This flag is for auto flipping the rect inside RenderUtils. Since we already flip it using 541 // getCorrectFlippedReadArea(). This flag is not needed. 542 baseParams.srcYFlipped = false; 543 baseParams.unpackFlipX = false; 544 baseParams.unpackFlipY = false; 545 546 return blitWithDraw(context, srcFrameBuffer, blitColorBuffer, blitDepthBuffer, 547 blitStencilBuffer, filter, baseParams); 548} 549 550angle::Result FramebufferMtl::blitWithDraw(const gl::Context *context, 551 FramebufferMtl *srcFrameBuffer, 552 bool blitColorBuffer, 553 bool blitDepthBuffer, 554 bool blitStencilBuffer, 555 GLenum filter, 556 const mtl::BlitParams &baseParams) 557{ 558 ContextMtl *contextMtl = mtl::GetImpl(context); 559 // Use blit with draw 560 mtl::RenderCommandEncoder *renderEncoder = nullptr; 561 562 // Blit Depth & stencil 563 if (blitDepthBuffer || blitStencilBuffer) 564 { 565 mtl::DepthStencilBlitParams dsBlitParams; 566 memcpy(&dsBlitParams, &baseParams, sizeof(baseParams)); 567 RenderTargetMtl *srcDepthRt = srcFrameBuffer->getDepthRenderTarget(); 568 RenderTargetMtl *srcStencilRt = srcFrameBuffer->getStencilRenderTarget(); 569 570 if (blitDepthBuffer) 571 { 572 dsBlitParams.src = srcDepthRt->getTexture(); 573 dsBlitParams.srcLevel = srcDepthRt->getLevelIndex(); 574 dsBlitParams.srcLayer = srcDepthRt->getLayerIndex(); 575 } 576 577 if (blitStencilBuffer && srcStencilRt->getTexture()) 578 { 579 dsBlitParams.srcStencil = srcStencilRt->getTexture()->getStencilView(); 580 dsBlitParams.srcLevel = srcStencilRt->getLevelIndex(); 581 dsBlitParams.srcLayer = srcStencilRt->getLayerIndex(); 582 583 if (!contextMtl->getDisplay()->getFeatures().hasShaderStencilOutput.enabled && 584 mStencilRenderTarget) 585 { 586 // Directly writing to stencil in shader is not supported, use temporary copy buffer 587 // work around. This is a compute pass. 588 mtl::StencilBlitViaBufferParams stencilOnlyBlitParams = dsBlitParams; 589 stencilOnlyBlitParams.dstStencil = mStencilRenderTarget->getTexture(); 590 stencilOnlyBlitParams.dstStencilLayer = mStencilRenderTarget->getLayerIndex(); 591 stencilOnlyBlitParams.dstStencilLevel = mStencilRenderTarget->getLevelIndex(); 592 stencilOnlyBlitParams.dstPackedDepthStencilFormat = 593 mStencilRenderTarget->getFormat().hasDepthAndStencilBits(); 594 595 ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitStencilViaCopyBuffer( 596 context, stencilOnlyBlitParams)); 597 598 // Prevent the stencil to be blitted with draw again 599 dsBlitParams.srcStencil = nullptr; 600 } 601 } 602 603 // The actual blitting of depth and/or stencil 604 renderEncoder = ensureRenderPassStarted(context); 605 ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitDepthStencilWithDraw( 606 context, renderEncoder, dsBlitParams)); 607 } // if (blitDepthBuffer || blitStencilBuffer) 608 else 609 { 610 renderEncoder = ensureRenderPassStarted(context); 611 } 612 613 // Blit color 614 if (blitColorBuffer) 615 { 616 mtl::ColorBlitParams colorBlitParams; 617 memcpy(&colorBlitParams, &baseParams, sizeof(baseParams)); 618 619 RenderTargetMtl *srcColorRt = srcFrameBuffer->getColorReadRenderTarget(context); 620 ASSERT(srcColorRt); 621 622 colorBlitParams.src = srcColorRt->getTexture(); 623 colorBlitParams.srcLevel = srcColorRt->getLevelIndex(); 624 colorBlitParams.srcLayer = srcColorRt->getLayerIndex(); 625 626 colorBlitParams.enabledBuffers = getState().getEnabledDrawBuffers(); 627 colorBlitParams.filter = filter; 628 colorBlitParams.dstLuminance = srcColorRt->getFormat().actualAngleFormat().isLUMA(); 629 630 ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw( 631 context, renderEncoder, srcColorRt->getFormat().actualAngleFormat(), colorBlitParams)); 632 } 633 634 return angle::Result::Continue; 635} 636 637bool FramebufferMtl::totalBitsUsedIsLessThanOrEqualToMaxBitsSupported( 638 const gl::Context *context) const 639{ 640 ContextMtl *contextMtl = mtl::GetImpl(context); 641 642 uint32_t bitsUsed = 0; 643 for (const gl::FramebufferAttachment &attachment : mState.getColorAttachments()) 644 { 645 if (attachment.isAttached()) 646 { 647 bitsUsed += attachment.getRedSize() + attachment.getGreenSize() + 648 attachment.getBlueSize() + attachment.getAlphaSize(); 649 } 650 } 651 652 return bitsUsed <= contextMtl->getDisplay()->getMaxColorTargetBits(); 653} 654 655gl::FramebufferStatus FramebufferMtl::checkStatus(const gl::Context *context) const 656{ 657 ContextMtl *contextMtl = mtl::GetImpl(context); 658 if (!contextMtl->getDisplay()->getFeatures().allowSeparateDepthStencilBuffers.enabled && 659 mState.hasSeparateDepthAndStencilAttachments()) 660 { 661 return gl::FramebufferStatus::Incomplete( 662 GL_FRAMEBUFFER_UNSUPPORTED, 663 gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers); 664 } 665 666 if (mState.getDepthAttachment() && mState.getDepthAttachment()->getFormat().info->depthBits && 667 mState.getDepthAttachment()->getFormat().info->stencilBits) 668 { 669 return checkPackedDepthStencilAttachment(); 670 } 671 672 if (mState.getStencilAttachment() && 673 mState.getStencilAttachment()->getFormat().info->depthBits && 674 mState.getStencilAttachment()->getFormat().info->stencilBits) 675 { 676 return checkPackedDepthStencilAttachment(); 677 } 678 679 if (!totalBitsUsedIsLessThanOrEqualToMaxBitsSupported(context)) 680 { 681 return gl::FramebufferStatus::Incomplete( 682 GL_FRAMEBUFFER_UNSUPPORTED, 683 gl::err::kFramebufferIncompleteColorBitsUsedExceedsMaxColorBitsSupported); 684 } 685 686 return gl::FramebufferStatus::Complete(); 687} 688 689gl::FramebufferStatus FramebufferMtl::checkPackedDepthStencilAttachment() const 690{ 691 if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.1, 12.0)) 692 { 693 // If depth/stencil attachment has depth & stencil bits, then depth & stencil must not have 694 // separate attachment. i.e. They must be the same texture or one of them has no 695 // attachment. 696 if (mState.hasSeparateDepthAndStencilAttachments()) 697 { 698 WARN() << "Packed depth stencil texture/buffer must not be mixed with other " 699 "texture/buffer."; 700 return gl::FramebufferStatus::Incomplete( 701 GL_FRAMEBUFFER_UNSUPPORTED, 702 gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers); 703 } 704 } 705 else 706 { 707 // Metal 2.0 and below doesn't allow packed depth stencil texture to be attached only as 708 // depth or stencil buffer. i.e. None of the depth & stencil attachment can be null. 709 if (!mState.getDepthStencilAttachment()) 710 { 711 WARN() << "Packed depth stencil texture/buffer must be bound to both depth & stencil " 712 "attachment point."; 713 return gl::FramebufferStatus::Incomplete( 714 GL_FRAMEBUFFER_UNSUPPORTED, 715 gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers); 716 } 717 } 718 return gl::FramebufferStatus::Complete(); 719} 720 721angle::Result FramebufferMtl::syncState(const gl::Context *context, 722 GLenum binding, 723 const gl::Framebuffer::DirtyBits &dirtyBits, 724 gl::Command command) 725{ 726 ContextMtl *contextMtl = mtl::GetImpl(context); 727 bool mustNotifyContext = false; 728 // Cache old mRenderPassDesc before update*RenderTarget() invalidate it. 729 mtl::RenderPassDesc oldRenderPassDesc = mRenderPassDesc; 730 731 for (size_t dirtyBit : dirtyBits) 732 { 733 switch (dirtyBit) 734 { 735 case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: 736 ANGLE_TRY(updateDepthRenderTarget(context)); 737 break; 738 case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: 739 ANGLE_TRY(updateStencilRenderTarget(context)); 740 break; 741 case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS: 742 // Restore depth attachment load action as its content may have been updated 743 // after framebuffer invalidation. 744 mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad; 745 break; 746 case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS: 747 // Restore stencil attachment load action as its content may have been updated 748 // after framebuffer invalidation. 749 mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 750 break; 751 case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS: 752 mustNotifyContext = true; 753 break; 754 case gl::Framebuffer::DIRTY_BIT_READ_BUFFER: 755 case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH: 756 case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT: 757 case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES: 758 case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS: 759 break; 760 default: 761 { 762 static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits"); 763 if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) 764 { 765 size_t colorIndexGL = static_cast<size_t>( 766 dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); 767 ANGLE_TRY(updateColorRenderTarget(context, colorIndexGL)); 768 } 769 else 770 { 771 ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 && 772 dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX); 773 // NOTE: might need to notify context. 774 775 // Restore color attachment load action as its content may have been updated 776 // after framebuffer invalidation. 777 size_t colorIndexGL = static_cast<size_t>( 778 dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0); 779 mRenderPassDesc.colorAttachments[colorIndexGL].loadAction = MTLLoadActionLoad; 780 } 781 break; 782 } 783 } 784 } 785 786 // If attachments have been changed and this is the current draw framebuffer, 787 // update the Metal context's incompatible attachments cache before preparing a render pass. 788 static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits"); 789 constexpr gl::Framebuffer::DirtyBits kAttachmentsMask = 790 gl::Framebuffer::DirtyBits::Mask(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX); 791 if (mustNotifyContext || (dirtyBits & kAttachmentsMask).any()) 792 { 793 const gl::State &glState = context->getState(); 794 if (mtl::GetImpl(glState.getDrawFramebuffer()) == this) 795 { 796 contextMtl->updateIncompatibleAttachments(glState); 797 } 798 } 799 800 ANGLE_TRY(prepareRenderPass(context, &mRenderPassDesc, command)); 801 bool renderPassChanged = !oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc); 802 803 if (mustNotifyContext || renderPassChanged) 804 { 805 FramebufferMtl *currentDrawFramebuffer = 806 mtl::GetImpl(context->getState().getDrawFramebuffer()); 807 if (currentDrawFramebuffer == this) 808 { 809 contextMtl->onDrawFrameBufferChangedState(context, this, renderPassChanged); 810 } 811 812 // Recreate pixel reading buffer if needed in future. 813 mReadPixelBuffer = nullptr; 814 } 815 816 return angle::Result::Continue; 817} 818 819angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context, 820 size_t index, 821 GLfloat *xy) const 822{ 823 UNIMPLEMENTED(); 824 return angle::Result::Stop; 825} 826 827bool FramebufferMtl::prepareForUse(const gl::Context *context) const 828{ 829 if (mBackbuffer) 830 { 831 // Backbuffer might obtain new drawable, which means it might change the 832 // the native texture used as the target of the render pass. 833 // We need to call this before creating render encoder. 834 if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context))) 835 { 836 return false; 837 } 838 839 if (mBackbuffer->hasRobustResourceInit()) 840 { 841 (void)mBackbuffer->initializeContents(context, GL_BACK, gl::ImageIndex::Make2D(0)); 842 if (mBackbuffer->hasDepthStencil()) 843 { 844 (void)mBackbuffer->initializeContents(context, GL_DEPTH, gl::ImageIndex::Make2D(0)); 845 } 846 } 847 } 848 return true; 849} 850 851RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget(const gl::Context *context) const 852{ 853 if (mState.getReadIndex() >= mColorRenderTargets.size()) 854 { 855 return nullptr; 856 } 857 858 if (!prepareForUse(context)) 859 { 860 return nullptr; 861 } 862 863 return mColorRenderTargets[mState.getReadIndex()]; 864} 865 866RenderTargetMtl *FramebufferMtl::getColorReadRenderTargetNoCache(const gl::Context *context) const 867{ 868 if (mState.getReadIndex() >= mColorRenderTargets.size()) 869 { 870 return nullptr; 871 } 872 873 if (mBackbuffer) 874 { 875 // If we have a backbuffer/window surface, we can take the old path here and return 876 // the cached color render target. 877 return getColorReadRenderTarget(context); 878 } 879 // If we have no backbuffer, get the attachment from state color attachments, as it may have 880 // changed before syncing. 881 const gl::FramebufferAttachment *attachment = mState.getColorAttachment(mState.getReadIndex()); 882 RenderTargetMtl *currentTarget = nullptr; 883 if (attachment->getRenderTarget(context, attachment->getRenderToTextureSamples(), 884 ¤tTarget) == angle::Result::Stop) 885 { 886 return nullptr; 887 } 888 return currentTarget; 889} 890 891int FramebufferMtl::getSamples() const 892{ 893 return mRenderPassDesc.sampleCount; 894} 895 896gl::Rectangle FramebufferMtl::getCompleteRenderArea() const 897{ 898 return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height); 899} 900 901bool FramebufferMtl::renderPassHasStarted(ContextMtl *contextMtl) const 902{ 903 return contextMtl->hasStartedRenderPass(mRenderPassDesc); 904} 905 906mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context) 907{ 908 return ensureRenderPassStarted(context, mRenderPassDesc); 909} 910 911mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context, 912 const mtl::RenderPassDesc &desc) 913{ 914 ContextMtl *contextMtl = mtl::GetImpl(context); 915 916 if (!prepareForUse(context)) 917 { 918 return nullptr; 919 } 920 921 // Only support ensureRenderPassStarted() with different load & store options only. The 922 // texture, level, slice must be the same. 923 ASSERT(desc.equalIgnoreLoadStoreOptions(mRenderPassDesc)); 924 925 mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(desc); 926 927 if (mRenderPassCleanStart) 928 { 929 // After a clean start we should reset the loadOp to MTLLoadActionLoad in case this render 930 // pass could be interrupted by a conversion compute shader pass then being resumed later. 931 mRenderPassCleanStart = false; 932 for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments) 933 { 934 colorAttachment.loadAction = MTLLoadActionLoad; 935 } 936 mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad; 937 mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 938 } 939 940 return encoder; 941} 942 943void FramebufferMtl::setLoadStoreActionOnRenderPassFirstStart( 944 mtl::RenderPassAttachmentDesc *attachmentOut, 945 const bool forceDepthStencilMultisampleLoad) 946{ 947 ASSERT(mRenderPassCleanStart); 948 949 mtl::RenderPassAttachmentDesc &attachment = *attachmentOut; 950 951 if (!forceDepthStencilMultisampleLoad && attachment.storeAction == MTLStoreActionDontCare) 952 { 953 // If we previously discarded attachment's content, then don't need to load it. 954 attachment.loadAction = MTLLoadActionDontCare; 955 } 956 else 957 { 958 attachment.loadAction = MTLLoadActionLoad; 959 } 960 961 if (attachment.hasImplicitMSTexture()) 962 { 963 attachment.storeAction = MTLStoreActionStoreAndMultisampleResolve; 964 } 965 else 966 { 967 attachment.storeAction = MTLStoreActionStore; // Default action is store 968 } 969} 970 971void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context) 972{ 973 mRenderPassCleanStart = true; 974 975 // If any of the render targets need to load their multisample textures, we should do the same 976 // for depth/stencil. 977 bool forceDepthStencilMultisampleLoad = false; 978 979 // Compute loadOp based on previous storeOp and reset storeOp flags: 980 for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments) 981 { 982 forceDepthStencilMultisampleLoad |= 983 colorAttachment.storeAction == MTLStoreActionStoreAndMultisampleResolve; 984 setLoadStoreActionOnRenderPassFirstStart(&colorAttachment, false); 985 } 986 // Depth load/store 987 setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.depthAttachment, 988 forceDepthStencilMultisampleLoad); 989 990 // Stencil load/store 991 setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.stencilAttachment, 992 forceDepthStencilMultisampleLoad); 993} 994 995void FramebufferMtl::onFrameEnd(const gl::Context *context) 996{ 997 if (!mBackbuffer || mBackbuffer->preserveBuffer()) 998 { 999 return; 1000 } 1001 1002 ContextMtl *contextMtl = mtl::GetImpl(context); 1003 // Always discard default FBO's depth stencil & multisample buffers at the end of the frame: 1004 if (this->renderPassHasStarted(contextMtl)) 1005 { 1006 mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(); 1007 1008 constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL}; 1009 (void)invalidateImpl(context, 2, dsAttachments); 1010 if (mBackbuffer->getSamples() > 1) 1011 { 1012 encoder->setColorStoreAction(MTLStoreActionMultisampleResolve, 0); 1013 } 1014 1015 contextMtl->endEncoding(false); 1016 1017 // Reset discard flag. 1018 onStartedDrawingToFrameBuffer(context); 1019 } 1020} 1021 1022angle::Result FramebufferMtl::updateColorRenderTarget(const gl::Context *context, 1023 size_t colorIndexGL) 1024{ 1025 ASSERT(colorIndexGL < mColorRenderTargets.size()); 1026 // Reset load store action 1027 mRenderPassDesc.colorAttachments[colorIndexGL].reset(); 1028 return updateCachedRenderTarget(context, mState.getColorAttachment(colorIndexGL), 1029 &mColorRenderTargets[colorIndexGL]); 1030} 1031 1032angle::Result FramebufferMtl::updateDepthRenderTarget(const gl::Context *context) 1033{ 1034 // Reset load store action 1035 mRenderPassDesc.depthAttachment.reset(); 1036 return updateCachedRenderTarget(context, mState.getDepthAttachment(), &mDepthRenderTarget); 1037} 1038 1039angle::Result FramebufferMtl::updateStencilRenderTarget(const gl::Context *context) 1040{ 1041 // Reset load store action 1042 mRenderPassDesc.stencilAttachment.reset(); 1043 return updateCachedRenderTarget(context, mState.getStencilAttachment(), &mStencilRenderTarget); 1044} 1045 1046angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *context, 1047 const gl::FramebufferAttachment *attachment, 1048 RenderTargetMtl **cachedRenderTarget) 1049{ 1050 RenderTargetMtl *newRenderTarget = nullptr; 1051 if (attachment) 1052 { 1053 ASSERT(attachment->isAttached()); 1054 ANGLE_TRY(attachment->getRenderTarget(context, attachment->getRenderToTextureSamples(), 1055 &newRenderTarget)); 1056 } 1057 *cachedRenderTarget = newRenderTarget; 1058 return angle::Result::Continue; 1059} 1060 1061angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context, 1062 mtl::RenderPassDesc *pDescOut, 1063 gl::Command command) 1064{ 1065 // Skip incompatible attachments for draw ops to avoid triggering Metal runtime failures. 1066 const gl::DrawBufferMask incompatibleAttachments = 1067 (command == gl::Command::Draw) ? mtl::GetImpl(context)->getIncompatibleAttachments() 1068 : gl::DrawBufferMask(); 1069 const gl::DrawBufferMask enabledDrawBuffers = 1070 getState().getEnabledDrawBuffers() & ~incompatibleAttachments; 1071 1072 mtl::RenderPassDesc &desc = *pDescOut; 1073 1074 mRenderPassFirstColorAttachmentFormat = nullptr; 1075 mRenderPassAttachmentsSameColorType = true; 1076 uint32_t maxColorAttachments = static_cast<uint32_t>(mState.getColorAttachments().size()); 1077 desc.numColorAttachments = 0; 1078 desc.sampleCount = 1; 1079 for (uint32_t colorIndexGL = 0; colorIndexGL < maxColorAttachments; ++colorIndexGL) 1080 { 1081 ASSERT(colorIndexGL < mColorRenderTargets.size()); 1082 1083 mtl::RenderPassColorAttachmentDesc &colorAttachment = desc.colorAttachments[colorIndexGL]; 1084 const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL]; 1085 1086 // GL allows data types of fragment shader color outputs to be incompatible with disabled 1087 // color attachments. To prevent various Metal validation issues, assign textures only to 1088 // enabled attachments. 1089 if (colorRenderTarget && enabledDrawBuffers.test(colorIndexGL)) 1090 { 1091 colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment); 1092 1093 desc.numColorAttachments = std::max(desc.numColorAttachments, colorIndexGL + 1); 1094 desc.sampleCount = std::max(desc.sampleCount, colorRenderTarget->getRenderSamples()); 1095 1096 if (!mRenderPassFirstColorAttachmentFormat) 1097 { 1098 mRenderPassFirstColorAttachmentFormat = &colorRenderTarget->getFormat(); 1099 } 1100 else 1101 { 1102 if (mRenderPassFirstColorAttachmentFormat->actualAngleFormat().isSint() != 1103 colorRenderTarget->getFormat().actualAngleFormat().isSint() || 1104 mRenderPassFirstColorAttachmentFormat->actualAngleFormat().isUint() != 1105 colorRenderTarget->getFormat().actualAngleFormat().isUint()) 1106 { 1107 mRenderPassAttachmentsSameColorType = false; 1108 } 1109 } 1110 } 1111 else 1112 { 1113 colorAttachment.reset(); 1114 } 1115 } 1116 1117 if (mDepthRenderTarget) 1118 { 1119 mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment); 1120 desc.sampleCount = std::max(desc.sampleCount, mDepthRenderTarget->getRenderSamples()); 1121 } 1122 else 1123 { 1124 desc.depthAttachment.reset(); 1125 } 1126 1127 if (mStencilRenderTarget) 1128 { 1129 mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment); 1130 desc.sampleCount = std::max(desc.sampleCount, mStencilRenderTarget->getRenderSamples()); 1131 } 1132 else 1133 { 1134 desc.stencilAttachment.reset(); 1135 } 1136 1137 if (desc.numColorAttachments == 0 && mDepthRenderTarget == nullptr && 1138 mStencilRenderTarget == nullptr) 1139 { 1140 desc.defaultWidth = mState.getDefaultWidth(); 1141 desc.defaultHeight = mState.getDefaultHeight(); 1142 } 1143 1144 return angle::Result::Continue; 1145} 1146 1147angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context, 1148 gl::DrawBufferMask clearColorBuffers, 1149 const mtl::ClearRectParams &clearOpts) 1150{ 1151 ContextMtl *contextMtl = mtl::GetImpl(context); 1152 bool startedRenderPass = contextMtl->hasStartedRenderPass(mRenderPassDesc); 1153 mtl::RenderCommandEncoder *encoder = nullptr; 1154 1155 if (startedRenderPass) 1156 { 1157 encoder = ensureRenderPassStarted(context); 1158 if (encoder->hasDrawCalls()) 1159 { 1160 // Render pass already has draw calls recorded, it is better to use clear with draw 1161 // operation. 1162 return clearWithDraw(context, clearColorBuffers, clearOpts); 1163 } 1164 else 1165 { 1166 // If render pass has started but there is no draw call yet. It is OK to change the 1167 // loadOp. 1168 return clearWithLoadOpRenderPassStarted(context, clearColorBuffers, clearOpts, encoder); 1169 } 1170 } 1171 else 1172 { 1173 return clearWithLoadOpRenderPassNotStarted(context, clearColorBuffers, clearOpts); 1174 } 1175} 1176 1177angle::Result FramebufferMtl::clearWithLoadOpRenderPassNotStarted( 1178 const gl::Context *context, 1179 gl::DrawBufferMask clearColorBuffers, 1180 const mtl::ClearRectParams &clearOpts) 1181{ 1182 mtl::RenderPassDesc tempDesc = mRenderPassDesc; 1183 1184 for (uint32_t colorIndexGL = 0; colorIndexGL < tempDesc.numColorAttachments; ++colorIndexGL) 1185 { 1186 ASSERT(colorIndexGL < tempDesc.colorAttachments.size()); 1187 1188 mtl::RenderPassColorAttachmentDesc &colorAttachment = 1189 tempDesc.colorAttachments[colorIndexGL]; 1190 const mtl::TextureRef &texture = colorAttachment.texture; 1191 1192 if (clearColorBuffers.test(colorIndexGL)) 1193 { 1194 colorAttachment.loadAction = MTLLoadActionClear; 1195 OverrideMTLClearColor(texture, clearOpts.clearColor.value(), 1196 &colorAttachment.clearColor); 1197 } 1198 else 1199 { 1200 colorAttachment.loadAction = MTLLoadActionLoad; 1201 } 1202 1203 if (colorAttachment.hasImplicitMSTexture()) 1204 { 1205 colorAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve; 1206 } 1207 else 1208 { 1209 colorAttachment.storeAction = MTLStoreActionStore; 1210 } 1211 } 1212 1213 if (clearOpts.clearDepth.valid()) 1214 { 1215 tempDesc.depthAttachment.loadAction = MTLLoadActionClear; 1216 tempDesc.depthAttachment.clearDepth = clearOpts.clearDepth.value(); 1217 } 1218 else 1219 { 1220 tempDesc.depthAttachment.loadAction = MTLLoadActionLoad; 1221 } 1222 1223 if (tempDesc.depthAttachment.hasImplicitMSTexture()) 1224 { 1225 tempDesc.depthAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve; 1226 } 1227 else 1228 { 1229 tempDesc.depthAttachment.storeAction = MTLStoreActionStore; 1230 } 1231 1232 if (clearOpts.clearStencil.valid()) 1233 { 1234 tempDesc.stencilAttachment.loadAction = MTLLoadActionClear; 1235 tempDesc.stencilAttachment.clearStencil = clearOpts.clearStencil.value(); 1236 } 1237 else 1238 { 1239 tempDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 1240 } 1241 1242 if (tempDesc.stencilAttachment.hasImplicitMSTexture()) 1243 { 1244 tempDesc.stencilAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve; 1245 } 1246 else 1247 { 1248 tempDesc.stencilAttachment.storeAction = MTLStoreActionStore; 1249 } 1250 1251 // Start new render encoder with loadOp=Clear 1252 ensureRenderPassStarted(context, tempDesc); 1253 1254 return angle::Result::Continue; 1255} 1256 1257angle::Result FramebufferMtl::clearWithLoadOpRenderPassStarted( 1258 const gl::Context *context, 1259 gl::DrawBufferMask clearColorBuffers, 1260 const mtl::ClearRectParams &clearOpts, 1261 mtl::RenderCommandEncoder *encoder) 1262{ 1263 ASSERT(!encoder->hasDrawCalls()); 1264 1265 for (uint32_t colorIndexGL = 0; colorIndexGL < mRenderPassDesc.numColorAttachments; 1266 ++colorIndexGL) 1267 { 1268 ASSERT(colorIndexGL < mRenderPassDesc.colorAttachments.size()); 1269 1270 mtl::RenderPassColorAttachmentDesc &colorAttachment = 1271 mRenderPassDesc.colorAttachments[colorIndexGL]; 1272 const mtl::TextureRef &texture = colorAttachment.texture; 1273 1274 if (clearColorBuffers.test(colorIndexGL)) 1275 { 1276 MTLClearColor clearVal; 1277 OverrideMTLClearColor(texture, clearOpts.clearColor.value(), &clearVal); 1278 1279 encoder->setColorLoadAction(MTLLoadActionClear, clearVal, colorIndexGL); 1280 } 1281 } 1282 1283 if (clearOpts.clearDepth.valid()) 1284 { 1285 encoder->setDepthLoadAction(MTLLoadActionClear, clearOpts.clearDepth.value()); 1286 } 1287 1288 if (clearOpts.clearStencil.valid()) 1289 { 1290 encoder->setStencilLoadAction(MTLLoadActionClear, clearOpts.clearStencil.value()); 1291 } 1292 1293 return angle::Result::Continue; 1294} 1295 1296angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context, 1297 gl::DrawBufferMask clearColorBuffers, 1298 const mtl::ClearRectParams &clearOpts) 1299{ 1300 ContextMtl *contextMtl = mtl::GetImpl(context); 1301 DisplayMtl *display = contextMtl->getDisplay(); 1302 1303 if (mRenderPassAttachmentsSameColorType) 1304 { 1305 // Start new render encoder if not already. 1306 mtl::RenderCommandEncoder *encoder = ensureRenderPassStarted(context, mRenderPassDesc); 1307 1308 return display->getUtils().clearWithDraw(context, encoder, clearOpts); 1309 } 1310 1311 // Not all attachments have the same color type. 1312 mtl::ClearRectParams overrideClearOps = clearOpts; 1313 overrideClearOps.enabledBuffers.reset(); 1314 1315 // First clear depth/stencil without color attachment 1316 if (clearOpts.clearDepth.valid() || clearOpts.clearStencil.valid()) 1317 { 1318 mtl::RenderPassDesc dsOnlyDesc = mRenderPassDesc; 1319 dsOnlyDesc.numColorAttachments = 0; 1320 mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(dsOnlyDesc); 1321 1322 ANGLE_TRY(display->getUtils().clearWithDraw(context, encoder, overrideClearOps)); 1323 } 1324 1325 // Clear the color attachment one by one. 1326 overrideClearOps.enabledBuffers.set(0); 1327 for (size_t drawbuffer : clearColorBuffers) 1328 { 1329 if (drawbuffer >= mRenderPassDesc.numColorAttachments) 1330 { 1331 // Iteration over drawbuffer indices always goes in ascending order 1332 break; 1333 } 1334 RenderTargetMtl *renderTarget = mColorRenderTargets[drawbuffer]; 1335 if (!renderTarget || !renderTarget->getTexture()) 1336 { 1337 continue; 1338 } 1339 const mtl::Format &format = renderTarget->getFormat(); 1340 mtl::PixelType clearColorType = overrideClearOps.clearColor.value().getType(); 1341 if ((clearColorType == mtl::PixelType::Int && !format.actualAngleFormat().isSint()) || 1342 (clearColorType == mtl::PixelType::UInt && !format.actualAngleFormat().isUint()) || 1343 (clearColorType == mtl::PixelType::Float && format.actualAngleFormat().isInt())) 1344 { 1345 continue; 1346 } 1347 1348 overrideClearOps.clearWriteMaskArray[0] = overrideClearOps.clearWriteMaskArray[drawbuffer]; 1349 1350 mtl::RenderCommandEncoder *encoder = 1351 contextMtl->getRenderTargetCommandEncoder(*renderTarget); 1352 ANGLE_TRY(display->getUtils().clearWithDraw(context, encoder, overrideClearOps)); 1353 } 1354 1355 return angle::Result::Continue; 1356} 1357 1358angle::Result FramebufferMtl::clearImpl(const gl::Context *context, 1359 gl::DrawBufferMask clearColorBuffers, 1360 mtl::ClearRectParams *pClearOpts) 1361{ 1362 auto &clearOpts = *pClearOpts; 1363 1364 if (!clearOpts.clearColor.valid() && !clearOpts.clearDepth.valid() && 1365 !clearOpts.clearStencil.valid()) 1366 { 1367 // No Op. 1368 return angle::Result::Continue; 1369 } 1370 1371 ContextMtl *contextMtl = mtl::GetImpl(context); 1372 const gl::Rectangle renderArea(0, 0, mState.getDimensions().width, 1373 mState.getDimensions().height); 1374 1375 clearOpts.colorFormat = mRenderPassFirstColorAttachmentFormat; 1376 clearOpts.dstTextureSize = mState.getExtents(); 1377 clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false); 1378 clearOpts.flipY = mFlipY; 1379 1380 // Discard clear altogether if scissor has 0 width or height. 1381 if (clearOpts.clearArea.width == 0 || clearOpts.clearArea.height == 0) 1382 { 1383 return angle::Result::Continue; 1384 } 1385 1386 clearOpts.clearWriteMaskArray = contextMtl->getWriteMaskArray(); 1387 uint32_t stencilMask = contextMtl->getStencilMask(); 1388 if (!contextMtl->getDepthMask()) 1389 { 1390 // Disable depth clearing, since depth write is disable 1391 clearOpts.clearDepth.reset(); 1392 } 1393 1394 // Only clear enabled buffers 1395 clearOpts.enabledBuffers = clearColorBuffers; 1396 1397 bool allBuffersUnmasked = true; 1398 for (size_t enabledBuffer : clearColorBuffers) 1399 { 1400 if (clearOpts.clearWriteMaskArray[enabledBuffer] != MTLColorWriteMaskAll) 1401 { 1402 allBuffersUnmasked = false; 1403 break; 1404 } 1405 } 1406 1407 if (clearOpts.clearArea == renderArea && 1408 (!clearOpts.clearColor.valid() || allBuffersUnmasked) && 1409 (!clearOpts.clearStencil.valid() || 1410 (stencilMask & mtl::kStencilMaskAll) == mtl::kStencilMaskAll)) 1411 { 1412 return clearWithLoadOp(context, clearColorBuffers, clearOpts); 1413 } 1414 1415 return clearWithDraw(context, clearColorBuffers, clearOpts); 1416} 1417 1418angle::Result FramebufferMtl::invalidateImpl(const gl::Context *context, 1419 size_t count, 1420 const GLenum *attachments) 1421{ 1422 ContextMtl *contextMtl = mtl::GetImpl(context); 1423 gl::DrawBufferMask invalidateColorBuffers; 1424 bool invalidateDepthBuffer = false; 1425 bool invalidateStencilBuffer = false; 1426 1427 for (size_t i = 0; i < count; ++i) 1428 { 1429 const GLenum attachment = attachments[i]; 1430 1431 switch (attachment) 1432 { 1433 case GL_DEPTH: 1434 case GL_DEPTH_ATTACHMENT: 1435 invalidateDepthBuffer = true; 1436 break; 1437 case GL_STENCIL: 1438 case GL_STENCIL_ATTACHMENT: 1439 invalidateStencilBuffer = true; 1440 break; 1441 case GL_DEPTH_STENCIL_ATTACHMENT: 1442 invalidateDepthBuffer = true; 1443 invalidateStencilBuffer = true; 1444 break; 1445 default: 1446 ASSERT( 1447 (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) || 1448 (attachment == GL_COLOR)); 1449 1450 invalidateColorBuffers.set( 1451 attachment == GL_COLOR ? 0u : (attachment - GL_COLOR_ATTACHMENT0)); 1452 } 1453 } 1454 1455 // Set the appropriate storeOp for attachments. 1456 // If we already start the render pass, then need to set the store action now. 1457 bool renderPassStarted = contextMtl->hasStartedRenderPass(mRenderPassDesc); 1458 mtl::RenderCommandEncoder *encoder = 1459 renderPassStarted ? contextMtl->getRenderCommandEncoder() : nullptr; 1460 1461 for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i) 1462 { 1463 if (invalidateColorBuffers.test(i)) 1464 { 1465 // Some opaque formats, like RGB8, are emulated as RGBA with alpha channel initialized 1466 // to 1.0. Invalidating such attachments may lead to random values in their alpha 1467 // channel, so skip invalidation in this case. 1468 RenderTargetMtl *renderTarget = mColorRenderTargets[i]; 1469 if (renderTarget && renderTarget->getTexture()) 1470 { 1471 const mtl::Format &mtlFormat = renderTarget->getFormat(); 1472 const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat(); 1473 const angle::Format &actualFormat = mtlFormat.actualAngleFormat(); 1474 if (intendedFormat.alphaBits == 0 && actualFormat.alphaBits) 1475 { 1476 continue; 1477 } 1478 } 1479 1480 mtl::RenderPassColorAttachmentDesc &colorAttachment = 1481 mRenderPassDesc.colorAttachments[i]; 1482 colorAttachment.storeAction = MTLStoreActionDontCare; 1483 if (renderPassStarted) 1484 { 1485 encoder->setColorStoreAction(MTLStoreActionDontCare, i); 1486 } 1487 } 1488 } 1489 1490 if (invalidateDepthBuffer && mDepthRenderTarget) 1491 { 1492 mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare; 1493 if (renderPassStarted) 1494 { 1495 encoder->setDepthStoreAction(MTLStoreActionDontCare); 1496 } 1497 } 1498 1499 if (invalidateStencilBuffer && mStencilRenderTarget) 1500 { 1501 mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare; 1502 if (renderPassStarted) 1503 { 1504 encoder->setStencilStoreAction(MTLStoreActionDontCare); 1505 } 1506 } 1507 1508 // Do not encode any further commands in this render pass which can affect the 1509 // framebuffer, or their effects will be lost. 1510 contextMtl->endEncoding(false); 1511 // Reset discard flag. 1512 onStartedDrawingToFrameBuffer(context); 1513 1514 return angle::Result::Continue; 1515} 1516 1517gl::Rectangle FramebufferMtl::getCorrectFlippedReadArea(const gl::Context *context, 1518 const gl::Rectangle &glArea) const 1519{ 1520 RenderTargetMtl *readRT = getColorReadRenderTarget(context); 1521 if (!readRT) 1522 { 1523 readRT = mDepthRenderTarget; 1524 } 1525 if (!readRT) 1526 { 1527 readRT = mStencilRenderTarget; 1528 } 1529 ASSERT(readRT); 1530 gl::Rectangle flippedArea = glArea; 1531 if (mFlipY) 1532 { 1533 flippedArea.y = readRT->getTexture()->height(readRT->getLevelIndex()) - flippedArea.y - 1534 flippedArea.height; 1535 } 1536 1537 return flippedArea; 1538} 1539 1540namespace 1541{ 1542 1543angle::Result readPixelsCopyImpl( 1544 const gl::Context *context, 1545 const gl::Rectangle &area, 1546 const PackPixelsParams &packPixelsParams, 1547 const RenderTargetMtl *renderTarget, 1548 const std::function<angle::Result(const gl::Rectangle ®ion, const uint8_t *&src)> &getDataFn, 1549 uint8_t *pixels) 1550{ 1551 const mtl::Format &readFormat = renderTarget->getFormat(); 1552 const angle::Format &readAngleFormat = readFormat.actualAngleFormat(); 1553 1554 auto packPixelsRowParams = packPixelsParams; 1555 gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1); 1556 int bufferRowPitch = area.width * readAngleFormat.pixelBytes; 1557 1558 int rowOffset = packPixelsParams.reverseRowOrder ? -1 : 1; 1559 int startRow = packPixelsParams.reverseRowOrder ? (area.y1() - 1) : area.y; 1560 1561 // Copy pixels row by row 1562 packPixelsRowParams.area.height = 1; 1563 packPixelsRowParams.reverseRowOrder = false; 1564 for (int r = startRow, i = 0; i < area.height; 1565 ++i, r += rowOffset, pixels += packPixelsRowParams.outputPitch) 1566 { 1567 srcRowRegion.y = r; 1568 packPixelsRowParams.area.y = packPixelsParams.area.y + i; 1569 1570 const uint8_t *src; 1571 ANGLE_TRY(getDataFn(srcRowRegion, src)); 1572 PackPixels(packPixelsRowParams, readAngleFormat, bufferRowPitch, src, pixels); 1573 } 1574 1575 return angle::Result::Continue; 1576} 1577 1578} // namespace 1579 1580angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context, 1581 const gl::Rectangle &area, 1582 const PackPixelsParams &packPixelsParams, 1583 const RenderTargetMtl *renderTarget, 1584 uint8_t *pixels) const 1585{ 1586 ContextMtl *contextMtl = mtl::GetImpl(context); 1587 const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures(); 1588 1589 if (!renderTarget) 1590 { 1591 return angle::Result::Continue; 1592 } 1593 1594 if (packPixelsParams.packBuffer) 1595 { 1596 return readPixelsToPBO(context, area, packPixelsParams, renderTarget); 1597 } 1598 1599 mtl::TextureRef texture; 1600 if (mBackbuffer) 1601 { 1602 // Backbuffer might have MSAA texture as render target, needs to obtain the 1603 // resolved texture to be able to read pixels. 1604 ANGLE_TRY(mBackbuffer->ensureColorTextureReadyForReadPixels(context)); 1605 texture = mBackbuffer->getColorTexture(); 1606 } 1607 else 1608 { 1609 texture = renderTarget->getTexture(); 1610 // For non-default framebuffer, MSAA read pixels is disallowed. 1611 if (!texture) 1612 { 1613 return angle::Result::Stop; 1614 } 1615 ANGLE_MTL_CHECK(contextMtl, texture->samples() == 1, GL_INVALID_OPERATION); 1616 } 1617 1618 const mtl::Format &readFormat = renderTarget->getFormat(); 1619 const angle::Format &readAngleFormat = readFormat.actualAngleFormat(); 1620 1621 if (features.copyIOSurfaceToNonIOSurfaceForReadOptimization.enabled && 1622 texture->hasIOSurface() && texture->mipmapLevels() == 1 && 1623 texture->textureType() == MTLTextureType2D) 1624 { 1625 // Reading a texture may be slow if it's an IOSurface because metal has to lock/unlock the 1626 // surface, whereas copying the texture to non IOSurface texture and then reading from that 1627 // may be fast depending on the GPU/driver. 1628 ANGLE_TRY(Copy2DTextureSlice0Level0ToTempTexture(context, texture, &texture)); 1629 } 1630 1631 if (features.copyTextureToBufferForReadOptimization.enabled) 1632 { 1633 mtl::BufferRef buffer; 1634 ANGLE_TRY(CopyTextureSliceLevelToTempBuffer(context, texture, renderTarget->getLevelIndex(), 1635 renderTarget->getLayerIndex(), &buffer)); 1636 1637 int bufferRowPitch = 1638 texture->width(renderTarget->getLevelIndex()) * readAngleFormat.pixelBytes; 1639 1640 buffer->syncContent(contextMtl, contextMtl->getBlitCommandEncoder()); 1641 const uint8_t *bufferData = buffer->mapReadOnly(contextMtl); 1642 1643 angle::Result result = readPixelsCopyImpl( 1644 context, area, packPixelsParams, renderTarget, 1645 [&](const gl::Rectangle ®ion, const uint8_t *&src) { 1646 src = 1647 bufferData + region.y * bufferRowPitch + region.x * readAngleFormat.pixelBytes; 1648 return angle::Result::Continue; 1649 }, 1650 pixels); 1651 1652 buffer->unmap(contextMtl); 1653 1654 return result; 1655 } 1656 1657 if (texture->isBeingUsedByGPU(contextMtl)) 1658 { 1659 contextMtl->flushCommandBuffer(mtl::WaitUntilFinished); 1660 } 1661 1662 angle::MemoryBuffer readPixelRowBuffer; 1663 int bufferRowPitch = area.width * readAngleFormat.pixelBytes; 1664 ANGLE_CHECK_GL_ALLOC(contextMtl, readPixelRowBuffer.resize(bufferRowPitch)); 1665 return readPixelsCopyImpl( 1666 context, area, packPixelsParams, renderTarget, 1667 [&](const gl::Rectangle ®ion, const uint8_t *&src) { 1668 // Read the pixels data to the row buffer 1669 ANGLE_TRY(mtl::ReadTexturePerSliceBytes( 1670 context, texture, bufferRowPitch, region, renderTarget->getLevelIndex(), 1671 renderTarget->getLayerIndex(), readPixelRowBuffer.data())); 1672 src = readPixelRowBuffer.data(); 1673 return angle::Result::Continue; 1674 }, 1675 pixels); 1676} 1677 1678angle::Result FramebufferMtl::readPixelsToPBO(const gl::Context *context, 1679 const gl::Rectangle &area, 1680 const PackPixelsParams &packPixelsParams, 1681 const RenderTargetMtl *renderTarget) const 1682{ 1683 ASSERT(packPixelsParams.packBuffer); 1684 ASSERT(renderTarget); 1685 1686 ContextMtl *contextMtl = mtl::GetImpl(context); 1687 1688 ANGLE_MTL_CHECK(contextMtl, packPixelsParams.offset <= std::numeric_limits<uint32_t>::max(), 1689 GL_INVALID_OPERATION); 1690 uint32_t offset = static_cast<uint32_t>(packPixelsParams.offset); 1691 1692 BufferMtl *packBufferMtl = mtl::GetImpl(packPixelsParams.packBuffer); 1693 mtl::BufferRef dstBuffer = packBufferMtl->getCurrentBuffer(); 1694 1695 return readPixelsToBuffer(context, area, renderTarget, packPixelsParams.reverseRowOrder, 1696 *packPixelsParams.destFormat, offset, packPixelsParams.outputPitch, 1697 &dstBuffer); 1698} 1699 1700angle::Result FramebufferMtl::readPixelsToBuffer(const gl::Context *context, 1701 const gl::Rectangle &area, 1702 const RenderTargetMtl *renderTarget, 1703 bool reverseRowOrder, 1704 const angle::Format &dstAngleFormat, 1705 uint32_t dstBufferOffset, 1706 uint32_t dstBufferRowPitch, 1707 const mtl::BufferRef *pDstBuffer) const 1708{ 1709 ASSERT(renderTarget); 1710 1711 ContextMtl *contextMtl = mtl::GetImpl(context); 1712 1713 const mtl::Format &readFormat = renderTarget->getFormat(); 1714 const angle::Format &readAngleFormat = readFormat.actualAngleFormat(); 1715 1716 mtl::TextureRef texture = renderTarget->getTexture(); 1717 1718 const mtl::BufferRef &dstBuffer = *pDstBuffer; 1719 1720 if (dstAngleFormat.id != readAngleFormat.id || texture->samples() > 1 || 1721 (dstBufferOffset % dstAngleFormat.pixelBytes) || 1722 (dstBufferOffset % mtl::kTextureToBufferBlittingAlignment)) 1723 { 1724 const angle::Format *actualDstAngleFormat; 1725 1726 // SRGB is special case: We need to write sRGB values to buffer, not linear values. 1727 switch (readAngleFormat.id) 1728 { 1729 case angle::FormatID::B8G8R8A8_UNORM_SRGB: 1730 case angle::FormatID::R8G8B8_UNORM_SRGB: 1731 case angle::FormatID::R8G8B8A8_UNORM_SRGB: 1732 if (dstAngleFormat.id != readAngleFormat.id) 1733 { 1734 switch (dstAngleFormat.id) 1735 { 1736 case angle::FormatID::B8G8R8A8_UNORM: 1737 actualDstAngleFormat = 1738 &angle::Format::Get(angle::FormatID::B8G8R8A8_UNORM_SRGB); 1739 break; 1740 case angle::FormatID::R8G8B8A8_UNORM: 1741 actualDstAngleFormat = 1742 &angle::Format::Get(angle::FormatID::R8G8B8A8_UNORM_SRGB); 1743 break; 1744 default: 1745 // Unsupported format. 1746 ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_ENUM); 1747 } 1748 break; 1749 } 1750 OS_FALLTHROUGH; 1751 default: 1752 actualDstAngleFormat = &dstAngleFormat; 1753 } 1754 1755 // Use compute shader 1756 mtl::CopyPixelsToBufferParams params; 1757 params.buffer = dstBuffer; 1758 params.bufferStartOffset = dstBufferOffset; 1759 params.bufferRowPitch = dstBufferRowPitch; 1760 1761 params.texture = texture; 1762 params.textureArea = area; 1763 params.textureLevel = renderTarget->getLevelIndex(); 1764 params.textureSliceOrDeph = renderTarget->getLayerIndex(); 1765 params.reverseTextureRowOrder = reverseRowOrder; 1766 1767 ANGLE_TRY(contextMtl->getDisplay()->getUtils().packPixelsFromTextureToBuffer( 1768 contextMtl, *actualDstAngleFormat, params)); 1769 } 1770 else 1771 { 1772 // Use blit command encoder 1773 if (!reverseRowOrder) 1774 { 1775 ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer( 1776 context, texture, dstBufferRowPitch, area, renderTarget->getLevelIndex(), 1777 renderTarget->getLayerIndex(), dstBufferOffset, dstBuffer)); 1778 } 1779 else 1780 { 1781 gl::Rectangle srcRowRegion(area.x, area.y, area.width, 1); 1782 1783 int startRow = area.y1() - 1; 1784 1785 uint32_t bufferRowOffset = dstBufferOffset; 1786 // Copy pixels row by row 1787 for (int r = startRow, copiedRows = 0; copiedRows < area.height; 1788 ++copiedRows, --r, bufferRowOffset += dstBufferRowPitch) 1789 { 1790 srcRowRegion.y = r; 1791 1792 // Read the pixels data to the buffer's row 1793 ANGLE_TRY(mtl::ReadTexturePerSliceBytesToBuffer( 1794 context, texture, dstBufferRowPitch, srcRowRegion, 1795 renderTarget->getLevelIndex(), renderTarget->getLayerIndex(), bufferRowOffset, 1796 dstBuffer)); 1797 } 1798 } 1799 } 1800 1801 return angle::Result::Continue; 1802} 1803 1804} // namespace rx 1805