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