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 <algorithm> 14#include <initializer_list> 15 16#include "common/Color.h" 17#include "common/MemoryBuffer.h" 18#include "common/debug.h" 19#include "common/mathutil.h" 20#include "image_util/imageformats.h" 21#include "image_util/loadimage.h" 22#include "libANGLE/Surface.h" 23#include "libANGLE/renderer/Format.h" 24#include "libANGLE/renderer/metal/BufferMtl.h" 25#include "libANGLE/renderer/metal/ContextMtl.h" 26#include "libANGLE/renderer/metal/DisplayMtl.h" 27#include "libANGLE/renderer/metal/FrameBufferMtl.h" 28#include "libANGLE/renderer/metal/ImageMtl.h" 29#include "libANGLE/renderer/metal/SamplerMtl.h" 30#include "libANGLE/renderer/metal/SurfaceMtl.h" 31#include "libANGLE/renderer/metal/mtl_common.h" 32#include "libANGLE/renderer/metal/mtl_format_utils.h" 33#include "libANGLE/renderer/metal/mtl_utils.h" 34#include "libANGLE/renderer/renderer_utils.h" 35 36namespace rx 37{ 38 39namespace 40{ 41 42gl::ImageIndex GetZeroLevelIndex(const mtl::TextureRef &image) 43{ 44 switch (image->textureType()) 45 { 46 case MTLTextureType2D: 47 return gl::ImageIndex::Make2D(0); 48 case MTLTextureTypeCube: 49 return gl::ImageIndex::MakeFromType(gl::TextureType::CubeMap, 0); 50 case MTLTextureType2DArray: 51 return gl::ImageIndex::Make2DArray(0 /** entire layers */); 52 case MTLTextureType2DMultisample: 53 return gl::ImageIndex::Make2DMultisample(); 54 case MTLTextureType3D: 55 return gl::ImageIndex::Make3D(0 /** entire layers */); 56 default: 57 UNREACHABLE(); 58 break; 59 } 60 61 return gl::ImageIndex(); 62} 63 64// Slice is ignored if texture type is not Cube or 2D array 65gl::ImageIndex GetCubeOrArraySliceMipIndex(const mtl::TextureRef &image, 66 uint32_t slice, 67 uint32_t level) 68{ 69 switch (image->textureType()) 70 { 71 case MTLTextureType2D: 72 return gl::ImageIndex::Make2D(level); 73 case MTLTextureTypeCube: 74 { 75 auto cubeFace = static_cast<gl::TextureTarget>( 76 static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + slice); 77 return gl::ImageIndex::MakeCubeMapFace(cubeFace, level); 78 } 79 case MTLTextureType2DArray: 80 return gl::ImageIndex::Make2DArray(level, slice); 81 case MTLTextureType2DMultisample: 82 return gl::ImageIndex::Make2DMultisample(); 83 case MTLTextureType3D: 84 return gl::ImageIndex::Make3D(level); 85 default: 86 UNREACHABLE(); 87 break; 88 } 89 90 return gl::ImageIndex(); 91} 92 93// layer is ignored if texture type is not Cube or 2D array or 3D 94gl::ImageIndex GetLayerMipIndex(const mtl::TextureRef &image, uint32_t layer, uint32_t level) 95{ 96 switch (image->textureType()) 97 { 98 case MTLTextureType2D: 99 return gl::ImageIndex::Make2D(level); 100 case MTLTextureTypeCube: 101 { 102 auto cubeFace = static_cast<gl::TextureTarget>( 103 static_cast<int>(gl::TextureTarget::CubeMapPositiveX) + layer); 104 return gl::ImageIndex::MakeCubeMapFace(cubeFace, level); 105 } 106 case MTLTextureType2DArray: 107 return gl::ImageIndex::Make2DArray(level, layer); 108 case MTLTextureType2DMultisample: 109 return gl::ImageIndex::Make2DMultisample(); 110 case MTLTextureType3D: 111 return gl::ImageIndex::Make3D(level, layer); 112 default: 113 UNREACHABLE(); 114 break; 115 } 116 117 return gl::ImageIndex(); 118} 119 120GLuint GetImageLayerIndexFrom(const gl::ImageIndex &index) 121{ 122 switch (index.getType()) 123 { 124 case gl::TextureType::_2D: 125 case gl::TextureType::_2DMultisample: 126 case gl::TextureType::Rectangle: 127 return 0; 128 case gl::TextureType::CubeMap: 129 return index.cubeMapFaceIndex(); 130 case gl::TextureType::_2DArray: 131 case gl::TextureType::_3D: 132 return index.getLayerIndex(); 133 default: 134 UNREACHABLE(); 135 } 136 137 return 0; 138} 139 140GLuint GetImageCubeFaceIndexOrZeroFrom(const gl::ImageIndex &index) 141{ 142 switch (index.getType()) 143 { 144 case gl::TextureType::CubeMap: 145 return index.cubeMapFaceIndex(); 146 default: 147 break; 148 } 149 150 return 0; 151} 152 153// Given texture type, get texture type of one image for a glTexImage call. 154// For example, for texture 2d, one image is also texture 2d. 155// for texture cube, one image is texture 2d. 156gl::TextureType GetTextureImageType(gl::TextureType texType) 157{ 158 switch (texType) 159 { 160 case gl::TextureType::CubeMap: 161 return gl::TextureType::_2D; 162 case gl::TextureType::_2D: 163 case gl::TextureType::_2DArray: 164 case gl::TextureType::_2DMultisample: 165 case gl::TextureType::_3D: 166 case gl::TextureType::Rectangle: 167 return texType; 168 default: 169 UNREACHABLE(); 170 return gl::TextureType::InvalidEnum; 171 } 172} 173 174// D24X8 by default writes depth data to high 24 bits of 32 bit integers. However, Metal separate 175// depth stencil blitting expects depth data to be in low 24 bits of the data. 176void WriteDepthStencilToDepth24(const uint8_t *srcPtr, uint8_t *dstPtr) 177{ 178 auto src = reinterpret_cast<const angle::DepthStencil *>(srcPtr); 179 auto dst = reinterpret_cast<uint32_t *>(dstPtr); 180 *dst = gl::floatToNormalized<24, uint32_t>(static_cast<float>(src->depth)); 181} 182 183void CopyTextureData(const MTLSize ®ionSize, 184 size_t srcRowPitch, 185 size_t src2DImageSize, 186 const uint8_t *psrc, 187 size_t destRowPitch, 188 size_t dest2DImageSize, 189 uint8_t *pdst) 190{ 191 { 192 size_t rowCopySize = std::min(srcRowPitch, destRowPitch); 193 for (NSUInteger d = 0; d < regionSize.depth; ++d) 194 { 195 for (NSUInteger r = 0; r < regionSize.height; ++r) 196 { 197 const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch; 198 uint8_t *pCopyDst = pdst + d * dest2DImageSize + r * destRowPitch; 199 memcpy(pCopyDst, pCopySrc, rowCopySize); 200 } 201 } 202 } 203} 204 205void ConvertDepthStencilData(const MTLSize ®ionSize, 206 const angle::Format &srcAngleFormat, 207 size_t srcRowPitch, 208 size_t src2DImageSize, 209 const uint8_t *psrc, 210 const angle::Format &dstAngleFormat, 211 rx::PixelWriteFunction pixelWriteFunctionOverride, 212 size_t destRowPitch, 213 size_t dest2DImageSize, 214 uint8_t *pdst) 215{ 216 if (srcAngleFormat.id == dstAngleFormat.id) 217 { 218 size_t rowCopySize = std::min(srcRowPitch, destRowPitch); 219 for (NSUInteger d = 0; d < regionSize.depth; ++d) 220 { 221 for (NSUInteger r = 0; r < regionSize.height; ++r) 222 { 223 const uint8_t *pCopySrc = psrc + d * src2DImageSize + r * srcRowPitch; 224 uint8_t *pCopyDst = pdst + d * dest2DImageSize + r * destRowPitch; 225 memcpy(pCopyDst, pCopySrc, rowCopySize); 226 } 227 } 228 } 229 else 230 { 231 rx::PixelWriteFunction pixelWriteFunction = pixelWriteFunctionOverride 232 ? pixelWriteFunctionOverride 233 : dstAngleFormat.pixelWriteFunction; 234 // This is only for depth & stencil case. 235 ASSERT(srcAngleFormat.depthBits || srcAngleFormat.stencilBits); 236 ASSERT(srcAngleFormat.pixelReadFunction && pixelWriteFunction); 237 238 // cache to store read result of source pixel 239 angle::DepthStencil depthStencilData; 240 auto sourcePixelReadData = reinterpret_cast<uint8_t *>(&depthStencilData); 241 ASSERT(srcAngleFormat.pixelBytes <= sizeof(depthStencilData)); 242 243 for (NSUInteger d = 0; d < regionSize.depth; ++d) 244 { 245 for (NSUInteger r = 0; r < regionSize.height; ++r) 246 { 247 for (NSUInteger c = 0; c < regionSize.width; ++c) 248 { 249 const uint8_t *sourcePixelData = 250 psrc + d * src2DImageSize + r * srcRowPitch + c * srcAngleFormat.pixelBytes; 251 252 uint8_t *destPixelData = pdst + d * dest2DImageSize + r * destRowPitch + 253 c * dstAngleFormat.pixelBytes; 254 255 srcAngleFormat.pixelReadFunction(sourcePixelData, sourcePixelReadData); 256 pixelWriteFunction(sourcePixelReadData, destPixelData); 257 } 258 } 259 } 260 } 261} 262 263mtl::BlitCommandEncoder *GetBlitCommandEncoderForResources( 264 ContextMtl *contextMtl, 265 const std::initializer_list<const mtl::Resource *> &resources) 266{ 267 if (std::none_of(resources.begin(), resources.end(), [contextMtl](const mtl::Resource *res) { 268 return res->hasPendingRenderWorks(contextMtl); 269 })) 270 { 271 // If no resource has pending render works waiting to be submitted, then it's safe to 272 // create a blit encoder without ending current render pass. The blit commands 273 // will run before any pending render commands. 274 return contextMtl->getBlitCommandEncoderWithoutEndingRenderEncoder(); 275 } 276 return contextMtl->getBlitCommandEncoder(); 277} 278 279angle::Result CopyDepthStencilTextureContentsToStagingBuffer( 280 ContextMtl *contextMtl, 281 const angle::Format &textureAngleFormat, 282 const angle::Format &stagingAngleFormat, 283 rx::PixelWriteFunction pixelWriteFunctionOverride, 284 const MTLSize ®ionSize, 285 const uint8_t *data, 286 size_t bytesPerRow, 287 size_t bytesPer2DImage, 288 size_t *bufferRowPitchOut, 289 size_t *buffer2DImageSizeOut, 290 mtl::BufferRef *bufferOut) 291{ 292 size_t stagingBufferRowPitch = regionSize.width * stagingAngleFormat.pixelBytes; 293 size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height; 294 size_t stagingBufferSize = stagingBuffer2DImageSize * regionSize.depth; 295 mtl::BufferRef stagingBuffer; 296 ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer)); 297 298 uint8_t *pdst = stagingBuffer->map(contextMtl); 299 300 ConvertDepthStencilData(regionSize, textureAngleFormat, bytesPerRow, bytesPer2DImage, data, 301 stagingAngleFormat, pixelWriteFunctionOverride, stagingBufferRowPitch, 302 stagingBuffer2DImageSize, pdst); 303 304 stagingBuffer->unmap(contextMtl); 305 306 *bufferOut = stagingBuffer; 307 *bufferRowPitchOut = stagingBufferRowPitch; 308 *buffer2DImageSizeOut = stagingBuffer2DImageSize; 309 310 return angle::Result::Continue; 311} 312 313angle::Result CopyTextureContentsToStagingBuffer(ContextMtl *contextMtl, 314 const angle::Format &textureAngleFormat, 315 const MTLSize ®ionSize, 316 const uint8_t *data, 317 size_t bytesPerRow, 318 size_t bytesPer2DImage, 319 size_t *bufferRowPitchOut, 320 size_t *buffer2DImageSizeOut, 321 mtl::BufferRef *bufferOut) 322{ 323 size_t stagingBufferRowPitch = regionSize.width * textureAngleFormat.pixelBytes; 324 size_t stagingBuffer2DImageSize = stagingBufferRowPitch * regionSize.height; 325 size_t stagingBufferSize = stagingBuffer2DImageSize * regionSize.depth; 326 mtl::BufferRef stagingBuffer; 327 ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer)); 328 329 uint8_t *pdst = stagingBuffer->map(contextMtl); 330 CopyTextureData(regionSize, bytesPerRow, bytesPer2DImage, data, stagingBufferRowPitch, 331 stagingBuffer2DImageSize, pdst); 332 333 stagingBuffer->unmap(contextMtl); 334 335 *bufferOut = stagingBuffer; 336 *bufferRowPitchOut = stagingBufferRowPitch; 337 *buffer2DImageSizeOut = stagingBuffer2DImageSize; 338 339 return angle::Result::Continue; 340} 341 342angle::Result CopyCompressedTextureContentsToStagingBuffer(ContextMtl *contextMtl, 343 const angle::Format &textureAngleFormat, 344 const MTLSize ®ionSizeInBlocks, 345 const uint8_t *data, 346 size_t bytesPerBlockRow, 347 size_t bytesPer2DImage, 348 size_t *bufferRowPitchOut, 349 size_t *buffer2DImageSizeOut, 350 mtl::BufferRef *bufferOut) 351{ 352 size_t stagingBufferRowPitch = bytesPerBlockRow; 353 size_t stagingBuffer2DImageSize = bytesPer2DImage; 354 size_t stagingBufferSize = stagingBuffer2DImageSize * regionSizeInBlocks.depth; 355 mtl::BufferRef stagingBuffer; 356 ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, stagingBufferSize, nullptr, &stagingBuffer)); 357 358 uint8_t *pdst = stagingBuffer->map(contextMtl); 359 CopyTextureData(regionSizeInBlocks, bytesPerBlockRow, bytesPer2DImage, data, 360 stagingBufferRowPitch, stagingBuffer2DImageSize, pdst); 361 362 stagingBuffer->unmap(contextMtl); 363 364 *bufferOut = stagingBuffer; 365 *bufferRowPitchOut = stagingBufferRowPitch; 366 *buffer2DImageSizeOut = stagingBuffer2DImageSize; 367 368 return angle::Result::Continue; 369} 370 371angle::Result SaturateDepth(ContextMtl *contextMtl, 372 mtl::BufferRef srcBuffer, 373 mtl::BufferRef dstBuffer, 374 uint32_t srcBufferOffset, 375 uint32_t srcPitch, 376 MTLSize size) 377{ 378 static_assert(gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE <= UINT_MAX); 379 mtl::DepthSaturationParams params; 380 params.srcBuffer = srcBuffer; 381 params.dstBuffer = dstBuffer; 382 params.srcBufferOffset = srcBufferOffset; 383 params.dstWidth = static_cast<uint32_t>(size.width); 384 params.dstHeight = static_cast<uint32_t>(size.height); 385 params.srcPitch = srcPitch; 386 ANGLE_TRY(contextMtl->getDisplay()->getUtils().saturateDepth(contextMtl, params)); 387 388 return angle::Result::Continue; 389} 390 391// This will copy a buffer to: 392// - the respective level & slice of an original texture if the "dst" texture is a view. 393// - the "dst" texture if it is not a view. 394// Notes: 395// - dstSlice is a slice in the "dst" texture not original texture. 396// - dstLevel is a level in the "dst" texture not original texture. 397// This function is needed because some GPUs such as the ones having AMD Bronze driver 398// have a bug when copying a buffer to a view of a 3D texture. 399void CopyBufferToOriginalTextureIfDstIsAView(ContextMtl *contextMtl, 400 mtl::BlitCommandEncoder *blitEncoder, 401 const mtl::BufferRef &src, 402 size_t srcOffset, 403 size_t srcBytesPerRow, 404 size_t srcBytesPerImage, 405 MTLSize srcSize, 406 const mtl::TextureRef &dst, 407 const uint32_t dstSlice, 408 const mtl::MipmapNativeLevel &dstLevel, 409 MTLOrigin dstOrigin, 410 MTLBlitOption blitOption) 411{ 412 mtl::TextureRef correctedTexture = dst; 413 mtl::MipmapNativeLevel correctedLevel = dstLevel; 414 uint32_t correctedSlice = dstSlice; 415 // TODO(b/343734719): Simulator has bug in parentRelativeSlice() so skip this step 416 // on simulator. 417 if (!contextMtl->getDisplay()->isSimulator() && correctedTexture->parentTexture()) 418 { 419 correctedLevel = correctedLevel + correctedTexture->parentRelativeLevel().get(); 420 correctedSlice += correctedTexture->parentRelativeSlice(); 421 correctedTexture = correctedTexture->parentTexture(); 422 } 423 424 blitEncoder->copyBufferToTexture(src, srcOffset, srcBytesPerRow, srcBytesPerImage, srcSize, 425 correctedTexture, correctedSlice, correctedLevel, dstOrigin, 426 blitOption); 427} 428 429angle::Result UploadDepthStencilTextureContentsWithStagingBuffer( 430 ContextMtl *contextMtl, 431 const angle::Format &textureAngleFormat, 432 MTLRegion region, 433 const mtl::MipmapNativeLevel &mipmapLevel, 434 uint32_t slice, 435 const uint8_t *data, 436 size_t bytesPerRow, 437 size_t bytesPer2DImage, 438 const mtl::TextureRef &texture) 439{ 440 ASSERT(texture && texture->valid()); 441 442 ASSERT(!texture->isCPUAccessible()); 443 444 ASSERT(!textureAngleFormat.depthBits || !textureAngleFormat.stencilBits); 445 446 // Depth and stencil textures cannot be of 3D type; 447 // arrays and cube maps must be uploaded per-slice. 448 ASSERT(region.size.depth == 1); 449 450 // Copy data to staging buffer 451 size_t stagingBufferRowPitch; 452 size_t stagingBuffer2DImageSize; 453 mtl::BufferRef stagingBuffer; 454 ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer( 455 contextMtl, textureAngleFormat, textureAngleFormat, textureAngleFormat.pixelWriteFunction, 456 region.size, data, bytesPerRow, bytesPer2DImage, &stagingBufferRowPitch, 457 &stagingBuffer2DImageSize, &stagingBuffer)); 458 459 if (textureAngleFormat.id == angle::FormatID::D32_FLOAT) 460 { 461 ANGLE_TRY(SaturateDepth(contextMtl, stagingBuffer, stagingBuffer, 0, 462 static_cast<uint32_t>(region.size.width), region.size)); 463 } 464 465 // Copy staging buffer to texture. 466 mtl::BlitCommandEncoder *encoder = 467 GetBlitCommandEncoderForResources(contextMtl, {stagingBuffer.get(), texture.get()}); 468 469 CopyBufferToOriginalTextureIfDstIsAView( 470 contextMtl, encoder, stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize, 471 region.size, texture, slice, mipmapLevel, region.origin, MTLBlitOptionNone); 472 473 return angle::Result::Continue; 474} 475 476// Packed depth stencil upload using staging buffer 477angle::Result UploadPackedDepthStencilTextureContentsWithStagingBuffer( 478 ContextMtl *contextMtl, 479 const angle::Format &textureAngleFormat, 480 MTLRegion region, 481 const mtl::MipmapNativeLevel &mipmapLevel, 482 uint32_t slice, 483 const uint8_t *data, 484 size_t bytesPerRow, 485 size_t bytesPer2DImage, 486 const mtl::TextureRef &texture) 487{ 488 ASSERT(texture && texture->valid()); 489 490 ASSERT(!texture->isCPUAccessible()); 491 492 ASSERT(textureAngleFormat.depthBits && textureAngleFormat.stencilBits); 493 494 // Depth and stencil textures cannot be of 3D type; 495 // arrays and cube maps must be uploaded per-slice. 496 ASSERT(region.size.depth == 1); 497 498 // We have to split the depth & stencil data into 2 buffers. 499 angle::FormatID stagingDepthBufferFormatId; 500 angle::FormatID stagingStencilBufferFormatId; 501 // Custom depth write function. We cannot use those in imageformats.cpp since Metal has some 502 // special cases. 503 rx::PixelWriteFunction stagingDepthBufferWriteFunctionOverride = nullptr; 504 505 switch (textureAngleFormat.id) 506 { 507 case angle::FormatID::D24_UNORM_S8_UINT: 508 // D24_UNORM_X8_UINT writes depth data to high 24 bits. But Metal expects depth data to 509 // be in low 24 bits. 510 stagingDepthBufferFormatId = angle::FormatID::D24_UNORM_X8_UINT; 511 stagingDepthBufferWriteFunctionOverride = WriteDepthStencilToDepth24; 512 stagingStencilBufferFormatId = angle::FormatID::S8_UINT; 513 break; 514 case angle::FormatID::D32_FLOAT_S8X24_UINT: 515 stagingDepthBufferFormatId = angle::FormatID::D32_FLOAT; 516 stagingStencilBufferFormatId = angle::FormatID::S8_UINT; 517 break; 518 default: 519 ANGLE_MTL_UNREACHABLE(contextMtl); 520 } 521 522 const angle::Format &angleStagingDepthFormat = angle::Format::Get(stagingDepthBufferFormatId); 523 const angle::Format &angleStagingStencilFormat = 524 angle::Format::Get(stagingStencilBufferFormatId); 525 526 size_t stagingDepthBufferRowPitch, stagingStencilBufferRowPitch; 527 size_t stagingDepthBuffer2DImageSize, stagingStencilBuffer2DImageSize; 528 mtl::BufferRef stagingDepthBuffer, stagingStencilBuffer; 529 530 // Copy depth data to staging depth buffer 531 ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer( 532 contextMtl, textureAngleFormat, angleStagingDepthFormat, 533 stagingDepthBufferWriteFunctionOverride, region.size, data, bytesPerRow, bytesPer2DImage, 534 &stagingDepthBufferRowPitch, &stagingDepthBuffer2DImageSize, &stagingDepthBuffer)); 535 536 // Copy stencil data to staging stencil buffer 537 ANGLE_TRY(CopyDepthStencilTextureContentsToStagingBuffer( 538 contextMtl, textureAngleFormat, angleStagingStencilFormat, nullptr, region.size, data, 539 bytesPerRow, bytesPer2DImage, &stagingStencilBufferRowPitch, 540 &stagingStencilBuffer2DImageSize, &stagingStencilBuffer)); 541 542 if (angleStagingDepthFormat.id == angle::FormatID::D32_FLOAT) 543 { 544 ANGLE_TRY(SaturateDepth(contextMtl, stagingDepthBuffer, stagingDepthBuffer, 0, 545 static_cast<uint32_t>(region.size.width), region.size)); 546 } 547 548 mtl::BlitCommandEncoder *encoder = GetBlitCommandEncoderForResources( 549 contextMtl, {stagingDepthBuffer.get(), stagingStencilBuffer.get(), texture.get()}); 550 551 CopyBufferToOriginalTextureIfDstIsAView( 552 contextMtl, encoder, stagingDepthBuffer, 0, stagingDepthBufferRowPitch, 553 stagingDepthBuffer2DImageSize, region.size, texture, slice, mipmapLevel, region.origin, 554 MTLBlitOptionDepthFromDepthStencil); 555 CopyBufferToOriginalTextureIfDstIsAView( 556 contextMtl, encoder, stagingStencilBuffer, 0, stagingStencilBufferRowPitch, 557 stagingStencilBuffer2DImageSize, region.size, texture, slice, mipmapLevel, region.origin, 558 MTLBlitOptionStencilFromDepthStencil); 559 560 return angle::Result::Continue; 561} 562 563angle::Result UploadTextureContentsWithStagingBuffer(ContextMtl *contextMtl, 564 const angle::Format &textureAngleFormat, 565 MTLRegion region, 566 const mtl::MipmapNativeLevel &mipmapLevel, 567 uint32_t slice, 568 const uint8_t *data, 569 size_t bytesPerRow, 570 size_t bytesPer2DImage, 571 const mtl::TextureRef &texture) 572{ 573 ASSERT(texture && texture->valid()); 574 575 angle::FormatID stagingBufferFormatID = textureAngleFormat.id; 576 const angle::Format &angleStagingFormat = angle::Format::Get(stagingBufferFormatID); 577 578 size_t stagingBufferRowPitch; 579 size_t stagingBuffer2DImageSize; 580 mtl::BufferRef stagingBuffer; 581 582 // Block-compressed formats need a bit of massaging for copy. 583 if (textureAngleFormat.isBlock) 584 { 585 GLenum internalFormat = textureAngleFormat.glInternalFormat; 586 const gl::InternalFormat &fmt = gl::GetSizedInternalFormatInfo(internalFormat); 587 MTLRegion newRegion = region; 588 bytesPerRow = 589 (region.size.width + fmt.compressedBlockWidth - 1) / fmt.compressedBlockWidth * 16; 590 bytesPer2DImage = (region.size.height + fmt.compressedBlockHeight - 1) / 591 fmt.compressedBlockHeight * bytesPerRow; 592 newRegion.size.width = 593 (region.size.width + fmt.compressedBlockWidth - 1) / fmt.compressedBlockWidth; 594 newRegion.size.height = 595 (region.size.height + fmt.compressedBlockHeight - 1) / fmt.compressedBlockHeight; 596 ANGLE_TRY(CopyCompressedTextureContentsToStagingBuffer( 597 contextMtl, angleStagingFormat, newRegion.size, data, bytesPerRow, bytesPer2DImage, 598 &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer)); 599 } 600 // Copy to staging buffer before uploading to texture. 601 else 602 { 603 ANGLE_TRY(CopyTextureContentsToStagingBuffer( 604 contextMtl, angleStagingFormat, region.size, data, bytesPerRow, bytesPer2DImage, 605 &stagingBufferRowPitch, &stagingBuffer2DImageSize, &stagingBuffer)); 606 } 607 mtl::BlitCommandEncoder *encoder = 608 GetBlitCommandEncoderForResources(contextMtl, {stagingBuffer.get(), texture.get()}); 609 610 CopyBufferToOriginalTextureIfDstIsAView( 611 contextMtl, encoder, stagingBuffer, 0, stagingBufferRowPitch, stagingBuffer2DImageSize, 612 region.size, texture, slice, mipmapLevel, region.origin, 0); 613 614 return angle::Result::Continue; 615} 616 617angle::Result UploadTextureContents(const gl::Context *context, 618 const angle::Format &textureAngleFormat, 619 const MTLRegion ®ion, 620 const mtl::MipmapNativeLevel &mipmapLevel, 621 uint32_t slice, 622 const uint8_t *data, 623 size_t bytesPerRow, 624 size_t bytesPer2DImage, 625 bool avoidStagingBuffers, 626 const mtl::TextureRef &texture) 627 628{ 629 ASSERT(texture && texture->valid()); 630 ContextMtl *contextMtl = mtl::GetImpl(context); 631 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(textureAngleFormat.id); 632 633 bool preferGPUInitialization = 634 !avoidStagingBuffers && 635 PreferStagedTextureUploads(context, texture, mtlFormat, mtl::StagingPurpose::Upload); 636 if (texture->isCPUAccessible() && !preferGPUInitialization) 637 { 638 if (mtlFormat.isPVRTC()) 639 { 640 // Replace Region Validation: rowBytes must be 0 641 bytesPerRow = 0; 642 } 643 644 // If texture is CPU accessible, just call replaceRegion() directly. 645 texture->replaceRegion(contextMtl, region, mipmapLevel, slice, data, bytesPerRow, 646 bytesPer2DImage); 647 648 return angle::Result::Continue; 649 } 650 651 // Texture is not CPU accessible or staging is forced due to a workaround 652 if (!textureAngleFormat.depthBits && !textureAngleFormat.stencilBits) 653 { 654 // Upload color data 655 ANGLE_TRY(UploadTextureContentsWithStagingBuffer(contextMtl, textureAngleFormat, region, 656 mipmapLevel, slice, data, bytesPerRow, 657 bytesPer2DImage, texture)); 658 } 659 else if (textureAngleFormat.depthBits && textureAngleFormat.stencilBits) 660 { 661 // Packed depth-stencil 662 ANGLE_TRY(UploadPackedDepthStencilTextureContentsWithStagingBuffer( 663 contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow, 664 bytesPer2DImage, texture)); 665 } 666 else 667 { 668 // Depth or stencil 669 ANGLE_TRY(UploadDepthStencilTextureContentsWithStagingBuffer( 670 contextMtl, textureAngleFormat, region, mipmapLevel, slice, data, bytesPerRow, 671 bytesPer2DImage, texture)); 672 } 673 674 return angle::Result::Continue; 675} 676 677// This might be unused on platform not supporting swizzle. 678ANGLE_APPLE_UNUSED 679GLenum OverrideSwizzleValue(const gl::Context *context, 680 GLenum swizzle, 681 const mtl::Format &format, 682 const gl::InternalFormat &glInternalFormat) 683{ 684 if (format.actualAngleFormat().hasDepthOrStencilBits()) 685 { 686 ASSERT(!format.swizzled); 687 if (context->getState().getClientMajorVersion() >= 3 && glInternalFormat.sized) 688 { 689 // ES 3.1 spec: treat depth and stencil textures as red textures during sampling. 690 if (swizzle == GL_GREEN || swizzle == GL_BLUE) 691 { 692 return GL_NONE; 693 } 694 else if (swizzle == GL_ALPHA) 695 { 696 return GL_ONE; 697 } 698 } 699 else 700 { 701 // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_depth_texture.txt 702 // Treat depth texture as luminance texture during sampling. 703 if (swizzle == GL_GREEN || swizzle == GL_BLUE) 704 { 705 return GL_RED; 706 } 707 else if (swizzle == GL_ALPHA) 708 { 709 return GL_ONE; 710 } 711 } 712 } 713 else if (format.swizzled) 714 { 715 // Combine the swizzles 716 switch (swizzle) 717 { 718 case GL_RED: 719 return format.swizzle[0]; 720 case GL_GREEN: 721 return format.swizzle[1]; 722 case GL_BLUE: 723 return format.swizzle[2]; 724 case GL_ALPHA: 725 return format.swizzle[3]; 726 default: 727 break; 728 } 729 } 730 731 return swizzle; 732} 733 734mtl::TextureRef &GetLayerLevelTextureView( 735 TextureMtl::LayerLevelTextureViewVector *layerLevelTextureViews, 736 uint32_t layer, 737 uint32_t level, 738 uint32_t layerCount, 739 uint32_t levelCount) 740{ 741 // Lazily allocate the full layer and level count to not trigger any std::vector reallocations. 742 if (layerLevelTextureViews->empty()) 743 { 744 layerLevelTextureViews->resize(layerCount); 745 } 746 ASSERT(layerLevelTextureViews->size() > layer); 747 748 TextureMtl::TextureViewVector &levelTextureViews = (*layerLevelTextureViews)[layer]; 749 750 if (levelTextureViews.empty()) 751 { 752 levelTextureViews.resize(levelCount); 753 } 754 ASSERT(levelTextureViews.size() > level); 755 756 return levelTextureViews[level]; 757} 758 759} // namespace 760 761// TextureMtl::NativeTextureWrapper implementation. 762// This class uses GL level instead of mtl::MipmapNativeLevel. 763// It seamlessly translates GL level to native level based on the base GL information passed in the 764// constructor. The base GL level is unchanged thoughout the lifetime of this object. 765// Note that NativeTextureWrapper's base GL level doesn't necessarily mean it's the same as a GL 766// texture's real base level. 767// - If NativeTextureWrapper holds a native storage of a non-immutable texture, 768// its base GL level is indeed equal to the GL texture's base level. 769// - If NativeTextureWrapper holds a native storage of an immutable texture, 770// it base GL level is actually 0. 771// - If NativeTextureWrapper holds a view from base level to max level of a GL texture, 772// then its base GL level is equal to the GL texture's base level. 773class TextureMtl::NativeTextureWrapper : angle::NonCopyable 774{ 775 public: 776 NativeTextureWrapper(mtl::TextureRef texture, GLuint baseGLLevel) 777 : mNativeTexture(std::move(texture)), mBaseGLLevel(baseGLLevel) 778 { 779 ASSERT(mNativeTexture && mNativeTexture->valid()); 780 } 781 782 operator const mtl::TextureRef &() const { return mNativeTexture; } 783 const mtl::TextureRef &getNativeTexture() const { return mNativeTexture; } 784 785 void replaceRegion(ContextMtl *context, 786 const MTLRegion ®ion, 787 GLuint glLevel, 788 uint32_t slice, 789 const uint8_t *data, 790 size_t bytesPerRow, 791 size_t bytesPer2DImage) 792 { 793 mNativeTexture->replaceRegion(context, region, getNativeLevel(glLevel), slice, data, 794 bytesPerRow, bytesPer2DImage); 795 } 796 797 void getBytes(ContextMtl *context, 798 size_t bytesPerRow, 799 size_t bytesPer2DInage, 800 const MTLRegion ®ion, 801 GLuint glLevel, 802 uint32_t slice, 803 uint8_t *dataOut) 804 { 805 mNativeTexture->getBytes(context, bytesPerRow, bytesPer2DInage, region, 806 getNativeLevel(glLevel), slice, dataOut); 807 } 808 809 GLuint getBaseGLLevel() const { return mBaseGLLevel; } 810 // Get max addressable GL level that this texture supports. 811 GLuint getMaxSupportedGLLevel() const { return mBaseGLLevel + mipmapLevels() - 1; } 812 // Check whether a GL level refers to a valid mip in this texture. 813 bool isGLLevelSupported(GLuint glLevel) 814 { 815 return glLevel >= mBaseGLLevel && glLevel <= getMaxSupportedGLLevel(); 816 } 817 mtl::MipmapNativeLevel getNativeLevel(GLuint glLevel) const 818 { 819 return mtl::GetNativeMipLevel(glLevel, mBaseGLLevel); 820 } 821 GLuint getGLLevel(const mtl::MipmapNativeLevel &nativeLevel) const 822 { 823 return mtl::GetGLMipLevel(nativeLevel, mBaseGLLevel); 824 } 825 826 mtl::TextureRef getStencilView() { return mNativeTexture->getStencilView(); } 827 828 MTLTextureType textureType() const { return mNativeTexture->textureType(); } 829 MTLPixelFormat pixelFormat() const { return mNativeTexture->pixelFormat(); } 830 831 uint32_t mipmapLevels() const { return mNativeTexture->mipmapLevels(); } 832 uint32_t arrayLength() const { return mNativeTexture->arrayLength(); } 833 uint32_t cubeFaces() const { return mNativeTexture->cubeFaces(); } 834 uint32_t cubeFacesOrArrayLength() const { return mNativeTexture->cubeFacesOrArrayLength(); } 835 836 uint32_t width(GLuint glLevel) const { return mNativeTexture->width(getNativeLevel(glLevel)); } 837 uint32_t height(GLuint glLevel) const 838 { 839 return mNativeTexture->height(getNativeLevel(glLevel)); 840 } 841 uint32_t depth(GLuint glLevel) const { return mNativeTexture->depth(getNativeLevel(glLevel)); } 842 843 gl::Extents size(GLuint glLevel) const { return mNativeTexture->size(getNativeLevel(glLevel)); } 844 845 // Get width, height, depth, size at base level. 846 uint32_t widthAt0() const { return width(mBaseGLLevel); } 847 uint32_t heightAt0() const { return height(mBaseGLLevel); } 848 uint32_t depthAt0() const { return depth(mBaseGLLevel); } 849 gl::Extents sizeAt0() const { return size(mBaseGLLevel); } 850 851 protected: 852 mtl::TextureRef mNativeTexture; 853 const GLuint mBaseGLLevel; 854}; 855 856// This class extends NativeTextureWrapper with support for view creation 857class TextureMtl::NativeTextureWrapperWithViewSupport : public NativeTextureWrapper 858{ 859 public: 860 NativeTextureWrapperWithViewSupport(mtl::TextureRef texture, GLuint baseGLLevel) 861 : NativeTextureWrapper(std::move(texture), baseGLLevel) 862 {} 863 864 // Create a view of one slice at a level. 865 mtl::TextureRef createSliceMipView(uint32_t slice, GLuint glLevel) 866 { 867 return mNativeTexture->createSliceMipView(slice, getNativeLevel(glLevel)); 868 } 869 // Create a levels range view 870 mtl::TextureRef createMipsView(GLuint glLevel, uint32_t levels) 871 { 872 return mNativeTexture->createMipsView(getNativeLevel(glLevel), levels); 873 } 874 // Create a view of a level. 875 mtl::TextureRef createMipView(GLuint glLevel) 876 { 877 return mNativeTexture->createMipView(getNativeLevel(glLevel)); 878 } 879 // Create a view for a shader image binding. 880 mtl::TextureRef createShaderImageView2D(GLuint glLevel, int layer, MTLPixelFormat format) 881 { 882 return mNativeTexture->createShaderImageView2D(getNativeLevel(glLevel), layer, format); 883 } 884 885 // Create a swizzled view 886 mtl::TextureRef createMipsSwizzleView(GLuint glLevel, 887 uint32_t levels, 888 MTLPixelFormat format, 889 const mtl::TextureSwizzleChannels &swizzle) 890 { 891 return mNativeTexture->createMipsSwizzleView(getNativeLevel(glLevel), levels, format, 892 swizzle); 893 } 894}; 895 896// TextureMtl implementation 897TextureMtl::TextureMtl(const gl::TextureState &state) : TextureImpl(state) {} 898 899TextureMtl::~TextureMtl() = default; 900 901void TextureMtl::onDestroy(const gl::Context *context) 902{ 903 releaseTexture(true); 904 mBoundSurface = nullptr; 905} 906 907void TextureMtl::releaseTexture(bool releaseImages) 908{ 909 releaseTexture(releaseImages, false); 910} 911 912void TextureMtl::releaseTexture(bool releaseImages, bool releaseTextureObjectsOnly) 913{ 914 915 if (releaseImages) 916 { 917 mTexImageDefs.clear(); 918 mShaderImageViews.clear(); 919 } 920 else if (mNativeTextureStorage) 921 { 922 // Release native texture but keep its old per face per mipmap level image views. 923 retainImageDefinitions(); 924 } 925 926 mNativeTextureStorage = nullptr; 927 mViewFromBaseToMaxLevel = nullptr; 928 mSwizzleStencilSamplingView = nullptr; 929 930 // Clear render target cache for each texture's image. We don't erase them because they 931 // might still be referenced by a framebuffer. 932 for (auto &sliceRenderTargets : mPerLayerRenderTargets) 933 { 934 for (RenderTargetMtl &mipRenderTarget : sliceRenderTargets.second) 935 { 936 mipRenderTarget.reset(); 937 } 938 } 939 940 for (mtl::TextureRef &view : mLevelViewsWithinBaseMax) 941 { 942 view.reset(); 943 } 944 945 if (!releaseTextureObjectsOnly) 946 { 947 mMetalSamplerState = nil; 948 mFormat = mtl::Format(); 949 } 950} 951 952angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) 953{ 954 if (mNativeTextureStorage) 955 { 956 return angle::Result::Continue; 957 } 958 959 // This should not be called from immutable texture. 960 ASSERT(!isImmutableOrPBuffer()); 961 962 ContextMtl *contextMtl = mtl::GetImpl(context); 963 964 // Create actual texture object: 965 GLuint mips = mState.getMipmapMaxLevel() - mState.getEffectiveBaseLevel() + 1; 966 gl::ImageDesc desc = mState.getBaseLevelDesc(); 967 ANGLE_MTL_CHECK(contextMtl, desc.format.valid(), GL_INVALID_OPERATION); 968 angle::FormatID angleFormatId = 969 angle::Format::InternalFormatToID(desc.format.info->sizedInternalFormat); 970 mFormat = contextMtl->getPixelFormat(angleFormatId); 971 972 ANGLE_TRY(createNativeTexture(context, mState.getType(), mips, desc.size)); 973 974 // Transfer data from defined images to actual texture object 975 int numCubeFaces = static_cast<int>(mNativeTextureStorage->cubeFaces()); 976 for (int face = 0; face < numCubeFaces; ++face) 977 { 978 for (mtl::MipmapNativeLevel actualMip = mtl::kZeroNativeMipLevel; actualMip.get() < mips; 979 ++actualMip) 980 { 981 GLuint imageMipLevel = mNativeTextureStorage->getGLLevel(actualMip); 982 mtl::TextureRef &imageToTransfer = mTexImageDefs[face][imageMipLevel].image; 983 984 // Only transfer if this mip & slice image has been defined and in correct size & 985 // format. 986 gl::Extents actualMipSize = mNativeTextureStorage->size(imageMipLevel); 987 if (imageToTransfer && imageToTransfer->sizeAt0() == actualMipSize && 988 imageToTransfer->arrayLength() == mNativeTextureStorage->arrayLength() && 989 imageToTransfer->pixelFormat() == mNativeTextureStorage->pixelFormat()) 990 { 991 mtl::BlitCommandEncoder *encoder = GetBlitCommandEncoderForResources( 992 contextMtl, 993 {imageToTransfer.get(), mNativeTextureStorage->getNativeTexture().get()}); 994 995 encoder->copyTexture(imageToTransfer, 0, mtl::kZeroNativeMipLevel, 996 *mNativeTextureStorage, face, actualMip, 997 imageToTransfer->arrayLength(), 1); 998 999 // Invalidate texture image definition at this index so that we can make it a 1000 // view of the native texture at this index later. 1001 imageToTransfer = nullptr; 1002 } 1003 } 1004 } 1005 1006 return angle::Result::Continue; 1007} 1008 1009angle::Result TextureMtl::createNativeTexture(const gl::Context *context, 1010 gl::TextureType type, 1011 GLuint mips, 1012 const gl::Extents &size) 1013{ 1014 ContextMtl *contextMtl = mtl::GetImpl(context); 1015 1016 // Create actual texture object: 1017 mSlices = 1; 1018 bool allowFormatView = mFormat.hasDepthAndStencilBits() || 1019 needsFormatViewForPixelLocalStorage( 1020 contextMtl->getDisplay()->getNativePixelLocalStorageOptions()); 1021 mtl::TextureRef nativeTextureStorage; 1022 switch (type) 1023 { 1024 case gl::TextureType::_2D: 1025 ANGLE_TRY(mtl::Texture::Make2DTexture( 1026 contextMtl, mFormat, size.width, size.height, mips, 1027 /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage)); 1028 break; 1029 case gl::TextureType::CubeMap: 1030 mSlices = 6; 1031 ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, size.width, mips, 1032 /** renderTargetOnly */ false, allowFormatView, 1033 &nativeTextureStorage)); 1034 break; 1035 case gl::TextureType::_3D: 1036 ANGLE_TRY(mtl::Texture::Make3DTexture( 1037 contextMtl, mFormat, size.width, size.height, size.depth, mips, 1038 /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage)); 1039 break; 1040 case gl::TextureType::_2DArray: 1041 mSlices = size.depth; 1042 ANGLE_TRY(mtl::Texture::Make2DArrayTexture( 1043 contextMtl, mFormat, size.width, size.height, mips, mSlices, 1044 /** renderTargetOnly */ false, allowFormatView, &nativeTextureStorage)); 1045 break; 1046 default: 1047 UNREACHABLE(); 1048 } 1049 1050 if (mState.getImmutableFormat()) 1051 { 1052 mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>( 1053 std::move(nativeTextureStorage), /*baseGLLevel=*/0); 1054 } 1055 else 1056 { 1057 mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>( 1058 std::move(nativeTextureStorage), /*baseGLLevel=*/mState.getEffectiveBaseLevel()); 1059 } 1060 1061 ANGLE_TRY(checkForEmulatedChannels(context, mFormat, *mNativeTextureStorage)); 1062 1063 ANGLE_TRY(createViewFromBaseToMaxLevel()); 1064 1065 // Create sampler state 1066 ANGLE_TRY(ensureSamplerStateCreated(context)); 1067 1068 return angle::Result::Continue; 1069} 1070 1071angle::Result TextureMtl::ensureSamplerStateCreated(const gl::Context *context) 1072{ 1073 if (mMetalSamplerState) 1074 { 1075 return angle::Result::Continue; 1076 } 1077 1078 ContextMtl *contextMtl = mtl::GetImpl(context); 1079 1080 mtl::SamplerDesc samplerDesc(mState.getSamplerState()); 1081 1082 if (mFormat.actualAngleFormat().depthBits && !mFormat.getCaps().filterable) 1083 { 1084 // On devices not supporting filtering for depth textures, we need to convert to nearest 1085 // here. 1086 samplerDesc.minFilter = MTLSamplerMinMagFilterNearest; 1087 samplerDesc.magFilter = MTLSamplerMinMagFilterNearest; 1088 if (samplerDesc.mipFilter != MTLSamplerMipFilterNotMipmapped) 1089 { 1090 samplerDesc.mipFilter = MTLSamplerMipFilterNearest; 1091 } 1092 1093 samplerDesc.maxAnisotropy = 1; 1094 } 1095 1096 // OpenGL ES 3.x: The rules for texel selection are modified 1097 // for cube maps so that texture wrap modes are ignored. 1098 if ((mState.getType() == gl::TextureType::CubeMap || 1099 mState.getType() == gl::TextureType::CubeMapArray) && 1100 context->getState().getClientMajorVersion() >= 3) 1101 { 1102 samplerDesc.rAddressMode = MTLSamplerAddressModeClampToEdge; 1103 samplerDesc.sAddressMode = MTLSamplerAddressModeClampToEdge; 1104 samplerDesc.tAddressMode = MTLSamplerAddressModeClampToEdge; 1105 } 1106 mMetalSamplerState = contextMtl->getDisplay()->getStateCache().getSamplerState( 1107 contextMtl->getMetalDevice(), samplerDesc); 1108 1109 return angle::Result::Continue; 1110} 1111 1112angle::Result TextureMtl::createViewFromBaseToMaxLevel() 1113{ 1114 ASSERT(mNativeTextureStorage); 1115 uint32_t maxLevel = 1116 std::min(mNativeTextureStorage->getMaxSupportedGLLevel(), mState.getEffectiveMaxLevel()); 1117 1118 mtl::TextureRef nativeViewFromBaseToMaxLevelRef; 1119 if (maxLevel == mNativeTextureStorage->getMaxSupportedGLLevel() && 1120 mState.getEffectiveBaseLevel() == mNativeTextureStorage->getBaseGLLevel()) 1121 { 1122 // If base & max level are the same in mNativeTextureStorage, we don't need 1123 // a dedicated view. Furthermore, Intel driver has some bugs when sampling a view 1124 // of a stencil texture. 1125 nativeViewFromBaseToMaxLevelRef = mNativeTextureStorage->getNativeTexture(); 1126 } 1127 else 1128 { 1129 uint32_t baseToMaxLevels = maxLevel - mState.getEffectiveBaseLevel() + 1; 1130 nativeViewFromBaseToMaxLevelRef = 1131 mNativeTextureStorage->createMipsView(mState.getEffectiveBaseLevel(), baseToMaxLevels); 1132 } 1133 1134 mViewFromBaseToMaxLevel = std::make_unique<NativeTextureWrapper>( 1135 nativeViewFromBaseToMaxLevelRef, mState.getEffectiveBaseLevel()); 1136 1137 // Recreate in bindToShader() 1138 mSwizzleStencilSamplingView = nullptr; 1139 return angle::Result::Continue; 1140} 1141 1142angle::Result TextureMtl::onBaseMaxLevelsChanged(const gl::Context *context) 1143{ 1144 if (!mNativeTextureStorage) 1145 { 1146 return angle::Result::Continue; 1147 } 1148 1149 if (isImmutableOrPBuffer()) 1150 { 1151 // For immutable texture, only recreate base-max view. 1152 ANGLE_TRY(createViewFromBaseToMaxLevel()); 1153 // Invalidate base-max per level views so that they can be recreated 1154 // in generateMipmap() 1155 for (mtl::TextureRef &view : mLevelViewsWithinBaseMax) 1156 { 1157 view.reset(); 1158 } 1159 return angle::Result::Continue; 1160 } 1161 1162 ContextMtl *contextMtl = mtl::GetImpl(context); 1163 1164 // Release native texture but keep old image definitions so that it can be recreated from old 1165 // image definitions with different base level 1166 releaseTexture(false, true); 1167 1168 // Tell context to rebind textures 1169 contextMtl->invalidateCurrentTextures(); 1170 1171 return angle::Result::Continue; 1172} 1173 1174angle::Result TextureMtl::ensureImageCreated(const gl::Context *context, 1175 const gl::ImageIndex &index) 1176{ 1177 mtl::TextureRef &image = getImage(index); 1178 if (!image) 1179 { 1180 // Image at this level hasn't been defined yet. We need to define it: 1181 const gl::ImageDesc &desc = mState.getImageDesc(index); 1182 ANGLE_TRY(redefineImage(context, index, mFormat, desc.size)); 1183 } 1184 return angle::Result::Continue; 1185} 1186 1187angle::Result TextureMtl::ensureLevelViewsWithinBaseMaxCreated() 1188{ 1189 ASSERT(mViewFromBaseToMaxLevel); 1190 for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel; 1191 mip.get() < mViewFromBaseToMaxLevel->mipmapLevels(); ++mip) 1192 { 1193 if (mLevelViewsWithinBaseMax[mip]) 1194 { 1195 continue; 1196 } 1197 1198 GLuint mipGLLevel = mViewFromBaseToMaxLevel->getGLLevel(mip); 1199 1200 if (mViewFromBaseToMaxLevel->textureType() != MTLTextureTypeCube && 1201 mTexImageDefs[0][mipGLLevel].image) 1202 { 1203 // Reuse texture image view. 1204 mLevelViewsWithinBaseMax[mip] = mTexImageDefs[0][mipGLLevel].image; 1205 } 1206 else 1207 { 1208 mLevelViewsWithinBaseMax[mip] = mNativeTextureStorage->createMipView(mipGLLevel); 1209 } 1210 } 1211 return angle::Result::Continue; 1212} 1213 1214mtl::TextureRef TextureMtl::createImageViewFromTextureStorage(GLuint cubeFaceOrZero, GLuint glLevel) 1215{ 1216 mtl::TextureRef image; 1217 if (mNativeTextureStorage->textureType() == MTLTextureTypeCube) 1218 { 1219 // Cube texture's image is per face. 1220 image = mNativeTextureStorage->createSliceMipView(cubeFaceOrZero, glLevel); 1221 } 1222 else 1223 { 1224 if (mViewFromBaseToMaxLevel->isGLLevelSupported(glLevel)) 1225 { 1226 mtl::MipmapNativeLevel nativeLevel = mViewFromBaseToMaxLevel->getNativeLevel(glLevel); 1227 if (mLevelViewsWithinBaseMax[nativeLevel]) 1228 { 1229 // Reuse the native level view 1230 image = mLevelViewsWithinBaseMax[nativeLevel]; 1231 } 1232 } 1233 1234 if (!image) 1235 { 1236 image = mNativeTextureStorage->createMipView(glLevel); 1237 } 1238 } 1239 1240 return image; 1241} 1242 1243void TextureMtl::retainImageDefinitions() 1244{ 1245 if (!mNativeTextureStorage) 1246 { 1247 return; 1248 } 1249 const GLuint mips = mNativeTextureStorage->mipmapLevels(); 1250 1251 int numCubeFaces = 1; 1252 switch (mState.getType()) 1253 { 1254 case gl::TextureType::CubeMap: 1255 numCubeFaces = 6; 1256 break; 1257 default: 1258 break; 1259 } 1260 1261 // Create image view per cube face, per mip level 1262 for (int face = 0; face < numCubeFaces; ++face) 1263 { 1264 for (mtl::MipmapNativeLevel mip = mtl::kZeroNativeMipLevel; mip.get() < mips; ++mip) 1265 { 1266 GLuint imageMipLevel = mNativeTextureStorage->getGLLevel(mip); 1267 ImageDefinitionMtl &imageDef = mTexImageDefs[face][imageMipLevel]; 1268 if (imageDef.image) 1269 { 1270 continue; 1271 } 1272 imageDef.image = createImageViewFromTextureStorage(face, imageMipLevel); 1273 imageDef.formatID = mFormat.intendedFormatId; 1274 } 1275 } 1276} 1277 1278mtl::TextureRef &TextureMtl::getImage(const gl::ImageIndex &imageIndex) 1279{ 1280 return getImageDefinition(imageIndex).image; 1281} 1282 1283ImageDefinitionMtl &TextureMtl::getImageDefinition(const gl::ImageIndex &imageIndex) 1284{ 1285 GLuint cubeFaceOrZero = GetImageCubeFaceIndexOrZeroFrom(imageIndex); 1286 ImageDefinitionMtl &imageDef = mTexImageDefs[cubeFaceOrZero][imageIndex.getLevelIndex()]; 1287 1288 if (!imageDef.image && mNativeTextureStorage) 1289 { 1290 // If native texture is already created, and the image at this index is not available, 1291 // then create a view of native texture at this index, so that modifications of the image 1292 // are reflected back to native texture's respective index. 1293 if (!mNativeTextureStorage->isGLLevelSupported(imageIndex.getLevelIndex())) 1294 { 1295 // Image outside native texture's mip levels is skipped. 1296 return imageDef; 1297 } 1298 1299 imageDef.image = 1300 createImageViewFromTextureStorage(cubeFaceOrZero, imageIndex.getLevelIndex()); 1301 imageDef.formatID = mFormat.intendedFormatId; 1302 } 1303 1304 return imageDef; 1305} 1306RenderTargetMtl &TextureMtl::getRenderTarget(const gl::ImageIndex &imageIndex) 1307{ 1308 ASSERT(imageIndex.getType() == gl::TextureType::_2D || 1309 imageIndex.getType() == gl::TextureType::Rectangle || 1310 imageIndex.getType() == gl::TextureType::_2DMultisample || imageIndex.hasLayer()); 1311 GLuint layer = GetImageLayerIndexFrom(imageIndex); 1312 RenderTargetMtl &rtt = mPerLayerRenderTargets[layer][imageIndex.getLevelIndex()]; 1313 if (!rtt.getTexture()) 1314 { 1315 // Lazy initialization of render target: 1316 mtl::TextureRef &image = getImage(imageIndex); 1317 if (image) 1318 { 1319 if (imageIndex.getType() == gl::TextureType::CubeMap) 1320 { 1321 // Cube map is special, the image is already the view of its layer 1322 rtt.set(image, mtl::kZeroNativeMipLevel, 0, mFormat); 1323 } 1324 else 1325 { 1326 rtt.set(image, mtl::kZeroNativeMipLevel, layer, mFormat); 1327 } 1328 } 1329 } 1330 return rtt; 1331} 1332 1333angle::Result TextureMtl::setImage(const gl::Context *context, 1334 const gl::ImageIndex &index, 1335 GLenum internalFormat, 1336 const gl::Extents &size, 1337 GLenum format, 1338 GLenum type, 1339 const gl::PixelUnpackState &unpack, 1340 gl::Buffer *unpackBuffer, 1341 const uint8_t *pixels) 1342{ 1343 const gl::InternalFormat &dstFormatInfo = gl::GetInternalFormatInfo(internalFormat, type); 1344 1345 return setImageImpl(context, index, dstFormatInfo, size, format, type, unpack, unpackBuffer, 1346 pixels); 1347} 1348 1349angle::Result TextureMtl::setSubImage(const gl::Context *context, 1350 const gl::ImageIndex &index, 1351 const gl::Box &area, 1352 GLenum format, 1353 GLenum type, 1354 const gl::PixelUnpackState &unpack, 1355 gl::Buffer *unpackBuffer, 1356 const uint8_t *pixels) 1357{ 1358 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, type); 1359 1360 return setSubImageImpl(context, index, area, formatInfo, type, unpack, unpackBuffer, pixels); 1361} 1362 1363angle::Result TextureMtl::setCompressedImage(const gl::Context *context, 1364 const gl::ImageIndex &index, 1365 GLenum internalFormat, 1366 const gl::Extents &size, 1367 const gl::PixelUnpackState &unpack, 1368 size_t imageSize, 1369 const uint8_t *pixels) 1370{ 1371 const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat); 1372 const gl::State &glState = context->getState(); 1373 gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack); 1374 1375 return setImageImpl(context, index, formatInfo, size, internalFormat, GL_UNSIGNED_BYTE, unpack, 1376 unpackBuffer, pixels); 1377} 1378 1379angle::Result TextureMtl::setCompressedSubImage(const gl::Context *context, 1380 const gl::ImageIndex &index, 1381 const gl::Box &area, 1382 GLenum format, 1383 const gl::PixelUnpackState &unpack, 1384 size_t imageSize, 1385 const uint8_t *pixels) 1386{ 1387 1388 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format, GL_UNSIGNED_BYTE); 1389 1390 const gl::State &glState = context->getState(); 1391 gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack); 1392 1393 return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, unpackBuffer, 1394 pixels); 1395} 1396 1397angle::Result TextureMtl::copyImage(const gl::Context *context, 1398 const gl::ImageIndex &index, 1399 const gl::Rectangle &sourceArea, 1400 GLenum internalFormat, 1401 gl::Framebuffer *source) 1402{ 1403 gl::Extents newImageSize(sourceArea.width, sourceArea.height, 1); 1404 const gl::InternalFormat &internalFormatInfo = 1405 gl::GetInternalFormatInfo(internalFormat, GL_UNSIGNED_BYTE); 1406 1407 ContextMtl *contextMtl = mtl::GetImpl(context); 1408 angle::FormatID angleFormatId = 1409 angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat); 1410 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 1411 1412 FramebufferMtl *srcFramebufferMtl = mtl::GetImpl(source); 1413 RenderTargetMtl *srcReadRT = srcFramebufferMtl->getColorReadRenderTarget(context); 1414 RenderTargetMtl colorReadRT; 1415 if (srcReadRT) 1416 { 1417 // Need to duplicate RenderTargetMtl since the srcReadRT would be invalidated in 1418 // redefineImage(). This can happen if the source and this texture are the same texture. 1419 // Duplication ensures the copyImage() will be able to proceed even if the source texture 1420 // will be redefined. 1421 colorReadRT.duplicateFrom(*srcReadRT); 1422 } 1423 1424 ANGLE_TRY(redefineImage(context, index, mtlFormat, newImageSize)); 1425 1426 gl::Extents fbSize = source->getReadColorAttachment()->getSize(); 1427 gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height); 1428 if (context->isWebGL() && !fbRect.encloses(sourceArea)) 1429 { 1430 ANGLE_TRY(initializeContents(context, GL_NONE, index)); 1431 } 1432 1433 return copySubImageImpl(context, index, gl::Offset(0, 0, 0), sourceArea, internalFormatInfo, 1434 srcFramebufferMtl, &colorReadRT); 1435} 1436 1437angle::Result TextureMtl::copySubImage(const gl::Context *context, 1438 const gl::ImageIndex &index, 1439 const gl::Offset &destOffset, 1440 const gl::Rectangle &sourceArea, 1441 gl::Framebuffer *source) 1442{ 1443 const gl::InternalFormat ¤tFormat = *mState.getImageDesc(index).format.info; 1444 FramebufferMtl *srcFramebufferMtl = mtl::GetImpl(source); 1445 RenderTargetMtl *colorReadRT = srcFramebufferMtl->getColorReadRenderTarget(context); 1446 return copySubImageImpl(context, index, destOffset, sourceArea, currentFormat, 1447 srcFramebufferMtl, colorReadRT); 1448} 1449 1450angle::Result TextureMtl::copyTexture(const gl::Context *context, 1451 const gl::ImageIndex &index, 1452 GLenum internalFormat, 1453 GLenum type, 1454 GLint sourceLevel, 1455 bool unpackFlipY, 1456 bool unpackPremultiplyAlpha, 1457 bool unpackUnmultiplyAlpha, 1458 const gl::Texture *source) 1459{ 1460 const gl::ImageDesc &sourceImageDesc = source->getTextureState().getImageDesc( 1461 NonCubeTextureTypeToTarget(source->getType()), sourceLevel); 1462 const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat, type); 1463 1464 // Only 2D textures are supported. 1465 ASSERT(sourceImageDesc.size.depth == 1); 1466 1467 ContextMtl *contextMtl = mtl::GetImpl(context); 1468 angle::FormatID angleFormatId = 1469 angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat); 1470 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 1471 1472 ANGLE_TRY(redefineImage(context, index, mtlFormat, sourceImageDesc.size)); 1473 1474 return copySubTextureImpl( 1475 context, index, gl::Offset(0, 0, 0), internalFormatInfo, sourceLevel, 1476 gl::Box(0, 0, 0, sourceImageDesc.size.width, sourceImageDesc.size.height, 1), unpackFlipY, 1477 unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source); 1478} 1479 1480angle::Result TextureMtl::copySubTexture(const gl::Context *context, 1481 const gl::ImageIndex &index, 1482 const gl::Offset &destOffset, 1483 GLint sourceLevel, 1484 const gl::Box &sourceBox, 1485 bool unpackFlipY, 1486 bool unpackPremultiplyAlpha, 1487 bool unpackUnmultiplyAlpha, 1488 const gl::Texture *source) 1489{ 1490 const gl::InternalFormat ¤tFormat = *mState.getImageDesc(index).format.info; 1491 1492 return copySubTextureImpl(context, index, destOffset, currentFormat, sourceLevel, sourceBox, 1493 unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source); 1494} 1495 1496angle::Result TextureMtl::copyCompressedTexture(const gl::Context *context, 1497 const gl::Texture *source) 1498{ 1499 UNIMPLEMENTED(); 1500 1501 return angle::Result::Stop; 1502} 1503 1504angle::Result TextureMtl::setStorage(const gl::Context *context, 1505 gl::TextureType type, 1506 size_t mipmaps, 1507 GLenum internalFormat, 1508 const gl::Extents &size) 1509{ 1510 ContextMtl *contextMtl = mtl::GetImpl(context); 1511 const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat); 1512 angle::FormatID angleFormatId = 1513 angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat); 1514 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 1515 1516 return setStorageImpl(context, type, mipmaps, mtlFormat, size); 1517} 1518 1519angle::Result TextureMtl::setStorageExternalMemory(const gl::Context *context, 1520 gl::TextureType type, 1521 size_t levels, 1522 GLenum internalFormat, 1523 const gl::Extents &size, 1524 gl::MemoryObject *memoryObject, 1525 GLuint64 offset, 1526 GLbitfield createFlags, 1527 GLbitfield usageFlags, 1528 const void *imageCreateInfoPNext) 1529{ 1530 UNIMPLEMENTED(); 1531 1532 return angle::Result::Stop; 1533} 1534 1535angle::Result TextureMtl::setStorageMultisample(const gl::Context *context, 1536 gl::TextureType type, 1537 GLsizei samples, 1538 GLint internalformat, 1539 const gl::Extents &size, 1540 bool fixedSampleLocations) 1541{ 1542 UNIMPLEMENTED(); 1543 1544 return angle::Result::Stop; 1545} 1546 1547angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context, 1548 gl::TextureType type, 1549 egl::Image *image) 1550{ 1551 releaseTexture(true); 1552 1553 ContextMtl *contextMtl = mtl::GetImpl(context); 1554 1555 ImageMtl *imageMtl = mtl::GetImpl(image); 1556 if (type != imageMtl->getImageTextureType()) 1557 { 1558 return angle::Result::Stop; 1559 } 1560 1561 mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>( 1562 imageMtl->getTexture(), /*baseGLLevel=*/0); 1563 1564 const angle::FormatID angleFormatId = 1565 angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat); 1566 mFormat = contextMtl->getPixelFormat(angleFormatId); 1567 1568 mSlices = mNativeTextureStorage->cubeFacesOrArrayLength(); 1569 1570 ANGLE_TRY(ensureSamplerStateCreated(context)); 1571 ANGLE_TRY(createViewFromBaseToMaxLevel()); 1572 1573 // Tell context to rebind textures 1574 contextMtl->invalidateCurrentTextures(); 1575 1576 return angle::Result::Continue; 1577} 1578 1579angle::Result TextureMtl::setImageExternal(const gl::Context *context, 1580 gl::TextureType type, 1581 egl::Stream *stream, 1582 const egl::Stream::GLTextureDescription &desc) 1583{ 1584 UNIMPLEMENTED(); 1585 return angle::Result::Stop; 1586} 1587 1588angle::Result TextureMtl::generateMipmap(const gl::Context *context) 1589{ 1590 ANGLE_TRY(ensureTextureCreated(context)); 1591 1592 ContextMtl *contextMtl = mtl::GetImpl(context); 1593 if (!mViewFromBaseToMaxLevel) 1594 { 1595 return angle::Result::Continue; 1596 } 1597 1598 const mtl::FormatCaps &caps = mFormat.getCaps(); 1599 // 1600 bool sRGB = mFormat.actualInternalFormat().colorEncoding == GL_SRGB; 1601 1602 bool avoidGPUPath = 1603 contextMtl->getDisplay()->getFeatures().forceNonCSBaseMipmapGeneration.enabled && 1604 mViewFromBaseToMaxLevel->widthAt0() < 5; 1605 1606 if (!avoidGPUPath && caps.writable && mState.getType() == gl::TextureType::_3D) 1607 { 1608 // http://anglebug.com/4921. 1609 // Use compute for 3D mipmap generation. 1610 ANGLE_TRY(ensureLevelViewsWithinBaseMaxCreated()); 1611 ANGLE_TRY(contextMtl->getDisplay()->getUtils().generateMipmapCS( 1612 contextMtl, *mViewFromBaseToMaxLevel, sRGB, &mLevelViewsWithinBaseMax)); 1613 } 1614 else if (!avoidGPUPath && caps.filterable && caps.colorRenderable) 1615 { 1616 mtl::BlitCommandEncoder *blitEncoder = GetBlitCommandEncoderForResources( 1617 contextMtl, {mViewFromBaseToMaxLevel->getNativeTexture().get()}); 1618 blitEncoder->generateMipmapsForTexture(*mViewFromBaseToMaxLevel); 1619 } 1620 else 1621 { 1622 ANGLE_TRY(generateMipmapCPU(context)); 1623 } 1624 1625 return angle::Result::Continue; 1626} 1627 1628angle::Result TextureMtl::generateMipmapCPU(const gl::Context *context) 1629{ 1630 ASSERT(mViewFromBaseToMaxLevel); 1631 1632 ContextMtl *contextMtl = mtl::GetImpl(context); 1633 const angle::Format &angleFormat = mFormat.actualAngleFormat(); 1634 // This format must have mip generation function. 1635 ANGLE_MTL_TRY(contextMtl, angleFormat.mipGenerationFunction); 1636 1637 for (uint32_t slice = 0; slice < mSlices; ++slice) 1638 { 1639 GLuint baseGLLevel = mViewFromBaseToMaxLevel->getBaseGLLevel(); 1640 1641 uint32_t prevLevelWidth = mViewFromBaseToMaxLevel->widthAt0(); 1642 uint32_t prevLevelHeight = mViewFromBaseToMaxLevel->heightAt0(); 1643 uint32_t prevLevelDepth = mViewFromBaseToMaxLevel->depthAt0(); 1644 size_t prevLevelRowPitch = angleFormat.pixelBytes * prevLevelWidth; 1645 size_t prevLevelDepthPitch = prevLevelRowPitch * prevLevelHeight; 1646 std::unique_ptr<uint8_t[]> prevLevelData(new (std::nothrow) 1647 uint8_t[prevLevelDepthPitch * prevLevelDepth]); 1648 ANGLE_CHECK_GL_ALLOC(contextMtl, prevLevelData); 1649 std::unique_ptr<uint8_t[]> dstLevelData; 1650 1651 // Download base level data 1652 mViewFromBaseToMaxLevel->getBytes( 1653 contextMtl, prevLevelRowPitch, prevLevelDepthPitch, 1654 MTLRegionMake3D(0, 0, 0, prevLevelWidth, prevLevelHeight, prevLevelDepth), baseGLLevel, 1655 slice, prevLevelData.get()); 1656 1657 for (GLuint mip = 1; mip < mViewFromBaseToMaxLevel->mipmapLevels(); ++mip) 1658 { 1659 GLuint glLevel = baseGLLevel + mip; 1660 uint32_t dstWidth = mViewFromBaseToMaxLevel->width(glLevel); 1661 uint32_t dstHeight = mViewFromBaseToMaxLevel->height(glLevel); 1662 uint32_t dstDepth = mViewFromBaseToMaxLevel->depth(glLevel); 1663 1664 size_t dstRowPitch = angleFormat.pixelBytes * dstWidth; 1665 size_t dstDepthPitch = dstRowPitch * dstHeight; 1666 size_t dstDataSize = dstDepthPitch * dstDepth; 1667 if (!dstLevelData) 1668 { 1669 // Allocate once and reuse the buffer 1670 dstLevelData.reset(new (std::nothrow) uint8_t[dstDataSize]); 1671 ANGLE_CHECK_GL_ALLOC(contextMtl, dstLevelData); 1672 } 1673 1674 // Generate mip level 1675 angleFormat.mipGenerationFunction( 1676 prevLevelWidth, prevLevelHeight, 1, prevLevelData.get(), prevLevelRowPitch, 1677 prevLevelDepthPitch, dstLevelData.get(), dstRowPitch, dstDepthPitch); 1678 1679 mtl::MipmapNativeLevel nativeLevel = mViewFromBaseToMaxLevel->getNativeLevel(glLevel); 1680 1681 // Upload to texture 1682 ANGLE_TRY(UploadTextureContents(context, angleFormat, 1683 MTLRegionMake3D(0, 0, 0, dstWidth, dstHeight, dstDepth), 1684 nativeLevel, slice, dstLevelData.get(), dstRowPitch, 1685 dstDepthPitch, false, *mViewFromBaseToMaxLevel)); 1686 1687 prevLevelWidth = dstWidth; 1688 prevLevelHeight = dstHeight; 1689 prevLevelDepth = dstDepth; 1690 prevLevelRowPitch = dstRowPitch; 1691 prevLevelDepthPitch = dstDepthPitch; 1692 std::swap(prevLevelData, dstLevelData); 1693 } // for mip level 1694 1695 } // For layers 1696 1697 return angle::Result::Continue; 1698} 1699 1700bool TextureMtl::needsFormatViewForPixelLocalStorage( 1701 const ShPixelLocalStorageOptions &plsOptions) const 1702{ 1703 // On iOS devices with GPU family 5 and later, Metal doesn't apply lossless compression to 1704 // the texture if we set MTLTextureUsagePixelFormatView. This shouldn't be a problem though 1705 // because iOS devices implement pixel local storage with framebuffer fetch instead of shader 1706 // images. 1707 if (plsOptions.type == ShPixelLocalStorageType::ImageLoadStore) 1708 { 1709 switch (mFormat.metalFormat) 1710 { 1711 case MTLPixelFormatRGBA8Unorm: 1712 case MTLPixelFormatRGBA8Uint: 1713 case MTLPixelFormatRGBA8Sint: 1714 return !plsOptions.supportsNativeRGBA8ImageFormats; 1715 default: 1716 break; 1717 } 1718 } 1719 return false; 1720} 1721 1722bool TextureMtl::isImmutableOrPBuffer() const 1723{ 1724 return mState.getImmutableFormat() || mBoundSurface; 1725} 1726 1727angle::Result TextureMtl::setBaseLevel(const gl::Context *context, GLuint baseLevel) 1728{ 1729 return onBaseMaxLevelsChanged(context); 1730} 1731 1732angle::Result TextureMtl::bindTexImage(const gl::Context *context, egl::Surface *surface) 1733{ 1734 releaseTexture(true); 1735 1736 mBoundSurface = surface; 1737 auto pBuffer = GetImplAs<OffscreenSurfaceMtl>(surface); 1738 mNativeTextureStorage = std::make_unique<NativeTextureWrapperWithViewSupport>( 1739 pBuffer->getColorTexture(), /*baseGLLevel=*/0); 1740 mFormat = pBuffer->getColorFormat(); 1741 mSlices = mNativeTextureStorage->cubeFacesOrArrayLength(); 1742 1743 ANGLE_TRY(ensureSamplerStateCreated(context)); 1744 ANGLE_TRY(createViewFromBaseToMaxLevel()); 1745 1746 // Tell context to rebind textures 1747 ContextMtl *contextMtl = mtl::GetImpl(context); 1748 contextMtl->invalidateCurrentTextures(); 1749 1750 return angle::Result::Continue; 1751} 1752 1753angle::Result TextureMtl::releaseTexImage(const gl::Context *context) 1754{ 1755 releaseTexture(true); 1756 mBoundSurface = nullptr; 1757 return angle::Result::Continue; 1758} 1759 1760angle::Result TextureMtl::getAttachmentRenderTarget(const gl::Context *context, 1761 GLenum binding, 1762 const gl::ImageIndex &imageIndex, 1763 GLsizei samples, 1764 FramebufferAttachmentRenderTarget **rtOut) 1765{ 1766 ANGLE_TRY(ensureTextureCreated(context)); 1767 1768 ContextMtl *contextMtl = mtl::GetImpl(context); 1769 ANGLE_MTL_TRY(contextMtl, mNativeTextureStorage); 1770 1771 *rtOut = &getRenderTarget(imageIndex); 1772 1773 return angle::Result::Continue; 1774} 1775 1776angle::Result TextureMtl::syncState(const gl::Context *context, 1777 const gl::Texture::DirtyBits &dirtyBits, 1778 gl::Command source) 1779{ 1780 ContextMtl *contextMtl = mtl::GetImpl(context); 1781 for (size_t dirtyBit : dirtyBits) 1782 { 1783 switch (dirtyBit) 1784 { 1785 case gl::Texture::DIRTY_BIT_COMPARE_MODE: 1786 case gl::Texture::DIRTY_BIT_COMPARE_FUNC: 1787 // Tell context to rebind textures so that ProgramMtl has a chance to verify 1788 // depth texture compare mode. 1789 contextMtl->invalidateCurrentTextures(); 1790 // fall through 1791 OS_FALLTHROUGH; 1792 case gl::Texture::DIRTY_BIT_MIN_FILTER: 1793 case gl::Texture::DIRTY_BIT_MAG_FILTER: 1794 case gl::Texture::DIRTY_BIT_WRAP_S: 1795 case gl::Texture::DIRTY_BIT_WRAP_T: 1796 case gl::Texture::DIRTY_BIT_WRAP_R: 1797 case gl::Texture::DIRTY_BIT_MAX_ANISOTROPY: 1798 case gl::Texture::DIRTY_BIT_MIN_LOD: 1799 case gl::Texture::DIRTY_BIT_MAX_LOD: 1800 case gl::Texture::DIRTY_BIT_SRGB_DECODE: 1801 case gl::Texture::DIRTY_BIT_BORDER_COLOR: 1802 // Recreate sampler state 1803 mMetalSamplerState = nil; 1804 break; 1805 case gl::Texture::DIRTY_BIT_MAX_LEVEL: 1806 case gl::Texture::DIRTY_BIT_BASE_LEVEL: 1807 ANGLE_TRY(onBaseMaxLevelsChanged(context)); 1808 break; 1809 case gl::Texture::DIRTY_BIT_SWIZZLE_RED: 1810 case gl::Texture::DIRTY_BIT_SWIZZLE_GREEN: 1811 case gl::Texture::DIRTY_BIT_SWIZZLE_BLUE: 1812 case gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA: 1813 case gl::Texture::DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE: 1814 { 1815 // Recreate swizzle/stencil view. 1816 mSwizzleStencilSamplingView = nullptr; 1817 } 1818 break; 1819 default: 1820 break; 1821 } 1822 } 1823 1824 ANGLE_TRY(ensureTextureCreated(context)); 1825 ANGLE_TRY(ensureSamplerStateCreated(context)); 1826 1827 return angle::Result::Continue; 1828} 1829 1830angle::Result TextureMtl::bindToShader(const gl::Context *context, 1831 mtl::RenderCommandEncoder *cmdEncoder, 1832 gl::ShaderType shaderType, 1833 gl::Sampler *sampler, 1834 int textureSlotIndex, 1835 int samplerSlotIndex) 1836{ 1837 ASSERT(mNativeTextureStorage); 1838 ASSERT(mViewFromBaseToMaxLevel); 1839 1840 float minLodClamp; 1841 float maxLodClamp; 1842 id<MTLSamplerState> samplerState; 1843 1844 if (!mSwizzleStencilSamplingView) 1845 { 1846#if ANGLE_MTL_SWIZZLE_AVAILABLE 1847 ContextMtl *contextMtl = mtl::GetImpl(context); 1848 const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures(); 1849 1850 // Sampling from unused channels of depth and stencil textures is undefined in Metal. 1851 // ANGLE relies on swizzled views to enforce values required by OpenGL ES specs. Some 1852 // drivers fail to sample from a swizzled view of a stencil texture so skip this step. 1853 const bool skipStencilSwizzle = mFormat.actualFormatId == angle::FormatID::S8_UINT && 1854 features.avoidStencilTextureSwizzle.enabled; 1855 if (!skipStencilSwizzle && 1856 (mState.getSwizzleState().swizzleRequired() || 1857 mFormat.actualAngleFormat().hasDepthOrStencilBits() || mFormat.swizzled) && 1858 features.hasTextureSwizzle.enabled) 1859 { 1860 const gl::InternalFormat &glInternalFormat = *mState.getBaseLevelDesc().format.info; 1861 1862 MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake( 1863 mtl::GetTextureSwizzle(OverrideSwizzleValue( 1864 context, mState.getSwizzleState().swizzleRed, mFormat, glInternalFormat)), 1865 mtl::GetTextureSwizzle(OverrideSwizzleValue( 1866 context, mState.getSwizzleState().swizzleGreen, mFormat, glInternalFormat)), 1867 mtl::GetTextureSwizzle(OverrideSwizzleValue( 1868 context, mState.getSwizzleState().swizzleBlue, mFormat, glInternalFormat)), 1869 mtl::GetTextureSwizzle(OverrideSwizzleValue( 1870 context, mState.getSwizzleState().swizzleAlpha, mFormat, glInternalFormat))); 1871 1872 MTLPixelFormat format = mViewFromBaseToMaxLevel->pixelFormat(); 1873 if (mState.isStencilMode()) 1874 { 1875 if (format == MTLPixelFormatDepth32Float_Stencil8) 1876 { 1877 format = MTLPixelFormatX32_Stencil8; 1878 } 1879# if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1880 else if (format == MTLPixelFormatDepth24Unorm_Stencil8) 1881 { 1882 format = MTLPixelFormatX24_Stencil8; 1883 } 1884# endif 1885 } 1886 1887 mSwizzleStencilSamplingView = mNativeTextureStorage->createMipsSwizzleView( 1888 mViewFromBaseToMaxLevel->getBaseGLLevel(), mViewFromBaseToMaxLevel->mipmapLevels(), 1889 format, swizzle); 1890 } 1891 else 1892#endif // ANGLE_MTL_SWIZZLE_AVAILABLE 1893 { 1894 mSwizzleStencilSamplingView = mState.isStencilMode() 1895 ? mViewFromBaseToMaxLevel->getStencilView() 1896 : mViewFromBaseToMaxLevel->getNativeTexture(); 1897 } 1898 } 1899 1900 if (!sampler) 1901 { 1902 samplerState = mMetalSamplerState; 1903 minLodClamp = mState.getSamplerState().getMinLod(); 1904 maxLodClamp = mState.getSamplerState().getMaxLod(); 1905 } 1906 else 1907 { 1908 SamplerMtl *samplerMtl = mtl::GetImpl(sampler); 1909 samplerState = samplerMtl->getSampler(mtl::GetImpl(context)); 1910 minLodClamp = sampler->getSamplerState().getMinLod(); 1911 maxLodClamp = sampler->getSamplerState().getMaxLod(); 1912 } 1913 1914 minLodClamp = std::max(minLodClamp, 0.f); 1915 1916 cmdEncoder->setTexture(shaderType, mSwizzleStencilSamplingView, textureSlotIndex); 1917 cmdEncoder->setSamplerState(shaderType, samplerState, minLodClamp, maxLodClamp, 1918 samplerSlotIndex); 1919 1920 return angle::Result::Continue; 1921} 1922 1923angle::Result TextureMtl::bindToShaderImage(const gl::Context *context, 1924 mtl::RenderCommandEncoder *cmdEncoder, 1925 gl::ShaderType shaderType, 1926 int textureSlotIndex, 1927 int level, 1928 int layer, 1929 GLenum format) 1930{ 1931 ASSERT(mNativeTextureStorage); 1932 ASSERT(mState.getImmutableFormat()); 1933 ASSERT(0 <= level && static_cast<uint32_t>(level) < mState.getImmutableLevels()); 1934 ASSERT(0 <= layer && static_cast<uint32_t>(layer) < mSlices); 1935 1936 ContextMtl *contextMtl = mtl::GetImpl(context); 1937 angle::FormatID angleFormatId = angle::Format::InternalFormatToID(format); 1938 mtl::Format imageAccessFormat = contextMtl->getPixelFormat(angleFormatId); 1939 LayerLevelTextureViewVector &textureViewVector = 1940 mShaderImageViews[imageAccessFormat.metalFormat]; 1941 mtl::TextureRef &textureRef = GetLayerLevelTextureView(&textureViewVector, layer, level, 1942 mSlices, mState.getImmutableLevels()); 1943 1944 if (textureRef == nullptr) 1945 { 1946 textureRef = mNativeTextureStorage->createShaderImageView2D(level, layer, 1947 imageAccessFormat.metalFormat); 1948 } 1949 1950 cmdEncoder->setRWTexture(shaderType, textureRef, textureSlotIndex); 1951 return angle::Result::Continue; 1952} 1953 1954angle::Result TextureMtl::redefineImage(const gl::Context *context, 1955 const gl::ImageIndex &index, 1956 const mtl::Format &mtlFormat, 1957 const gl::Extents &size) 1958{ 1959 bool imageWithinNativeStorageLevels = false; 1960 if (mNativeTextureStorage && mNativeTextureStorage->isGLLevelSupported(index.getLevelIndex())) 1961 { 1962 imageWithinNativeStorageLevels = true; 1963 GLuint glLevel = index.getLevelIndex(); 1964 // Calculate the expected size for the index we are defining. If the size is different 1965 // from the given size, or the format is different, we are redefining the image so we 1966 // must release it. 1967 ASSERT(mNativeTextureStorage->textureType() == mtl::GetTextureType(index.getType())); 1968 if (mFormat != mtlFormat || size != mNativeTextureStorage->size(glLevel)) 1969 { 1970 // Keep other images 1971 releaseTexture(/*releaseImages=*/false); 1972 } 1973 } 1974 1975 // Early-out on empty textures, don't create a zero-sized storage. 1976 if (size.empty()) 1977 { 1978 return angle::Result::Continue; 1979 } 1980 1981 ContextMtl *contextMtl = mtl::GetImpl(context); 1982 // Cache last defined image format: 1983 mFormat = mtlFormat; 1984 ImageDefinitionMtl &imageDef = getImageDefinition(index); 1985 1986 // If native texture still exists, it means the size hasn't been changed, no need to create new 1987 // image 1988 if (mNativeTextureStorage && imageDef.image && imageWithinNativeStorageLevels) 1989 { 1990 ASSERT(imageDef.image->textureType() == 1991 mtl::GetTextureType(GetTextureImageType(index.getType())) && 1992 imageDef.formatID == mFormat.intendedFormatId && imageDef.image->sizeAt0() == size); 1993 } 1994 else 1995 { 1996 imageDef.formatID = mtlFormat.intendedFormatId; 1997 bool allowFormatView = mFormat.hasDepthAndStencilBits() || 1998 needsFormatViewForPixelLocalStorage( 1999 contextMtl->getDisplay()->getNativePixelLocalStorageOptions()); 2000 // Create image to hold texture's data at this level & slice: 2001 switch (index.getType()) 2002 { 2003 case gl::TextureType::_2D: 2004 case gl::TextureType::CubeMap: 2005 ANGLE_TRY(mtl::Texture::Make2DTexture( 2006 contextMtl, mtlFormat, size.width, size.height, 1, 2007 /** renderTargetOnly */ false, allowFormatView, &imageDef.image)); 2008 break; 2009 case gl::TextureType::_3D: 2010 ANGLE_TRY(mtl::Texture::Make3DTexture( 2011 contextMtl, mtlFormat, size.width, size.height, size.depth, 1, 2012 /** renderTargetOnly */ false, allowFormatView, &imageDef.image)); 2013 break; 2014 case gl::TextureType::_2DArray: 2015 ANGLE_TRY(mtl::Texture::Make2DArrayTexture( 2016 contextMtl, mtlFormat, size.width, size.height, 1, size.depth, 2017 /** renderTargetOnly */ false, allowFormatView, &imageDef.image)); 2018 break; 2019 default: 2020 UNREACHABLE(); 2021 } 2022 2023 // Make sure emulated channels are properly initialized in this newly allocated texture. 2024 ANGLE_TRY(checkForEmulatedChannels(context, mtlFormat, imageDef.image)); 2025 } 2026 2027 // Tell context to rebind textures 2028 contextMtl->invalidateCurrentTextures(); 2029 2030 return angle::Result::Continue; 2031} 2032 2033// If mipmaps = 0, this function will create full mipmaps texture. 2034angle::Result TextureMtl::setStorageImpl(const gl::Context *context, 2035 gl::TextureType type, 2036 size_t mipmaps, 2037 const mtl::Format &mtlFormat, 2038 const gl::Extents &size) 2039{ 2040 // Don't need to hold old images data. 2041 releaseTexture(true); 2042 2043 ContextMtl *contextMtl = mtl::GetImpl(context); 2044 2045 // Tell context to rebind textures 2046 contextMtl->invalidateCurrentTextures(); 2047 2048 mFormat = mtlFormat; 2049 2050 ANGLE_TRY(createNativeTexture(context, type, mState.getImmutableLevels(), size)); 2051 ANGLE_TRY(createViewFromBaseToMaxLevel()); 2052 2053 return angle::Result::Continue; 2054} 2055 2056angle::Result TextureMtl::setImageImpl(const gl::Context *context, 2057 const gl::ImageIndex &index, 2058 const gl::InternalFormat &dstFormatInfo, 2059 const gl::Extents &size, 2060 GLenum srcFormat, 2061 GLenum srcType, 2062 const gl::PixelUnpackState &unpack, 2063 gl::Buffer *unpackBuffer, 2064 const uint8_t *pixels) 2065{ 2066 ContextMtl *contextMtl = mtl::GetImpl(context); 2067 angle::FormatID angleFormatId = 2068 angle::Format::InternalFormatToID(dstFormatInfo.sizedInternalFormat); 2069 const mtl::Format &mtlFormat = contextMtl->getPixelFormat(angleFormatId); 2070 2071 ANGLE_TRY(redefineImage(context, index, mtlFormat, size)); 2072 2073 // Early-out on empty textures, don't create a zero-sized storage. 2074 if (size.empty()) 2075 { 2076 return angle::Result::Continue; 2077 } 2078 2079 // Format of the supplied pixels. 2080 const gl::InternalFormat *srcFormatInfo; 2081 if (srcFormat != dstFormatInfo.format || srcType != dstFormatInfo.type) 2082 { 2083 srcFormatInfo = &gl::GetInternalFormatInfo(srcFormat, srcType); 2084 } 2085 else 2086 { 2087 srcFormatInfo = &dstFormatInfo; 2088 } 2089 return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth), 2090 *srcFormatInfo, srcType, unpack, unpackBuffer, pixels); 2091} 2092 2093angle::Result TextureMtl::setSubImageImpl(const gl::Context *context, 2094 const gl::ImageIndex &index, 2095 const gl::Box &area, 2096 const gl::InternalFormat &formatInfo, 2097 GLenum type, 2098 const gl::PixelUnpackState &unpack, 2099 gl::Buffer *unpackBuffer, 2100 const uint8_t *oriPixels) 2101{ 2102 if (!oriPixels && !unpackBuffer) 2103 { 2104 return angle::Result::Continue; 2105 } 2106 2107 ContextMtl *contextMtl = mtl::GetImpl(context); 2108 2109 ANGLE_TRY(ensureImageCreated(context, index)); 2110 mtl::TextureRef &image = getImage(index); 2111 2112 GLuint sourceRowPitch = 0; 2113 GLuint sourceDepthPitch = 0; 2114 GLuint sourceSkipBytes = 0; 2115 ANGLE_CHECK_GL_MATH(contextMtl, formatInfo.computeRowPitch(type, area.width, unpack.alignment, 2116 unpack.rowLength, &sourceRowPitch)); 2117 ANGLE_CHECK_GL_MATH( 2118 contextMtl, formatInfo.computeDepthPitch(area.height, unpack.imageHeight, sourceRowPitch, 2119 &sourceDepthPitch)); 2120 ANGLE_CHECK_GL_MATH(contextMtl, 2121 formatInfo.computeSkipBytes(type, sourceRowPitch, sourceDepthPitch, unpack, 2122 index.usesTex3D(), &sourceSkipBytes)); 2123 2124 // Get corresponding source data's ANGLE format 2125 angle::FormatID srcAngleFormatId; 2126 if (formatInfo.sizedInternalFormat == GL_DEPTH_COMPONENT24) 2127 { 2128 // GL_DEPTH_COMPONENT24 is special case, its supplied data is 32 bit depth. 2129 srcAngleFormatId = angle::FormatID::D32_UNORM; 2130 } 2131 else 2132 { 2133 srcAngleFormatId = angle::Format::InternalFormatToID(formatInfo.sizedInternalFormat); 2134 } 2135 const angle::Format &srcAngleFormat = angle::Format::Get(srcAngleFormatId); 2136 2137 const uint8_t *usablePixels = oriPixels + sourceSkipBytes; 2138 2139 // Upload to texture 2140 if (index.getType() == gl::TextureType::_2DArray) 2141 { 2142 // OpenGL unifies texture array and texture 3d's box area by using z & depth as array start 2143 // index & length for texture array. However, Metal treats them differently. We need to 2144 // handle them in separate code. 2145 MTLRegion mtlRegion = MTLRegionMake3D(area.x, area.y, 0, area.width, area.height, 1); 2146 2147 for (int slice = 0; slice < area.depth; ++slice) 2148 { 2149 int sliceIndex = slice + area.z; 2150 const uint8_t *srcPixels = usablePixels + slice * sourceDepthPitch; 2151 ANGLE_TRY(setPerSliceSubImage(context, sliceIndex, mtlRegion, formatInfo, type, 2152 srcAngleFormat, sourceRowPitch, sourceDepthPitch, 2153 unpackBuffer, srcPixels, image)); 2154 } 2155 } 2156 else 2157 { 2158 MTLRegion mtlRegion = 2159 MTLRegionMake3D(area.x, area.y, area.z, area.width, area.height, area.depth); 2160 2161 ANGLE_TRY(setPerSliceSubImage(context, 0, mtlRegion, formatInfo, type, srcAngleFormat, 2162 sourceRowPitch, sourceDepthPitch, unpackBuffer, usablePixels, 2163 image)); 2164 } 2165 2166 return angle::Result::Continue; 2167} 2168 2169angle::Result TextureMtl::setPerSliceSubImage(const gl::Context *context, 2170 int slice, 2171 const MTLRegion &mtlArea, 2172 const gl::InternalFormat &internalFormat, 2173 GLenum type, 2174 const angle::Format &pixelsAngleFormat, 2175 size_t pixelsRowPitch, 2176 size_t pixelsDepthPitch, 2177 gl::Buffer *unpackBuffer, 2178 const uint8_t *pixels, 2179 const mtl::TextureRef &image) 2180{ 2181 // If source pixels are luminance or RGB8, we need to convert them to RGBA 2182 2183 if (mFormat.needConversion(pixelsAngleFormat.id)) 2184 { 2185 return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type, 2186 pixelsAngleFormat, pixelsRowPitch, pixelsDepthPitch, 2187 unpackBuffer, pixels, image); 2188 } 2189 2190 // No conversion needed. 2191 ContextMtl *contextMtl = mtl::GetImpl(context); 2192 2193 if (unpackBuffer) 2194 { 2195 uintptr_t offset = reinterpret_cast<uintptr_t>(pixels); 2196 GLuint minRowPitch; 2197 ANGLE_CHECK_GL_MATH(contextMtl, internalFormat.computeRowPitch( 2198 type, static_cast<GLint>(mtlArea.size.width), 2199 /** aligment */ 1, /** rowLength */ 0, &minRowPitch)); 2200 if (offset % mFormat.actualAngleFormat().pixelBytes || pixelsRowPitch < minRowPitch) 2201 { 2202 // offset is not divisible by pixelByte or the source row pitch is smaller than minimum 2203 // row pitch, use convertAndSetPerSliceSubImage() function. 2204 return convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type, 2205 pixelsAngleFormat, pixelsRowPitch, 2206 pixelsDepthPitch, unpackBuffer, pixels, image); 2207 } 2208 2209 BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer); 2210 2211 if (mFormat.hasDepthAndStencilBits()) 2212 { 2213 // NOTE(hqle): packed depth & stencil texture cannot copy from buffer directly, needs 2214 // to split its depth & stencil data and copy separately. 2215 const uint8_t *clientData = unpackBufferMtl->getBufferDataReadOnly(contextMtl); 2216 clientData += offset; 2217 ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 2218 mtl::kZeroNativeMipLevel, slice, clientData, 2219 pixelsRowPitch, pixelsDepthPitch, false, image)); 2220 } 2221 else 2222 { 2223 mtl::BufferRef sourceBuffer = unpackBufferMtl->getCurrentBuffer(); 2224 // PVRTC1 blocks are stored in a reflected Morton order 2225 // and need to be linearized for buffer uploads in Metal. 2226 // This step is skipped for textures that have only one block. 2227 if (mFormat.isPVRTC() && mtlArea.size.height > 4) 2228 { 2229 // PVRTC1 inherent requirement. 2230 ASSERT(gl::isPow2(mtlArea.size.width) && gl::isPow2(mtlArea.size.height)); 2231 // Metal-specific limitation enforced by ANGLE validation. 2232 ASSERT(mtlArea.size.width == mtlArea.size.height); 2233 static_assert(gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE <= 262144, 2234 "The current kernel can handle up to 65536 blocks per dimension."); 2235 2236 // Current command buffer implementation does not support 64-bit offsets. 2237 ANGLE_MTL_CHECK(contextMtl, offset <= std::numeric_limits<uint32_t>::max(), 2238 GL_INVALID_OPERATION); 2239 2240 mtl::BufferRef stagingBuffer; 2241 ANGLE_TRY( 2242 mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer)); 2243 2244 mtl::BlockLinearizationParams params; 2245 params.srcBuffer = sourceBuffer; 2246 params.dstBuffer = stagingBuffer; 2247 params.srcBufferOffset = static_cast<uint32_t>(offset); 2248 params.blocksWide = 2249 static_cast<GLuint>(mtlArea.size.width) / internalFormat.compressedBlockWidth; 2250 params.blocksHigh = 2251 static_cast<GLuint>(mtlArea.size.height) / internalFormat.compressedBlockHeight; 2252 2253 // PVRTC1 textures always have at least 2 blocks in each dimension. 2254 // Enforce correct block layout for 8x8 textures that use 8x4 blocks. 2255 params.blocksWide = std::max(params.blocksWide, 2u); 2256 2257 ANGLE_TRY(contextMtl->getDisplay()->getUtils().linearizeBlocks(contextMtl, params)); 2258 2259 sourceBuffer = stagingBuffer; 2260 offset = 0; 2261 } 2262 else if (pixelsAngleFormat.id == angle::FormatID::D32_FLOAT) 2263 { 2264 // Current command buffer implementation does not support 64-bit offsets. 2265 ANGLE_MTL_CHECK(contextMtl, offset <= std::numeric_limits<uint32_t>::max(), 2266 GL_INVALID_OPERATION); 2267 2268 mtl::BufferRef stagingBuffer; 2269 ANGLE_TRY( 2270 mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer)); 2271 2272 ASSERT(pixelsAngleFormat.pixelBytes == 4 && offset % 4 == 0); 2273 ANGLE_TRY(SaturateDepth(contextMtl, sourceBuffer, stagingBuffer, 2274 static_cast<uint32_t>(offset), 2275 static_cast<uint32_t>(pixelsRowPitch) / 4, mtlArea.size)); 2276 2277 sourceBuffer = stagingBuffer; 2278 offset = 0; 2279 } 2280 2281 // Use blit encoder to copy 2282 mtl::BlitCommandEncoder *blitEncoder = 2283 GetBlitCommandEncoderForResources(contextMtl, {sourceBuffer.get(), image.get()}); 2284 CopyBufferToOriginalTextureIfDstIsAView( 2285 contextMtl, blitEncoder, sourceBuffer, offset, pixelsRowPitch, pixelsDepthPitch, 2286 mtlArea.size, image, slice, mtl::kZeroNativeMipLevel, mtlArea.origin, 2287 mFormat.isPVRTC() ? mtl::kBlitOptionRowLinearPVRTC : MTLBlitOptionNone); 2288 } 2289 } 2290 else 2291 { 2292 ANGLE_TRY(UploadTextureContents(context, mFormat.actualAngleFormat(), mtlArea, 2293 mtl::kZeroNativeMipLevel, slice, pixels, pixelsRowPitch, 2294 pixelsDepthPitch, false, image)); 2295 } 2296 return angle::Result::Continue; 2297} 2298 2299angle::Result TextureMtl::convertAndSetPerSliceSubImage(const gl::Context *context, 2300 int slice, 2301 const MTLRegion &mtlArea, 2302 const gl::InternalFormat &internalFormat, 2303 GLenum type, 2304 const angle::Format &pixelsAngleFormat, 2305 size_t pixelsRowPitch, 2306 size_t pixelsDepthPitch, 2307 gl::Buffer *unpackBuffer, 2308 const uint8_t *pixels, 2309 const mtl::TextureRef &image) 2310{ 2311 ASSERT(image && image->valid()); 2312 2313 ContextMtl *contextMtl = mtl::GetImpl(context); 2314 2315 if (unpackBuffer) 2316 { 2317 ANGLE_MTL_CHECK(contextMtl, 2318 reinterpret_cast<uintptr_t>(pixels) <= std::numeric_limits<uint32_t>::max(), 2319 GL_INVALID_OPERATION); 2320 2321 uint32_t offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pixels)); 2322 2323 BufferMtl *unpackBufferMtl = mtl::GetImpl(unpackBuffer); 2324 if (!mFormat.getCaps().writable || mFormat.hasDepthOrStencilBits() || 2325 mFormat.intendedAngleFormat().isBlock) 2326 { 2327 // Unsupported format, use CPU path. 2328 const uint8_t *clientData = unpackBufferMtl->getBufferDataReadOnly(contextMtl); 2329 clientData += offset; 2330 ANGLE_TRY(convertAndSetPerSliceSubImage(context, slice, mtlArea, internalFormat, type, 2331 pixelsAngleFormat, pixelsRowPitch, 2332 pixelsDepthPitch, nullptr, clientData, image)); 2333 } 2334 else 2335 { 2336 // Use compute shader 2337 mtl::CopyPixelsFromBufferParams params; 2338 params.buffer = unpackBufferMtl->getCurrentBuffer(); 2339 params.bufferStartOffset = offset; 2340 params.bufferRowPitch = static_cast<uint32_t>(pixelsRowPitch); 2341 params.bufferDepthPitch = static_cast<uint32_t>(pixelsDepthPitch); 2342 params.texture = image; 2343 params.textureArea = mtl::MTLRegionToGLBox(mtlArea); 2344 2345 // If texture is not array, slice must be zero, if texture is array, mtlArea.origin.z 2346 // must be zero. 2347 // This is because this function uses Metal convention: where slice is only used for 2348 // array textures, and z layer of mtlArea.origin is only used for 3D textures. 2349 ASSERT(slice == 0 || params.textureArea.z == 0); 2350 2351 // For mtl::RenderUtils we convert to OpenGL convention: z layer is used as either array 2352 // texture's slice or 3D texture's layer index. 2353 params.textureArea.z += slice; 2354 2355 ANGLE_TRY(contextMtl->getDisplay()->getUtils().unpackPixelsFromBufferToTexture( 2356 contextMtl, pixelsAngleFormat, params)); 2357 } 2358 } // if (unpackBuffer) 2359 else 2360 { 2361 LoadImageFunctionInfo loadFunctionInfo = mFormat.textureLoadFunctions 2362 ? mFormat.textureLoadFunctions(type) 2363 : LoadImageFunctionInfo(); 2364 const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId); 2365 const size_t dstRowPitch = dstFormat.pixelBytes * mtlArea.size.width; 2366 2367 // It is very important to avoid allocating a new buffer for each row during these 2368 // uploads. 2369 const bool kAvoidStagingBuffers = true; 2370 2371 // Check if original image data is compressed: 2372 if (mFormat.intendedAngleFormat().isBlock) 2373 { 2374 if (mFormat.intendedFormatId != mFormat.actualFormatId) 2375 { 2376 ASSERT(loadFunctionInfo.loadFunction); 2377 2378 // Need to create a buffer to hold entire decompressed image. 2379 const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height; 2380 angle::MemoryBuffer decompressBuf; 2381 ANGLE_CHECK_GL_ALLOC(contextMtl, 2382 decompressBuf.resize(dstDepthPitch * mtlArea.size.depth)); 2383 2384 // Decompress 2385 loadFunctionInfo.loadFunction(contextMtl->getImageLoadContext(), mtlArea.size.width, 2386 mtlArea.size.height, mtlArea.size.depth, pixels, 2387 pixelsRowPitch, pixelsDepthPitch, 2388 decompressBuf.data(), dstRowPitch, dstDepthPitch); 2389 2390 // Upload to texture 2391 ANGLE_TRY(UploadTextureContents( 2392 context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice, 2393 decompressBuf.data(), dstRowPitch, dstDepthPitch, kAvoidStagingBuffers, image)); 2394 } 2395 else 2396 { 2397 // Assert that we're filling the level in it's entierety. 2398 ASSERT(mtlArea.size.width == static_cast<unsigned int>(image->sizeAt0().width)); 2399 ASSERT(mtlArea.size.height == static_cast<unsigned int>(image->sizeAt0().height)); 2400 const size_t dstDepthPitch = dstRowPitch * mtlArea.size.height; 2401 ANGLE_TRY(UploadTextureContents( 2402 context, dstFormat, mtlArea, mtl::kZeroNativeMipLevel, slice, pixels, 2403 dstRowPitch, dstDepthPitch, kAvoidStagingBuffers, image)); 2404 } 2405 } // if (mFormat.intendedAngleFormat().isBlock) 2406 else 2407 { 2408 // Create scratch row buffer 2409 angle::MemoryBuffer conversionRow; 2410 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); 2411 2412 // Convert row by row: 2413 MTLRegion mtlRow = mtlArea; 2414 mtlRow.size.height = mtlRow.size.depth = 1; 2415 for (NSUInteger d = 0; d < mtlArea.size.depth; ++d) 2416 { 2417 mtlRow.origin.z = mtlArea.origin.z + d; 2418 for (NSUInteger r = 0; r < mtlArea.size.height; ++r) 2419 { 2420 const uint8_t *psrc = pixels + d * pixelsDepthPitch + r * pixelsRowPitch; 2421 mtlRow.origin.y = mtlArea.origin.y + r; 2422 2423 // Convert pixels 2424 if (loadFunctionInfo.loadFunction) 2425 { 2426 loadFunctionInfo.loadFunction(contextMtl->getImageLoadContext(), 2427 mtlRow.size.width, 1, 1, psrc, pixelsRowPitch, 2428 0, conversionRow.data(), dstRowPitch, 0); 2429 } 2430 else if (mFormat.hasDepthOrStencilBits()) 2431 { 2432 ConvertDepthStencilData(mtlRow.size, pixelsAngleFormat, pixelsRowPitch, 0, 2433 psrc, dstFormat, nullptr, dstRowPitch, 0, 2434 conversionRow.data()); 2435 } 2436 else 2437 { 2438 CopyImageCHROMIUM(psrc, pixelsRowPitch, pixelsAngleFormat.pixelBytes, 0, 2439 pixelsAngleFormat.pixelReadFunction, conversionRow.data(), 2440 dstRowPitch, dstFormat.pixelBytes, 0, 2441 dstFormat.pixelWriteFunction, internalFormat.format, 2442 dstFormat.componentType, mtlRow.size.width, 1, 1, false, 2443 false, false); 2444 } 2445 2446 // Upload to texture 2447 ANGLE_TRY(UploadTextureContents( 2448 context, dstFormat, mtlRow, mtl::kZeroNativeMipLevel, slice, 2449 conversionRow.data(), dstRowPitch, 0, kAvoidStagingBuffers, image)); 2450 } 2451 } 2452 } // if (mFormat.intendedAngleFormat().isBlock) 2453 } // if (unpackBuffer) 2454 2455 return angle::Result::Continue; 2456} 2457 2458angle::Result TextureMtl::checkForEmulatedChannels(const gl::Context *context, 2459 const mtl::Format &mtlFormat, 2460 const mtl::TextureRef &texture) 2461{ 2462 bool emulatedChannels = mtl::IsFormatEmulated(mtlFormat); 2463 2464 // For emulated channels that GL texture intends to not have, 2465 // we need to initialize their content. 2466 if (emulatedChannels) 2467 { 2468 uint32_t mipmaps = texture->mipmapLevels(); 2469 2470 uint32_t layers = texture->cubeFacesOrArrayLength(); 2471 for (uint32_t layer = 0; layer < layers; ++layer) 2472 { 2473 for (uint32_t mip = 0; mip < mipmaps; ++mip) 2474 { 2475 auto index = mtl::ImageNativeIndex::FromBaseZeroGLIndex( 2476 GetCubeOrArraySliceMipIndex(texture, layer, mip)); 2477 2478 ANGLE_TRY(mtl::InitializeTextureContents(context, texture, mtlFormat, index)); 2479 } 2480 } 2481 } 2482 return angle::Result::Continue; 2483} 2484 2485angle::Result TextureMtl::initializeContents(const gl::Context *context, 2486 GLenum binding, 2487 const gl::ImageIndex &index) 2488{ 2489 if (index.isLayered()) 2490 { 2491 // InitializeTextureContents is only able to initialize one layer at a time. 2492 const gl::ImageDesc &desc = mState.getImageDesc(index); 2493 uint32_t layerCount; 2494 if (index.isEntireLevelCubeMap()) 2495 { 2496 layerCount = 6; 2497 } 2498 else 2499 { 2500 layerCount = desc.size.depth; 2501 } 2502 2503 gl::ImageIndexIterator ite = index.getLayerIterator(layerCount); 2504 while (ite.hasNext()) 2505 { 2506 gl::ImageIndex layerIndex = ite.next(); 2507 ANGLE_TRY(initializeContents(context, GL_NONE, layerIndex)); 2508 } 2509 return angle::Result::Continue; 2510 } 2511 else if (index.getLayerCount() > 1) 2512 { 2513 for (int layer = 0; layer < index.getLayerCount(); ++layer) 2514 { 2515 int layerIdx = layer + index.getLayerIndex(); 2516 gl::ImageIndex layerIndex = 2517 gl::ImageIndex::MakeFromType(index.getType(), index.getLevelIndex(), layerIdx); 2518 ANGLE_TRY(initializeContents(context, GL_NONE, layerIndex)); 2519 } 2520 return angle::Result::Continue; 2521 } 2522 2523 ASSERT(index.getLayerCount() == 1 && !index.isLayered()); 2524 ANGLE_TRY(ensureImageCreated(context, index)); 2525 ContextMtl *contextMtl = mtl::GetImpl(context); 2526 ImageDefinitionMtl &imageDef = getImageDefinition(index); 2527 const mtl::TextureRef &image = imageDef.image; 2528 const mtl::Format &format = contextMtl->getPixelFormat(imageDef.formatID); 2529 // For Texture's image definition, we always use zero mip level. 2530 if (format.metalFormat == MTLPixelFormatInvalid) 2531 { 2532 return angle::Result::Stop; 2533 } 2534 return mtl::InitializeTextureContents( 2535 context, image, format, 2536 mtl::ImageNativeIndex::FromBaseZeroGLIndex( 2537 GetLayerMipIndex(image, GetImageLayerIndexFrom(index), /** level */ 0))); 2538} 2539 2540angle::Result TextureMtl::copySubImageImpl(const gl::Context *context, 2541 const gl::ImageIndex &index, 2542 const gl::Offset &destOffset, 2543 const gl::Rectangle &sourceArea, 2544 const gl::InternalFormat &internalFormat, 2545 const FramebufferMtl *source, 2546 const RenderTargetMtl *colorReadRT) 2547{ 2548 if (!colorReadRT || !colorReadRT->getTexture()) 2549 { 2550 // Is this an error? 2551 return angle::Result::Continue; 2552 } 2553 2554 gl::Extents fbSize = colorReadRT->getTexture()->size(colorReadRT->getLevelIndex()); 2555 gl::Rectangle clippedSourceArea; 2556 if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, fbSize.width, fbSize.height), 2557 &clippedSourceArea)) 2558 { 2559 return angle::Result::Continue; 2560 } 2561 2562 // If negative offsets are given, clippedSourceArea ensures we don't read from those offsets. 2563 // However, that changes the sourceOffset->destOffset mapping. Here, destOffset is shifted by 2564 // the same amount as clipped to correct the error. 2565 const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x, 2566 destOffset.y + clippedSourceArea.y - sourceArea.y, 0); 2567 2568 ANGLE_TRY(ensureImageCreated(context, index)); 2569 2570 if (!mFormat.getCaps().isRenderable()) 2571 { 2572 return copySubImageCPU(context, index, modifiedDestOffset, clippedSourceArea, 2573 internalFormat, source, colorReadRT); 2574 } 2575 2576 // NOTE(hqle): Use compute shader. 2577 return copySubImageWithDraw(context, index, modifiedDestOffset, clippedSourceArea, 2578 internalFormat, source, colorReadRT); 2579} 2580 2581angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context, 2582 const gl::ImageIndex &index, 2583 const gl::Offset &modifiedDestOffset, 2584 const gl::Rectangle &clippedSourceArea, 2585 const gl::InternalFormat &internalFormat, 2586 const FramebufferMtl *source, 2587 const RenderTargetMtl *colorReadRT) 2588{ 2589 ContextMtl *contextMtl = mtl::GetImpl(context); 2590 DisplayMtl *displayMtl = contextMtl->getDisplay(); 2591 2592 const RenderTargetMtl &imageRtt = getRenderTarget(index); 2593 2594 mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(imageRtt); 2595 mtl::ColorBlitParams blitParams; 2596 2597 blitParams.dstTextureSize = imageRtt.getTexture()->size(imageRtt.getLevelIndex()); 2598 blitParams.dstRect = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y, 2599 clippedSourceArea.width, clippedSourceArea.height); 2600 blitParams.dstScissorRect = blitParams.dstRect; 2601 2602 blitParams.enabledBuffers.set(0); 2603 2604 blitParams.src = colorReadRT->getTexture(); 2605 blitParams.srcLevel = colorReadRT->getLevelIndex(); 2606 blitParams.srcLayer = colorReadRT->getLayerIndex(); 2607 2608 blitParams.srcNormalizedCoords = mtl::NormalizedCoords( 2609 clippedSourceArea, colorReadRT->getTexture()->size(blitParams.srcLevel)); 2610 blitParams.srcYFlipped = source->flipY(); 2611 blitParams.dstLuminance = internalFormat.isLUMA(); 2612 2613 return displayMtl->getUtils().blitColorWithDraw( 2614 context, cmdEncoder, colorReadRT->getFormat().actualAngleFormat(), blitParams); 2615} 2616 2617angle::Result TextureMtl::copySubImageCPU(const gl::Context *context, 2618 const gl::ImageIndex &index, 2619 const gl::Offset &modifiedDestOffset, 2620 const gl::Rectangle &clippedSourceArea, 2621 const gl::InternalFormat &internalFormat, 2622 const FramebufferMtl *source, 2623 const RenderTargetMtl *colorReadRT) 2624{ 2625 mtl::TextureRef &image = getImage(index); 2626 ASSERT(image && image->valid()); 2627 2628 ContextMtl *contextMtl = mtl::GetImpl(context); 2629 2630 const angle::Format &dstFormat = angle::Format::Get(mFormat.actualFormatId); 2631 const int dstRowPitch = dstFormat.pixelBytes * clippedSourceArea.width; 2632 angle::MemoryBuffer conversionRow; 2633 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); 2634 2635 gl::Rectangle srcRowArea = gl::Rectangle(clippedSourceArea.x, 0, clippedSourceArea.width, 1); 2636 MTLRegion mtlDstRowArea = MTLRegionMake2D(modifiedDestOffset.x, 0, clippedSourceArea.width, 1); 2637 uint32_t dstSlice = 0; 2638 switch (index.getType()) 2639 { 2640 case gl::TextureType::_2D: 2641 case gl::TextureType::CubeMap: 2642 dstSlice = 0; 2643 break; 2644 case gl::TextureType::_2DArray: 2645 ASSERT(index.hasLayer()); 2646 dstSlice = index.getLayerIndex(); 2647 break; 2648 case gl::TextureType::_3D: 2649 ASSERT(index.hasLayer()); 2650 dstSlice = 0; 2651 mtlDstRowArea.origin.z = index.getLayerIndex(); 2652 break; 2653 default: 2654 UNREACHABLE(); 2655 } 2656 2657 // It is very important to avoid allocating a new buffer for each row during these 2658 // uploads. 2659 const bool kAvoidStagingBuffers = true; 2660 2661 // Copy row by row: 2662 for (int r = 0; r < clippedSourceArea.height; ++r) 2663 { 2664 mtlDstRowArea.origin.y = modifiedDestOffset.y + r; 2665 srcRowArea.y = clippedSourceArea.y + r; 2666 2667 PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0); 2668 2669 // Read pixels from framebuffer to memory: 2670 gl::Rectangle flippedSrcRowArea = source->getCorrectFlippedReadArea(context, srcRowArea); 2671 ANGLE_TRY(source->readPixelsImpl(context, flippedSrcRowArea, packParams, colorReadRT, 2672 conversionRow.data())); 2673 2674 // Upload to texture 2675 ANGLE_TRY(UploadTextureContents(context, dstFormat, mtlDstRowArea, mtl::kZeroNativeMipLevel, 2676 dstSlice, conversionRow.data(), dstRowPitch, 0, 2677 kAvoidStagingBuffers, image)); 2678 } 2679 2680 return angle::Result::Continue; 2681} 2682 2683angle::Result TextureMtl::copySubTextureImpl(const gl::Context *context, 2684 const gl::ImageIndex &index, 2685 const gl::Offset &destOffset, 2686 const gl::InternalFormat &internalFormat, 2687 GLint sourceLevel, 2688 const gl::Box &sourceBox, 2689 bool unpackFlipY, 2690 bool unpackPremultiplyAlpha, 2691 bool unpackUnmultiplyAlpha, 2692 const gl::Texture *source) 2693{ 2694 // Only 2D textures are supported. 2695 ASSERT(sourceBox.depth == 1); 2696 ASSERT(source->getType() == gl::TextureType::_2D); 2697 gl::ImageIndex sourceIndex = gl::ImageIndex::Make2D(sourceLevel); 2698 2699 ContextMtl *contextMtl = mtl::GetImpl(context); 2700 TextureMtl *sourceMtl = mtl::GetImpl(source); 2701 2702 ANGLE_TRY(ensureImageCreated(context, index)); 2703 2704 ANGLE_TRY(sourceMtl->ensureImageCreated(context, sourceIndex)); 2705 2706 const ImageDefinitionMtl &srcImageDef = sourceMtl->getImageDefinition(sourceIndex); 2707 const mtl::TextureRef &sourceImage = srcImageDef.image; 2708 const mtl::Format &sourceFormat = contextMtl->getPixelFormat(srcImageDef.formatID); 2709 const angle::Format &sourceAngleFormat = sourceFormat.actualAngleFormat(); 2710 2711 if (!mFormat.getCaps().isRenderable()) 2712 { 2713 return copySubTextureCPU(context, index, destOffset, internalFormat, 2714 mtl::kZeroNativeMipLevel, sourceBox, sourceAngleFormat, 2715 unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, 2716 sourceImage); 2717 } 2718 return copySubTextureWithDraw( 2719 context, index, destOffset, internalFormat, mtl::kZeroNativeMipLevel, sourceBox, 2720 sourceAngleFormat, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, sourceImage); 2721} 2722 2723angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context, 2724 const gl::ImageIndex &index, 2725 const gl::Offset &destOffset, 2726 const gl::InternalFormat &internalFormat, 2727 const mtl::MipmapNativeLevel &sourceNativeLevel, 2728 const gl::Box &sourceBox, 2729 const angle::Format &sourceAngleFormat, 2730 bool unpackFlipY, 2731 bool unpackPremultiplyAlpha, 2732 bool unpackUnmultiplyAlpha, 2733 const mtl::TextureRef &sourceTexture) 2734{ 2735 ContextMtl *contextMtl = mtl::GetImpl(context); 2736 DisplayMtl *displayMtl = contextMtl->getDisplay(); 2737 2738 mtl::TextureRef image = getImage(index); 2739 ASSERT(image && image->valid()); 2740 2741 if (internalFormat.colorEncoding == GL_SRGB) 2742 { 2743 image = image->getLinearColorView(); 2744 } 2745 2746 mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getTextureRenderCommandEncoder( 2747 image, mtl::ImageNativeIndex::FromBaseZeroGLIndex(GetZeroLevelIndex(image))); 2748 mtl::ColorBlitParams blitParams; 2749 2750 blitParams.dstTextureSize = image->sizeAt0(); 2751 blitParams.dstRect = 2752 gl::Rectangle(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height); 2753 blitParams.dstScissorRect = blitParams.dstRect; 2754 2755 blitParams.enabledBuffers.set(0); 2756 2757 blitParams.src = sourceTexture; 2758 blitParams.srcLevel = sourceNativeLevel; 2759 blitParams.srcLayer = 0; 2760 blitParams.srcNormalizedCoords = 2761 mtl::NormalizedCoords(sourceBox.toRect(), sourceTexture->size(sourceNativeLevel)); 2762 blitParams.srcYFlipped = false; 2763 blitParams.dstLuminance = internalFormat.isLUMA(); 2764 blitParams.unpackFlipY = unpackFlipY; 2765 blitParams.unpackPremultiplyAlpha = unpackPremultiplyAlpha; 2766 blitParams.unpackUnmultiplyAlpha = unpackUnmultiplyAlpha; 2767 blitParams.transformLinearToSrgb = sourceAngleFormat.isSRGB; 2768 2769 return displayMtl->getUtils().copyTextureWithDraw(context, cmdEncoder, sourceAngleFormat, 2770 mFormat.actualAngleFormat(), blitParams); 2771} 2772 2773angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context, 2774 const gl::ImageIndex &index, 2775 const gl::Offset &destOffset, 2776 const gl::InternalFormat &internalFormat, 2777 const mtl::MipmapNativeLevel &sourceNativeLevel, 2778 const gl::Box &sourceBox, 2779 const angle::Format &sourceAngleFormat, 2780 bool unpackFlipY, 2781 bool unpackPremultiplyAlpha, 2782 bool unpackUnmultiplyAlpha, 2783 const mtl::TextureRef &sourceTexture) 2784{ 2785 mtl::TextureRef &image = getImage(index); 2786 ASSERT(image && image->valid()); 2787 2788 ContextMtl *contextMtl = mtl::GetImpl(context); 2789 2790 const angle::Format &dstAngleFormat = mFormat.actualAngleFormat(); 2791 const int srcRowPitch = sourceAngleFormat.pixelBytes * sourceBox.width; 2792 const int srcImageSize = srcRowPitch * sourceBox.height; 2793 const int convRowPitch = dstAngleFormat.pixelBytes * sourceBox.width; 2794 const int convImageSize = convRowPitch * sourceBox.height; 2795 angle::MemoryBuffer conversionSrc, conversionDst; 2796 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionSrc.resize(srcImageSize)); 2797 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionDst.resize(convImageSize)); 2798 2799 MTLRegion mtlSrcArea = 2800 MTLRegionMake2D(sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height); 2801 MTLRegion mtlDstArea = 2802 MTLRegionMake2D(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height); 2803 2804 // Read pixels from source to memory: 2805 sourceTexture->getBytes(contextMtl, srcRowPitch, 0, mtlSrcArea, sourceNativeLevel, 0, 2806 conversionSrc.data()); 2807 2808 // Convert to destination format 2809 CopyImageCHROMIUM(conversionSrc.data(), srcRowPitch, sourceAngleFormat.pixelBytes, 0, 2810 sourceAngleFormat.pixelReadFunction, conversionDst.data(), convRowPitch, 2811 dstAngleFormat.pixelBytes, 0, dstAngleFormat.pixelWriteFunction, 2812 internalFormat.format, internalFormat.componentType, sourceBox.width, 2813 sourceBox.height, 1, unpackFlipY, unpackPremultiplyAlpha, 2814 unpackUnmultiplyAlpha); 2815 2816 // Upload to texture 2817 ANGLE_TRY(UploadTextureContents(context, dstAngleFormat, mtlDstArea, mtl::kZeroNativeMipLevel, 2818 0, conversionDst.data(), convRowPitch, 0, false, image)); 2819 2820 return angle::Result::Continue; 2821} 2822 2823} // namespace rx 2824