1 2// 3// Copyright 2019 The ANGLE Project Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style license that can be 5// found in the LICENSE file. 6// 7// TextureMtl.mm: 8// Implements the class methods for TextureMtl. 9// 10 11#include "libANGLE/renderer/metal/TextureMtl.h" 12 13#include "common/Color.h" 14#include "common/MemoryBuffer.h" 15#include "common/debug.h" 16#include "common/mathutil.h" 17#include "libANGLE/renderer/metal/ContextMtl.h" 18#include "libANGLE/renderer/metal/DisplayMtl.h" 19#include "libANGLE/renderer/metal/FrameBufferMtl.h" 20#include "libANGLE/renderer/metal/mtl_common.h" 21#include "libANGLE/renderer/metal/mtl_format_utils.h" 22#include "libANGLE/renderer/metal/mtl_utils.h" 23#include "libANGLE/renderer/renderer_utils.h" 24 25namespace rx 26{ 27 28namespace 29{ 30 31MTLColorWriteMask GetColorWriteMask(const mtl::Format &mtlFormat, bool *emulatedChannelsOut) 32{ 33 const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat(); 34 const angle::Format &actualFormat = mtlFormat.actualAngleFormat(); 35 bool emulatedChannels = false; 36 MTLColorWriteMask colorWritableMask = MTLColorWriteMaskAll; 37 if (intendedFormat.alphaBits == 0 && actualFormat.alphaBits) 38 { 39 emulatedChannels = true; 40 // Disable alpha write to this texture 41 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskAlpha); 42 } 43 if (intendedFormat.luminanceBits == 0) 44 { 45 if (intendedFormat.redBits == 0 && actualFormat.redBits) 46 { 47 emulatedChannels = true; 48 // Disable red write to this texture 49 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskRed); 50 } 51 if (intendedFormat.greenBits == 0 && actualFormat.greenBits) 52 { 53 emulatedChannels = true; 54 // Disable green write to this texture 55 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskGreen); 56 } 57 if (intendedFormat.blueBits == 0 && actualFormat.blueBits) 58 { 59 emulatedChannels = true; 60 // Disable blue write to this texture 61 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskBlue); 62 } 63 } 64 65 *emulatedChannelsOut = emulatedChannels; 66 67 return colorWritableMask; 68} 69 70gl::ImageIndex GetImageBaseLevelIndex(const mtl::TextureRef &image) 71{ 72 gl::ImageIndex imageBaseIndex; 73 switch (image->textureType()) 74 { 75 case MTLTextureType2D: 76 imageBaseIndex = gl::ImageIndex::Make2D(0); 77 break; 78 default: 79 UNREACHABLE(); 80 break; 81 } 82 83 return imageBaseIndex; 84} 85 86GLuint GetImageLayerIndex(const gl::ImageIndex &index) 87{ 88 switch (index.getType()) 89 { 90 case gl::TextureType::_2D: 91 return 0; 92 case gl::TextureType::CubeMap: 93 return index.cubeMapFaceIndex(); 94 default: 95 UNREACHABLE(); 96 } 97 98 return 0; 99} 100 101// Given texture type, get texture type of one image at a slice and a level. 102// For example, for texture 2d, one image is also texture 2d. 103// for texture cube, one image is texture 2d. 104// For texture 2d array, one image is texture 2d. 105gl::TextureType GetTextureImageType(gl::TextureType texType) 106{ 107 switch (texType) 108 { 109 case gl::TextureType::_2D: 110 case gl::TextureType::CubeMap: 111 return gl::TextureType::_2D; 112 default: 113 UNREACHABLE(); 114 return gl::TextureType::InvalidEnum; 115 } 116} 117 118angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl, 119 const angle::Format &textureAngleFormat, 120 const angle::Format &stagingAngleFormat, 121 const MTLSize ®ionSize, 122 const uint8_t *data, 123 size_t bytesPerRow, 124 size_t *bufferRowPitchOut, 125 size_t *buffer2DImageSizeOut, 126 mtl::BufferRef *bufferOut) 127{ 128 // NOTE(hqle): 3D textures not supported yet. 129 ASSERT(regionSize.depth == 1); 130 131 size_t stagingBufferRowPitch = regionSize.width * stagingAngleFormat.pixelBytes; 132 size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height; 133 size_t stagingBufferSize = stagingBuffer2DImageSize * regionSize.depth; 134 mtl::BufferRef stagingBuffer; 135 ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer)); 136 137 uint8_t *pdst = stagingBuffer->map(contextMtl); 138 139 if (textureAngleFormat.id == stagingAngleFormat.id) 140 { 141 for (NSUInteger r = 0; r < regionSize.height; ++r) 142 { 143 const uint8_t *pCopySrc = data + r * bytesPerRow; 144 uint8_t *pCopyDst = pdst + r * stagingBufferRowPitch; 145 memcpy(pCopyDst, pCopySrc, stagingBufferRowPitch); 146 } 147 } 148 else 149 { 150 // This is only for depth & stencil case. 151 ASSERT(textureAngleFormat.depthBits || textureAngleFormat.stencilBits); 152 ASSERT(textureAngleFormat.pixelReadFunction && stagingAngleFormat.pixelWriteFunction); 153 154 // cache to store read result of source pixel 155 angle::DepthStencil depthStencilData; 156 auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData); 157 ASSERT(textureAngleFormat.pixelBytes <= sizeof(depthStencilData)); 158 159 for (NSUInteger r = 0; r < regionSize.height; ++r) 160 { 161 for (NSUInteger c = 0; c < regionSize.width; ++c) 162 { 163 const uint8_t *sourcePixelData = 164 data + r * bytesPerRow + c * textureAngleFormat.pixelBytes; 165 166 uint8_t *destPixelData = 167 pdst + r * stagingBufferRowPitch + c * stagingAngleFormat.pixelBytes; 168 169 textureAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData); 170 stagingAngleFormat.pixelWriteFunction(sourcePixelReadData, destPixelData); 171 } 172 } 173 } 174 175 stagingBuffer->unmap(contextMtl); 176 177 *bufferOut = stagingBuffer; 178 *bufferRowPitchOut = stagingBufferRowPitch; 179 *buffer2DImageSizeOut = stagingBuffer2DImageSize; 180 181 return angle::Result::Continue; 182} 183 184angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl, 185 const mtl::TextureRef &texture, 186 const angle::Format &textureAngleFormat, 187 MTLRegion region, 188 uint32_t mipmapLevel, 189 uint32_t slice, 190 const uint8_t *data, 191 size_t bytesPerRow) 192{ 193 ASSERT(texture && texture->valid()); 194 195 ASSERT(!texture->isCPUAccessible()); 196 197 ASSERT(!textureAngleFormat.depthBits || !textureAngleFormat.stencilBits); 198 199 // Compressed texture is not supporte atm 200 ASSERT(!textureAngleFormat.isBlock); 201 202 // Copy data to staging buffer 203 size_t stagingBufferRowPitch; 204 size_t stagingBuffer2DImageSize; 205 mtl::BufferRef stagingBuffer; 206 ANGLE_TRY(CopyTextureContentsToStagingBuffer( 207 contextMtl, textureAngleFormat, textureAngleFormat, region.size, data, bytesPerRow, 208 &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer)); 209 210 // Copy staging buffer to texture. 211 mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder(); 212 encoder->copyBufferToTexture(stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize, 213 region.size, texture, slice, mipmapLevel, region.origin, 214 MTLBlitOptionNone); 215 216 return angle::Result::Continue; 217} 218 219// Packed depth stencil upload using staging buffer 220angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer( 221 ContextMtl *contextMtl, 222 const mtl::TextureRef &texture, 223 const angle::Format &textureAngleFormat, 224 MTLRegion region, 225 uint32_t mipmapLevel, 226 uint32_t slice, 227 const uint8_t *data, 228 size_t bytesPerRow) 229{ 230 ASSERT(texture && texture->valid()); 231 232 ASSERT(!texture->isCPUAccessible()); 233 234 ASSERT(textureAngleFormat.depthBits && textureAngleFormat.stencilBits); 235 236 // We have to split the depth & stencil data into 2 buffers. 237 angle::FormatID stagingDepthBufferFormatId; 238 angle::FormatID stagingStencilBufferFormatId; 239 240 switch (textureAngleFormat.id) 241 { 242 case angle::FormatID::D24_UNORM_S8_UINT: 243 stagingDepthBufferFormatId = angle::FormatID::D24_UNORM_X8_UINT; 244 stagingStencilBufferFormatId = angle::FormatID::S8_UINT; 245 break; 246 case angle::FormatID::D32_FLOAT_S8X24_UINT: 247 stagingDepthBufferFormatId = angle::FormatID::D32_FLOAT; 248 stagingStencilBufferFormatId = angle::FormatID::S8_UINT; 249 break; 250 default: 251 ANGLE_MTL_UNREACHABLE(contextMtl); 252 } 253 254 const angle::Format &angleStagingDepthFormat = angle::Format::Get(stagingDepthBufferFormatId); 255 const angle::Format &angleStagingStencilFormat = 256 angle::Format::Get(stagingStencilBufferFormatId); 257 258 size_t stagingDepthBufferRowPitch, stagingStencilBufferRowPitch; 259 size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize; 260 mtl::BufferRef stagingDepthbuffer, stagingStencilBuffer; 261 262 ANGLE_TRY(CopyTextureContentsToStagingBuffer( 263 contextMtl, textureAngleFormat, angleStagingDepthFormat, region.size, data, bytesPerRow, 264 &stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthbuffer)); 265 266 ANGLE_TRY(CopyTextureContentsToStagingBuffer( 267 contextMtl, textureAngleFormat, angleStagingStencilFormat, region.size, data, bytesPerRow, 268 &stagingStencilBufferRowPitch, &stagingStencilBuffer2DImageSize, &stagingStencilBuffer)); 269 270 mtl::BlitCommandEncoder *encoder = contextMtl->getBlitCommandEncoder(); 271 272 encoder->copyBufferToTexture(stagingDepthbuffer, 0, stagingDepthBufferRowPitch, 273 stagingDepthBuffer2DImageSize, region.size, texture, slice, 274 mipmapLevel, region.origin, MTLBlitOptionDepthFromDepthStencil); 275 encoder->copyBufferToTexture(stagingStencilBuffer, 0, stagingStencilBufferRowPitch, 276 stagingStencilBuffer2DImageSize, region.size, texture, slice, 277 mipmapLevel, region.origin, MTLBlitOptionStencilFromDepthStencil); 278 279 return angle::Result::Continue; 280} 281 282angle::Result UploadTextureContents(const gl::Context *context, 283 const mtl::TextureRef &texture, 284 const angle::Format &textureAngleFormat, 285 const MTLRegion ®ion, 286 uint32_t mipmapLevel, 287 uint32_t slice, 288 const uint8_t *data, 289 size_t bytesPerRow) 290{ 291 ASSERT(texture && texture->valid()); 292 ContextMtl *contextMtl = mtl::GetImpl(context); 293 294 if (texture->isCPUAccessible()) 295 { 296 // If texture is CPU accessible, just call replaceRegion() directly. 297 texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow); 298 299 return angle::Result::Continue; 300 } 301 302 // Texture is not CPU accessible, we need to use staging buffer 303 if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits) 304 { 305 ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer( 306 contextMtl, texture, textureAngleFormat, region, mipmapLevel, slice, data, 307 bytesPerRow)); 308 } 309 else 310 { 311 ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, texture, textureAngleFormat, 312 region, mipmapLevel, slice, data, 313 bytesPerRow)); 314 } 315 316 return angle::Result::Continue; 317} 318 319} // namespace 320 321// TextureMtl implementation 322TextureMtl::TextureMtl(const gl::TextureState &state) : TextureImpl(state) {} 323 324TextureMtl::~TextureMtl() = default; 325 326void TextureMtl::onDestroy(const gl::Context *context) 327{ 328 releaseTexture(true); 329} 330 331void TextureMtl::releaseTexture(bool releaseImages) 332{ 333 mFormat = mtl::Format(); 334 335 mNativeTexture = nullptr; 336 mMetalSamplerState = nil; 337 338 for (RenderTargetMtl &rt : mLayeredRenderTargets) 339 { 340 rt.set(nullptr); 341 } 342 343 if (releaseImages) 344 { 345 mTexImages.clear(); 346 } 347 348 mLayeredTextureViews.clear(); 349 350 mIsPow2 = false; 351} 352 353angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) 354{ 355 if (mNativeTexture) 356 { 357 return angle::Result::Continue; 358 } 359 360 ContextMtl *contextMtl = mtl::GetImpl(context); 361 362 // Create actual texture object: 363 int layers = 0; 364 const GLuint mips = mState.getMipmapMaxLevel() + 1; 365 const gl::ImageDesc &desc = mState.getBaseLevelDesc(); 366 367 mIsPow2 = gl::isPow2(desc.size.width) && gl::isPow2(desc.size.height); 368 369 switch (mState.getType()) 370 { 371 case gl::TextureType::_2D: 372 layers = 1; 373 ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mFormat, desc.size.width, 374 desc.size.height, mips, false, true, 375 &mNativeTexture)); 376 mLayeredRenderTargets.resize(1); 377 mLayeredRenderTargets[0].set(mNativeTexture, 0, 0, mFormat); 378 mLayeredTextureViews.resize(1); 379 mLayeredTextureViews[0] = mNativeTexture; 380 break; 381 case gl::TextureType::CubeMap: 382 layers = 6; 383 ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, desc.size.width, mips, 384 false, true, &mNativeTexture)); 385 mLayeredRenderTargets.resize(gl::kCubeFaceCount); 386 mLayeredTextureViews.resize(gl::kCubeFaceCount); 387 for (uint32_t f = 0; f < gl::kCubeFaceCount; ++f) 388 { 389 mLayeredTextureViews[f] = mNativeTexture->createCubeFaceView(f); 390 mLayeredRenderTargets[f].set(mLayeredTextureViews[f], 0, 0, mFormat); 391 } 392 break; 393 default: 394 UNREACHABLE(); 395 } 396 397 ANGLE_TRY(checkForEmulatedChannels(context, mFormat, mNativeTexture)); 398 399 // Transfer data from images to actual texture object 400 mtl::BlitCommandEncoder *encoder = nullptr; 401 for (int layer = 0; layer < layers; ++layer) 402 { 403 for (GLuint mip = 0; mip < mips; ++mip) 404 { 405 mtl::TextureRef &imageToTransfer = mTexImages[layer][mip]; 406 407 // Only transfer if this mip & slice image has been defined and in correct size & 408 // format. 409 gl::Extents actualMipSize = mNativeTexture->size(mip); 410 if (imageToTransfer && imageToTransfer->size() == actualMipSize && 411 imageToTransfer->pixelFormat() == mNativeTexture->pixelFormat()) 412 { 413 MTLSize mtlSize = 414 MTLSizeMake(actualMipSize.width, actualMipSize.height, actualMipSize.depth); 415 MTLOrigin mtlOrigin = MTLOriginMake(0, 0, 0); 416 417 if (!encoder) 418 { 419 encoder = contextMtl->getBlitCommandEncoder(); 420 } 421 encoder->copyTexture(mNativeTexture, layer, mip, mtlOrigin, mtlSize, 422 imageToTransfer, 0, 0, mtlOrigin); 423 } 424 425 imageToTransfer = nullptr; 426 // Make this image the actual texture object's view at this mip and slice. 427 // So that in future, glTexSubImage* will update the actual texture 428 // directly. 429 mTexImages[layer][mip] = mNativeTexture->createSliceMipView(layer, mip); 430 } 431 } 432 433 // Create sampler state 434 ANGLE_TRY(ensureSamplerStateCreated(context)); 435 436 return angle::Result::Continue; 437} 438 439angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context) 440{ 441 if (mMetalSamplerState) 442 { 443 return angle::Result::Continue; 444 } 445 446 ContextMtl *contextMtl = mtl::GetImpl(context); 447 DisplayMtl *displayMtl = contextMtl->getDisplay(); 448 449 mtl::SamplerDesc samplerDesc(mState.getSamplerState()); 450 451 if (mFormat.actualAngleFormat().depthBits && 452 !displayMtl->getFeatures().hasDepthTextureFiltering.enabled) 453 { 454 // On devices not supporting filtering for depth textures, we need to convert to nearest 455 // here. 456 samplerDesc.minFilter = MTLSamplerMinMagFilterNearest; 457 samplerDesc.magFilter = MTLSamplerMinMagFilterNearest; 458 if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped) 459 { 460 samplerDesc.mipFilter = MTLSamplerMipFilterNearest; 461 } 462 463 samplerDesc.maxAnisotropy = 1; 464 } 465 466 mMetalSamplerState = 467 displayMtl->getStateCache().getSamplerState(displayMtl->getMetalDevice(), samplerDesc); 468 469 return angle::Result::Continue; 470} 471 472angle::Result TextureMtl::ensureImageCreated(const gl::Context *context, 473 const gl::ImageIndex &index) 474{ 475 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 476 if (!image) 477 { 478 // Image at this level hasn't been defined yet. We need to define it: 479 const gl::ImageDesc &desc = mState.getImageDesc(index); 480 ANGLE_TRY(redefineImage(context, index, mFormat, desc.size)); 481 } 482 return angle::Result::Continue; 483} 484 485angle::Result TextureMtl::setImage(const gl::Context *context, 486 const gl::ImageIndex &index, 487 GLenum internalFormat, 488 const gl::Extents &size, 489 GLenum format, 490 GLenum type, 491 const gl::PixelUnpackState &unpack, 492 gl::Buffer *unpackBuffer, 493 const uint8_t *pixels) 494{ 495 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type); 496 497 return setImageImpl(context, index, formatInfo, size, type, unpack, pixels); 498} 499 500angle::Result TextureMtl::setSubImage(const gl::Context *context, 501 const gl::ImageIndex &index, 502 const gl::Box &area, 503 GLenum format, 504 GLenum type, 505 const gl::PixelUnpackState &unpack, 506 gl::Buffer *unpackBuffer, 507 const uint8_t *pixels) 508{ 509 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type); 510 511 return setSubImageImpl(context, index, area, formatInfo, type, unpack, pixels); 512} 513 514angle::Result TextureMtl::setCompressedImage(const gl::Context *context, 515 const gl::ImageIndex &index, 516 GLenum internalFormat, 517 const gl::Extents &size, 518 const gl::PixelUnpackState &unpack, 519 size_t imageSize, 520 const uint8_t *pixels) 521{ 522 const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat); 523 524 return setImageImpl(context, index, formatInfo, size, GL_UNSIGNED_BYTE, unpack, pixels); 525} 526 527angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context, 528 const gl::ImageIndex &index, 529 const gl::Box &area, 530 GLenum format, 531 const gl::PixelUnpackState &unpack, 532 size_t imageSize, 533 const uint8_t *pixels) 534{ 535 536 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE); 537 538 return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, pixels); 539} 540 541angle::Result TextureMtl::copyImage(const gl::Context *context, 542 const gl::ImageIndex &index, 543 const gl::Rectangle &sourceArea, 544 GLenum internalFormat, 545 gl::Framebuffer *source) 546{ 547 gl::Extents newImageSize(sourceArea.width, sourceArea.height, 1); 548 const gl::InternalFormat &internalFormatInfo = 549 gl::GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE); 550 551 ContextMtl *contextMtl = mtl::GetImpl(context); 552 angle::FormatID angleFormatId = 553 angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat); 554 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 555 556 ANGLE_TRY(redefineImage(context, index, mtlFormat, newImageSize)); 557 558 if (context->isWebGL()) 559 { 560 ANGLE_TRY(initializeContents(context, index)); 561 } 562 563 return copySubImageImpl(context, index, gl::Offset(0, 0, 0), sourceArea, internalFormatInfo, 564 source); 565} 566 567angle::Result TextureMtl::copySubImage(const gl::Context *context, 568 const gl::ImageIndex &index, 569 const gl::Offset &destOffset, 570 const gl::Rectangle &sourceArea, 571 gl::Framebuffer *source) 572{ 573 const gl::InternalFormat ¤tFormat = *mState.getImageDesc(index).format.info; 574 return copySubImageImpl(context, index, destOffset, sourceArea, currentFormat, source); 575} 576 577angle::Result TextureMtl::copyTexture(const gl::Context *context, 578 const gl::ImageIndex &index, 579 GLenum internalFormat, 580 GLenum type, 581 size_t sourceLevel, 582 bool unpackFlipY, 583 bool unpackPremultiplyAlpha, 584 bool unpackUnmultiplyAlpha, 585 const gl::Texture *source) 586{ 587 UNIMPLEMENTED(); 588 589 return angle::Result::Stop; 590} 591 592angle::Result TextureMtl::copySubTexture(const gl::Context *context, 593 const gl::ImageIndex &index, 594 const gl::Offset &destOffset, 595 size_t sourceLevel, 596 const gl::Box &sourceBox, 597 bool unpackFlipY, 598 bool unpackPremultiplyAlpha, 599 bool unpackUnmultiplyAlpha, 600 const gl::Texture *source) 601{ 602 UNIMPLEMENTED(); 603 604 return angle::Result::Stop; 605} 606 607angle::Result TextureMtl::copyCompressedTexture(const gl::Context *context, 608 const gl::Texture *source) 609{ 610 UNIMPLEMENTED(); 611 612 return angle::Result::Stop; 613} 614 615angle::Result TextureMtl::setStorage(const gl::Context *context, 616 gl::TextureType type, 617 size_t mipmaps, 618 GLenum internalFormat, 619 const gl::Extents &size) 620{ 621 ContextMtl *contextMtl = mtl::GetImpl(context); 622 const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat); 623 angle::FormatID angleFormatId = 624 angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat); 625 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 626 627 return setStorageImpl(context, type, mipmaps, mtlFormat, size); 628} 629 630angle::Result TextureMtl::setStorageExternalMemory(const gl::Context *context, 631 gl::TextureType type, 632 size_t levels, 633 GLenum internalFormat, 634 const gl::Extents &size, 635 gl::MemoryObject *memoryObject, 636 GLuint64 offset) 637{ 638 UNIMPLEMENTED(); 639 640 return angle::Result::Stop; 641} 642 643angle::Result TextureMtl::setStorageMultisample(const gl::Context *context, 644 gl::TextureType type, 645 GLsizei samples, 646 GLint internalformat, 647 const gl::Extents &size, 648 bool fixedSampleLocations) 649{ 650 UNIMPLEMENTED(); 651 652 return angle::Result::Stop; 653} 654 655angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context, 656 gl::TextureType type, 657 egl::Image *image) 658{ 659 UNIMPLEMENTED(); 660 661 return angle::Result::Stop; 662} 663 664angle::Result TextureMtl::setImageExternal(const gl::Context *context, 665 gl::TextureType type, 666 egl::Stream *stream, 667 const egl::Stream::GLTextureDescription &desc) 668{ 669 UNIMPLEMENTED(); 670 return angle::Result::Stop; 671} 672 673angle::Result TextureMtl::generateMipmap(const gl::Context *context) 674{ 675 ANGLE_TRY(ensureTextureCreated(context)); 676 677 ContextMtl *contextMtl = mtl::GetImpl(context); 678 if (!mNativeTexture) 679 { 680 return angle::Result::Continue; 681 } 682 683 const gl::TextureCapsMap &textureCapsMap = contextMtl->getNativeTextureCaps(); 684 const gl::TextureCaps &textureCaps = textureCapsMap.get(mFormat.intendedFormatId); 685 686 if (textureCaps.filterable && textureCaps.renderbuffer) 687 { 688 mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder(); 689 blitEncoder->generateMipmapsForTexture(mNativeTexture); 690 } 691 else 692 { 693 ANGLE_TRY(generateMipmapCPU(context)); 694 } 695 696 return angle::Result::Continue; 697} 698 699angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context) 700{ 701 ASSERT(mNativeTexture && mNativeTexture->valid()); 702 ASSERT(mLayeredTextureViews.size() <= std::numeric_limits<uint32_t>::max()); 703 uint32_t layers = static_cast<uint32_t>(mLayeredTextureViews.size()); 704 705 ContextMtl *contextMtl = mtl::GetImpl(context); 706 const angle::Format &angleFormat = mFormat.actualAngleFormat(); 707 // This format must have mip generation function. 708 ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction); 709 710 // NOTE(hqle): Support base level of ES 3.0. 711 for (uint32_t layer = 0; layer < layers; ++layer) 712 { 713 int maxMipLevel = static_cast<int>(mNativeTexture->mipmapLevels()) - 1; 714 int firstLevel = 0; 715 716 uint32_t prevLevelWidth = mNativeTexture->width(); 717 uint32_t prevLevelHeight = mNativeTexture->height(); 718 size_t prevLevelRowPitch = angleFormat.pixelBytes * prevLevelWidth; 719 std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow) 720 uint8_t[prevLevelRowPitch * prevLevelHeight]); 721 ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData); 722 std::unique_ptr<uint8_t[]> dstLevelData; 723 724 // Download base level data 725 mLayeredTextureViews[layer]->getBytes( 726 contextMtl, prevLevelRowPitch, MTLRegionMake2D(0, 0, prevLevelWidth, prevLevelHeight), 727 firstLevel, prevLevelData.get()); 728 729 for (int mip = firstLevel + 1; mip <= maxMipLevel; ++mip) 730 { 731 uint32_t dstWidth = mNativeTexture->width(mip); 732 uint32_t dstHeight = mNativeTexture->height(mip); 733 734 size_t dstRowPitch = angleFormat.pixelBytes * dstWidth; 735 size_t dstDataSize = dstRowPitch * dstHeight; 736 if (!dstLevelData) 737 { 738 // Allocate once and reuse the buffer 739 dstLevelData.reset(new (std::nothrow) uint8_t[dstDataSize]); 740 ANGLE_CHECK_GL_ALLOC(contextMtl, dstLevelData); 741 } 742 743 // Generate mip level 744 angleFormat.mipGenerationFunction(prevLevelWidth, prevLevelHeight, 1, 745 prevLevelData.get(), prevLevelRowPitch, 0, 746 dstLevelData.get(), dstRowPitch, 0); 747 748 // Upload to texture 749 ANGLE_TRY(UploadTextureContents(context, mNativeTexture, angleFormat, 750 MTLRegionMake2D(0, 0, dstWidth, dstHeight), mip, layer, 751 dstLevelData.get(), dstRowPitch)); 752 753 prevLevelWidth = dstWidth; 754 prevLevelHeight = dstHeight; 755 prevLevelRowPitch = dstRowPitch; 756 std::swap(prevLevelData, dstLevelData); 757 } // for mip level 758 759 } // For layers 760 761 return angle::Result::Continue; 762} 763 764angle::Result TextureMtl::setBaseLevel(const gl::Context *context, GLuint baseLevel) 765{ 766 // NOTE(hqle): ES 3.0 767 UNIMPLEMENTED(); 768 769 return angle::Result::Stop; 770} 771 772angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface *surface) 773{ 774 UNIMPLEMENTED(); 775 776 return angle::Result::Stop; 777} 778 779angle::Result TextureMtl::releaseTexImage(const gl::Context *context) 780{ 781 UNIMPLEMENTED(); 782 783 return angle::Result::Stop; 784} 785 786angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context, 787 GLenum binding, 788 const gl::ImageIndex &imageIndex, 789 GLsizei samples, 790 FramebufferAttachmentRenderTarget **rtOut) 791{ 792 ANGLE_TRY(ensureTextureCreated(context)); 793 // NOTE(hqle): Support MSAA. 794 // Non-zero mip level attachments are an ES 3.0 feature. 795 ASSERT(imageIndex.getLevelIndex() == 0); 796 797 ContextMtl *contextMtl = mtl::GetImpl(context); 798 ANGLE_MTL_TRY(contextMtl, mNativeTexture); 799 800 switch (imageIndex.getType()) 801 { 802 case gl::TextureType::_2D: 803 *rtOut = &mLayeredRenderTargets[0]; 804 break; 805 case gl::TextureType::CubeMap: 806 *rtOut = &mLayeredRenderTargets[imageIndex.cubeMapFaceIndex()]; 807 break; 808 default: 809 UNREACHABLE(); 810 } 811 812 return angle::Result::Continue; 813} 814 815angle::Result TextureMtl::syncState(const gl::Context *context, 816 const gl::Texture::DirtyBits &dirtyBits) 817{ 818 if (dirtyBits.any()) 819 { 820 // Invalidate sampler state 821 mMetalSamplerState = nil; 822 } 823 824 ANGLE_TRY(ensureTextureCreated(context)); 825 ANGLE_TRY(ensureSamplerStateCreated(context)); 826 827 return angle::Result::Continue; 828} 829 830angle::Result TextureMtl::bindVertexShader(const gl::Context *context, 831 mtl::RenderCommandEncoder *cmdEncoder, 832 int textureSlotIndex, 833 int samplerSlotIndex) 834{ 835 ASSERT(mNativeTexture); 836 // ES 2.0: non power of two texture won't have any mipmap. 837 // We don't support OES_texture_npot atm. 838 float maxLodClamp = FLT_MAX; 839 if (!mIsPow2) 840 { 841 maxLodClamp = 0; 842 } 843 844 cmdEncoder->setVertexTexture(mNativeTexture, textureSlotIndex); 845 cmdEncoder->setVertexSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex); 846 847 return angle::Result::Continue; 848} 849 850angle::Result TextureMtl::bindFragmentShader(const gl::Context *context, 851 mtl::RenderCommandEncoder *cmdEncoder, 852 int textureSlotIndex, 853 int samplerSlotIndex) 854{ 855 ASSERT(mNativeTexture); 856 // ES 2.0: non power of two texture won't have any mipmap. 857 // We don't support OES_texture_npot atm. 858 float maxLodClamp = FLT_MAX; 859 if (!mIsPow2) 860 { 861 maxLodClamp = 0; 862 } 863 864 cmdEncoder->setFragmentTexture(mNativeTexture, textureSlotIndex); 865 cmdEncoder->setFragmentSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex); 866 867 return angle::Result::Continue; 868} 869 870angle::Result TextureMtl::redefineImage(const gl::Context *context, 871 const gl::ImageIndex &index, 872 const mtl::Format &mtlFormat, 873 const gl::Extents &size) 874{ 875 if (mNativeTexture) 876 { 877 if (mNativeTexture->valid()) 878 { 879 // Calculate the expected size for the index we are defining. If the size is different 880 // from the given size, or the format is different, we are redefining the image so we 881 // must release it. 882 bool typeChanged = 883 mNativeTexture->textureType() != mtl::GetTextureType(index.getType()); 884 if (mFormat != mtlFormat || size != mNativeTexture->size(index) || typeChanged) 885 { 886 // Keep other images data if texture type hasn't been changed. 887 releaseTexture(typeChanged); 888 } 889 } 890 } 891 892 // Early-out on empty textures, don't create a zero-sized storage. 893 if (size.empty()) 894 { 895 return angle::Result::Continue; 896 } 897 898 ContextMtl *contextMtl = mtl::GetImpl(context); 899 // Cache last defined image format: 900 mFormat = mtlFormat; 901 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 902 903 // If actual texture exists, it means the size hasn't been changed, no need to create new image 904 if (mNativeTexture && image) 905 { 906 ASSERT(image->textureType() == mtl::GetTextureType(GetTextureImageType(index.getType())) && 907 image->pixelFormat() == mFormat.metalFormat && image->size() == size); 908 } 909 else 910 { 911 // Create image to hold texture's data at this level & slice: 912 switch (index.getType()) 913 { 914 case gl::TextureType::_2D: 915 case gl::TextureType::CubeMap: 916 ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mtlFormat, size.width, 917 size.height, 1, false, false, &image)); 918 break; 919 default: 920 UNREACHABLE(); 921 } 922 } 923 924 // Make sure emulated channels are properly initialized 925 ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, image)); 926 927 // Tell context to rebind textures 928 contextMtl->invalidateCurrentTextures(); 929 930 return angle::Result::Continue; 931} 932 933// If mipmaps = 0, this function will create full mipmaps texture. 934angle::Result TextureMtl::setStorageImpl(const gl::Context *context, 935 gl::TextureType type, 936 size_t mipmaps, 937 const mtl::Format &mtlFormat, 938 const gl::Extents &size) 939{ 940 if (mNativeTexture) 941 { 942 // Don't need to hold old images data. 943 releaseTexture(true); 944 } 945 946 ContextMtl *contextMtl = mtl::GetImpl(context); 947 948 // Tell context to rebind textures 949 contextMtl->invalidateCurrentTextures(); 950 951 mFormat = mtlFormat; 952 953 // Texture will be created later in ensureTextureCreated() 954 955 return angle::Result::Continue; 956} 957 958angle::Result TextureMtl::setImageImpl(const gl::Context *context, 959 const gl::ImageIndex &index, 960 const gl::InternalFormat &formatInfo, 961 const gl::Extents &size, 962 GLenum type, 963 const gl::PixelUnpackState &unpack, 964 const uint8_t *pixels) 965{ 966 ContextMtl *contextMtl = mtl::GetImpl(context); 967 angle::FormatID angleFormatId = 968 angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat); 969 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 970 971 ANGLE_TRY(redefineImage(context, index, mtlFormat, size)); 972 973 // Early-out on empty textures, don't create a zero-sized storage. 974 if (size.empty()) 975 { 976 return angle::Result::Continue; 977 } 978 979 return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth), 980 formatInfo, type, unpack, pixels); 981} 982 983angle::Result TextureMtl::setSubImageImpl(const gl::Context *context, 984 const gl::ImageIndex &index, 985 const gl::Box &area, 986 const gl::InternalFormat &formatInfo, 987 GLenum type, 988 const gl::PixelUnpackState &unpack, 989 const uint8_t *pixels) 990{ 991 if (!pixels) 992 { 993 return angle::Result::Continue; 994 } 995 996 ASSERT(unpack.skipRows == 0 && unpack.skipPixels == 0 && unpack.skipImages == 0); 997 998 ContextMtl *contextMtl = mtl::GetImpl(context); 999 1000 angle::FormatID angleFormatId = 1001 angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat); 1002 const mtl::Format &mtlSrcFormat = contextMtl->getPixelFormat(angleFormatId); 1003 1004 if (mFormat.metalFormat != mtlSrcFormat.metalFormat) 1005 { 1006 ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION); 1007 } 1008 1009 ANGLE_TRY(ensureImageCreated(context, index)); 1010 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 1011 1012 GLuint sourceRowPitch = 0; 1013 ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment, 1014 unpack.rowLength, &sourceRowPitch)); 1015 // Check if partial image update is supported for this format 1016 if (!formatInfo.supportSubImage()) 1017 { 1018 // area must be the whole mip level 1019 sourceRowPitch = 0; 1020 gl::Extents size = image->size(index); 1021 if (area.x != 0 || area.y != 0 || area.width != size.width || area.height != size.height) 1022 { 1023 ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION); 1024 } 1025 } 1026 1027 // Only 2D/cube texture is supported atm 1028 auto mtlRegion = MTLRegionMake2D(area.x, area.y, area.width, area.height); 1029 1030 const angle::Format &srcAngleFormat = 1031 angle::Format::Get(angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat)); 1032 1033 // If source pixels are luminance or RGB8, we need to convert them to RGBA 1034 if (mFormat.actualFormatId != srcAngleFormat.id) 1035 { 1036 return convertAndSetSubImage(context, index, mtlRegion, formatInfo, srcAngleFormat, 1037 sourceRowPitch, pixels); 1038 } 1039 1040 // Upload to texture 1041 ANGLE_TRY(UploadTextureContents(context, image, mFormat.actualAngleFormat(), mtlRegion, 0, 0, 1042 pixels, sourceRowPitch)); 1043 1044 return angle::Result::Continue; 1045} 1046 1047angle::Result TextureMtl::convertAndSetSubImage(const gl::Context *context, 1048 const gl::ImageIndex &index, 1049 const MTLRegion &mtlArea, 1050 const gl::InternalFormat &internalFormat, 1051 const angle::Format &pixelsFormat, 1052 size_t pixelsRowPitch, 1053 const uint8_t *pixels) 1054{ 1055 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 1056 ASSERT(image && image->valid()); 1057 ASSERT(image->textureType() == MTLTextureType2D); 1058 1059 ContextMtl *contextMtl = mtl::GetImpl(context); 1060 1061 // Create scratch buffer 1062 const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId); 1063 angle::MemoryBuffer conversionRow; 1064 const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width; 1065 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); 1066 1067 MTLRegion mtlRow = mtlArea; 1068 mtlRow.size.height = 1; 1069 const uint8_t *psrc = pixels; 1070 for (NSUInteger r = 0; r < mtlArea.size.height; ++r, psrc += pixelsRowPitch) 1071 { 1072 mtlRow.origin.y = mtlArea.origin.y + r; 1073 1074 // Convert pixels 1075 CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsFormat.pixelBytes, 0, 1076 pixelsFormat.pixelReadFunction, conversionRow.data(), dstRowPitch, 1077 dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction, 1078 internalFormat.format, dstFormat.componentType, mtlRow.size.width, 1, 1, 1079 false, false, false); 1080 1081 // Upload to texture 1082 ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlRow, 0, 0, 1083 conversionRow.data(), dstRowPitch)); 1084 } 1085 1086 return angle::Result::Continue; 1087} 1088 1089angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context, 1090 const mtl::Format &mtlFormat, 1091 const mtl::TextureRef &texture) 1092{ 1093 bool emulatedChannels = false; 1094 MTLColorWriteMask colorWritableMask = GetColorWriteMask(mtlFormat, &emulatedChannels); 1095 texture->setColorWritableMask(colorWritableMask); 1096 1097 // For emulated channels that GL texture intends to not have, 1098 // we need to initialize their content. 1099 if (emulatedChannels) 1100 { 1101 uint32_t mipmaps = texture->mipmapLevels(); 1102 1103 int layers = texture->textureType() == MTLTextureTypeCube ? 6 : 1; 1104 for (int layer = 0; layer < layers; ++layer) 1105 { 1106 auto cubeFace = static_cast<gl::TextureTarget>( 1107 static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + layer); 1108 for (uint32_t mip = 0; mip < mipmaps; ++mip) 1109 { 1110 gl::ImageIndex index; 1111 if (layers > 1) 1112 { 1113 index = gl::ImageIndex::MakeCubeMapFace(cubeFace, mip); 1114 } 1115 else 1116 { 1117 index = gl::ImageIndex::Make2D(mip); 1118 } 1119 1120 ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mFormat, index)); 1121 } 1122 } 1123 } 1124 return angle::Result::Continue; 1125} 1126 1127angle::Result TextureMtl::initializeContents(const gl::Context *context, 1128 const gl::ImageIndex &index) 1129{ 1130 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 1131 return mtl::InitializeTextureContents(context, image, mFormat, GetImageBaseLevelIndex(image)); 1132} 1133 1134angle::Result TextureMtl::copySubImageImpl(const gl::Context *context, 1135 const gl::ImageIndex &index, 1136 const gl::Offset &destOffset, 1137 const gl::Rectangle &sourceArea, 1138 const gl::InternalFormat &internalFormat, 1139 gl::Framebuffer *source) 1140{ 1141 gl::Extents fbSize = source->getReadColorAttachment()->getSize(); 1142 gl::Rectangle clippedSourceArea; 1143 if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height), 1144 &clippedSourceArea)) 1145 { 1146 return angle::Result::Continue; 1147 } 1148 1149 // If negative offsets are given, clippedSourceArea ensures we don't read from those offsets. 1150 // However, that changes the sourceOffset->destOffset mapping. Here, destOffset is shifted by 1151 // the same amount as clipped to correct the error. 1152 const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x, 1153 destOffset.y + clippedSourceArea.y - sourceArea.y, 0); 1154 1155 ANGLE_TRY(ensureImageCreated(context, index)); 1156 1157 if (!mtl::Format::FormatRenderable(mFormat.metalFormat)) 1158 { 1159 return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea, 1160 internalFormat, source); 1161 } 1162 1163 // NOTE(hqle): Use compute shader. 1164 return copySubImageWithDraw(context, index, modifiedDestOffset, clippedSourceArea, 1165 internalFormat, source); 1166} 1167 1168angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context, 1169 const gl::ImageIndex &index, 1170 const gl::Offset &modifiedDestOffset, 1171 const gl::Rectangle &clippedSourceArea, 1172 const gl::InternalFormat &internalFormat, 1173 gl::Framebuffer *source) 1174{ 1175 ContextMtl *contextMtl = mtl::GetImpl(context); 1176 DisplayMtl *displayMtl = contextMtl->getDisplay(); 1177 FramebufferMtl *framebufferMtl = mtl::GetImpl(source); 1178 1179 RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget(); 1180 1181 if (!colorReadRT || !colorReadRT->getTexture()) 1182 { 1183 // Is this an error? 1184 return angle::Result::Continue; 1185 } 1186 1187 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 1188 ASSERT(image && image->valid()); 1189 1190 mtl::RenderCommandEncoder *cmdEncoder = 1191 contextMtl->getRenderCommandEncoder(image, GetImageBaseLevelIndex(image)); 1192 mtl::BlitParams blitParams; 1193 1194 blitParams.dstOffset = modifiedDestOffset; 1195 blitParams.dstColorMask = image->getColorWritableMask(); 1196 1197 blitParams.src = colorReadRT->getTexture(); 1198 blitParams.srcLevel = static_cast<uint32_t>(colorReadRT->getLevelIndex()); 1199 blitParams.srcRect = clippedSourceArea; 1200 blitParams.srcYFlipped = framebufferMtl->flipY(); 1201 blitParams.dstLuminance = internalFormat.isLUMA(); 1202 1203 displayMtl->getUtils().blitWithDraw(context, cmdEncoder, blitParams); 1204 1205 return angle::Result::Continue; 1206} 1207 1208angle::Result TextureMtl::copySubImageCPU(const gl::Context *context, 1209 const gl::ImageIndex &index, 1210 const gl::Offset &modifiedDestOffset, 1211 const gl::Rectangle &clippedSourceArea, 1212 const gl::InternalFormat &internalFormat, 1213 gl::Framebuffer *source) 1214{ 1215 mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()]; 1216 ASSERT(image && image->valid()); 1217 1218 ContextMtl *contextMtl = mtl::GetImpl(context); 1219 FramebufferMtl *framebufferMtl = mtl::GetImpl(source); 1220 RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget(); 1221 1222 if (!colorReadRT || !colorReadRT->getTexture()) 1223 { 1224 // Is this an error? 1225 return angle::Result::Continue; 1226 } 1227 1228 const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId); 1229 const int dstRowPitch = dstFormat.pixelBytes * clippedSourceArea.width; 1230 angle::MemoryBuffer conversionRow; 1231 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); 1232 1233 MTLRegion mtlDstRowArea = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1); 1234 gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1); 1235 1236 for (int r = 0; r < clippedSourceArea.height; ++r) 1237 { 1238 mtlDstRowArea.origin.y = modifiedDestOffset.y + r; 1239 srcRowArea.y = clippedSourceArea.y + r; 1240 1241 PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0); 1242 1243 // Read pixels from framebuffer to memory: 1244 gl::Rectangle flippedSrcRowArea = framebufferMtl->getReadPixelArea(srcRowArea); 1245 ANGLE_TRY(framebufferMtl->readPixelsImpl(context, flippedSrcRowArea, packParams, 1246 framebufferMtl->getColorReadRenderTarget(), 1247 conversionRow.data())); 1248 1249 // Upload to texture 1250 ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlDstRowArea, 0, 0, 1251 conversionRow.data(), dstRowPitch)); 1252 } 1253 1254 return angle::Result::Continue; 1255} 1256} 1257