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