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