1// 2// Copyright 2019 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6// mtl_resources.mm: 7// Implements wrapper classes for Metal's MTLTexture and MTLBuffer. 8// 9 10#include "libANGLE/renderer/metal/mtl_resources.h" 11 12#include <TargetConditionals.h> 13 14#include <algorithm> 15 16#include "common/debug.h" 17#include "libANGLE/renderer/metal/ContextMtl.h" 18#include "libANGLE/renderer/metal/DisplayMtl.h" 19#include "libANGLE/renderer/metal/mtl_command_buffer.h" 20#include "libANGLE/renderer/metal/mtl_context_device.h" 21#include "libANGLE/renderer/metal/mtl_format_utils.h" 22#include "libANGLE/renderer/metal/mtl_utils.h" 23 24namespace rx 25{ 26namespace mtl 27{ 28namespace 29{ 30inline NSUInteger GetMipSize(NSUInteger baseSize, const MipmapNativeLevel level) 31{ 32 return std::max<NSUInteger>(1, baseSize >> level.get()); 33} 34 35// Asynchronously synchronize the content of a resource between GPU memory and its CPU cache. 36// NOTE: This operation doesn't finish immediately upon function's return. 37template <class T> 38void InvokeCPUMemSync(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder, T *resource) 39{ 40#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 41 if (blitEncoder) 42 { 43 blitEncoder->synchronizeResource(resource); 44 45 resource->resetCPUReadMemNeedSync(); 46 resource->setCPUReadMemSyncPending(true); 47 } 48#endif 49} 50 51template <class T> 52void EnsureCPUMemWillBeSynced(ContextMtl *context, T *resource) 53{ 54#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 55 // Make sure GPU & CPU contents are synchronized. 56 // NOTE: Only MacOS has separated storage for resource on CPU and GPU and needs explicit 57 // synchronization 58 if (resource->get().storageMode == MTLStorageModeManaged && resource->isCPUReadMemNeedSync()) 59 { 60 mtl::BlitCommandEncoder *blitEncoder = context->getBlitCommandEncoder(); 61 InvokeCPUMemSync(context, blitEncoder, resource); 62 } 63#endif 64 resource->resetCPUReadMemNeedSync(); 65} 66 67MTLResourceOptions resourceOptionsForStorageMode(MTLStorageMode storageMode) 68{ 69 switch (storageMode) 70 { 71 case MTLStorageModeShared: 72 return MTLResourceStorageModeShared; 73#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 74 case MTLStorageModeManaged: 75 return MTLResourceStorageModeManaged; 76#endif 77 case MTLStorageModePrivate: 78 return MTLResourceStorageModePrivate; 79 case MTLStorageModeMemoryless: 80 return MTLResourceStorageModeMemoryless; 81#if TARGET_OS_SIMULATOR 82 default: 83 // TODO(http://anglebug.com/8012): Remove me once hacked SDKs are fixed. 84 UNREACHABLE(); 85 return MTLResourceStorageModeShared; 86#endif 87 } 88} 89 90} // namespace 91 92// Resource implementation 93Resource::Resource() : mUsageRef(std::make_shared<UsageRef>()) {} 94 95// Share the GPU usage ref with other resource 96Resource::Resource(Resource *other) : Resource(other->mUsageRef) {} 97Resource::Resource(std::shared_ptr<UsageRef> otherUsageRef) : mUsageRef(std::move(otherUsageRef)) 98{ 99 ASSERT(mUsageRef); 100} 101 102void Resource::reset() 103{ 104 mUsageRef->cmdBufferQueueSerial = 0; 105 resetCPUReadMemDirty(); 106 resetCPUReadMemNeedSync(); 107 resetCPUReadMemSyncPending(); 108} 109 110bool Resource::isBeingUsedByGPU(Context *context) const 111{ 112 return context->cmdQueue().isResourceBeingUsedByGPU(this); 113} 114 115bool Resource::hasPendingWorks(Context *context) const 116{ 117 return context->cmdQueue().resourceHasPendingWorks(this); 118} 119 120bool Resource::hasPendingRenderWorks(Context *context) const 121{ 122 return context->cmdQueue().resourceHasPendingRenderWorks(this); 123} 124 125void Resource::setUsedByCommandBufferWithQueueSerial(uint64_t serial, 126 bool writing, 127 bool isRenderCommand) 128{ 129 if (writing) 130 { 131 mUsageRef->cpuReadMemNeedSync = true; 132 mUsageRef->cpuReadMemDirty = true; 133 } 134 135 mUsageRef->cmdBufferQueueSerial = std::max(mUsageRef->cmdBufferQueueSerial, serial); 136 137 if (isRenderCommand) 138 { 139 if (writing) 140 { 141 mUsageRef->lastWritingRenderEncoderSerial = mUsageRef->cmdBufferQueueSerial; 142 } 143 else 144 { 145 mUsageRef->lastReadingRenderEncoderSerial = mUsageRef->cmdBufferQueueSerial; 146 } 147 } 148} 149 150// Texture implemenetation 151/** static */ 152angle::Result Texture::Make2DTexture(ContextMtl *context, 153 const Format &format, 154 uint32_t width, 155 uint32_t height, 156 uint32_t mips, 157 bool renderTargetOnly, 158 bool allowFormatView, 159 TextureRef *refOut) 160{ 161 ANGLE_MTL_OBJC_SCOPE 162 { 163 MTLTextureDescriptor *desc = 164 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat 165 width:width 166 height:height 167 mipmapped:mips == 0 || mips > 1]; 168 return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut); 169 } // ANGLE_MTL_OBJC_SCOPE 170} 171 172/** static */ 173angle::Result Texture::MakeMemoryLess2DTexture(ContextMtl *context, 174 const Format &format, 175 uint32_t width, 176 uint32_t height, 177 TextureRef *refOut) 178{ 179 ANGLE_MTL_OBJC_SCOPE 180 { 181 MTLTextureDescriptor *desc = 182 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat 183 width:width 184 height:height 185 mipmapped:NO]; 186 187 return MakeTexture(context, format, desc, 1, true, false, true, refOut); 188 } // ANGLE_MTL_OBJC_SCOPE 189} 190/** static */ 191angle::Result Texture::MakeCubeTexture(ContextMtl *context, 192 const Format &format, 193 uint32_t size, 194 uint32_t mips, 195 bool renderTargetOnly, 196 bool allowFormatView, 197 TextureRef *refOut) 198{ 199 ANGLE_MTL_OBJC_SCOPE 200 { 201 MTLTextureDescriptor *desc = 202 [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:format.metalFormat 203 size:size 204 mipmapped:mips == 0 || mips > 1]; 205 206 return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut); 207 } // ANGLE_MTL_OBJC_SCOPE 208} 209 210/** static */ 211angle::Result Texture::Make2DMSTexture(ContextMtl *context, 212 const Format &format, 213 uint32_t width, 214 uint32_t height, 215 uint32_t samples, 216 bool renderTargetOnly, 217 bool allowFormatView, 218 TextureRef *refOut) 219{ 220 ANGLE_MTL_OBJC_SCOPE 221 { 222 MTLTextureDescriptor *desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE]; 223 desc.textureType = MTLTextureType2DMultisample; 224 desc.pixelFormat = format.metalFormat; 225 desc.width = width; 226 desc.height = height; 227 desc.mipmapLevelCount = 1; 228 desc.sampleCount = samples; 229 230 return MakeTexture(context, format, desc, 1, renderTargetOnly, allowFormatView, refOut); 231 } // ANGLE_MTL_OBJC_SCOPE 232} 233 234/** static */ 235angle::Result Texture::Make2DArrayTexture(ContextMtl *context, 236 const Format &format, 237 uint32_t width, 238 uint32_t height, 239 uint32_t mips, 240 uint32_t arrayLength, 241 bool renderTargetOnly, 242 bool allowFormatView, 243 TextureRef *refOut) 244{ 245 ANGLE_MTL_OBJC_SCOPE 246 { 247 // Use texture2DDescriptorWithPixelFormat to calculate full range mipmap range: 248 MTLTextureDescriptor *desc = 249 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat 250 width:width 251 height:height 252 mipmapped:mips == 0 || mips > 1]; 253 254 desc.textureType = MTLTextureType2DArray; 255 desc.arrayLength = arrayLength; 256 257 return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut); 258 } // ANGLE_MTL_OBJC_SCOPE 259} 260 261/** static */ 262angle::Result Texture::Make3DTexture(ContextMtl *context, 263 const Format &format, 264 uint32_t width, 265 uint32_t height, 266 uint32_t depth, 267 uint32_t mips, 268 bool renderTargetOnly, 269 bool allowFormatView, 270 TextureRef *refOut) 271{ 272 ANGLE_MTL_OBJC_SCOPE 273 { 274 // Use texture2DDescriptorWithPixelFormat to calculate full range mipmap range: 275 const uint32_t maxDimen = std::max({width, height, depth}); 276 MTLTextureDescriptor *desc = 277 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat 278 width:maxDimen 279 height:maxDimen 280 mipmapped:mips == 0 || mips > 1]; 281 282 desc.textureType = MTLTextureType3D; 283 desc.width = width; 284 desc.height = height; 285 desc.depth = depth; 286 287 return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut); 288 } // ANGLE_MTL_OBJC_SCOPE 289} 290 291/** static */ 292angle::Result Texture::MakeTexture(ContextMtl *context, 293 const Format &mtlFormat, 294 MTLTextureDescriptor *desc, 295 uint32_t mips, 296 bool renderTargetOnly, 297 bool allowFormatView, 298 TextureRef *refOut) 299{ 300 return MakeTexture(context, mtlFormat, desc, mips, renderTargetOnly, allowFormatView, false, 301 refOut); 302} 303 304angle::Result Texture::MakeTexture(ContextMtl *context, 305 const Format &mtlFormat, 306 MTLTextureDescriptor *desc, 307 uint32_t mips, 308 bool renderTargetOnly, 309 bool allowFormatView, 310 bool memoryLess, 311 TextureRef *refOut) 312{ 313 if (desc.pixelFormat == MTLPixelFormatInvalid) 314 { 315 return angle::Result::Stop; 316 } 317 318 ASSERT(refOut); 319 Texture *newTexture = 320 new Texture(context, desc, mips, renderTargetOnly, allowFormatView, memoryLess); 321 ANGLE_MTL_CHECK(context, newTexture->valid(), GL_OUT_OF_MEMORY); 322 refOut->reset(newTexture); 323 324 if (!mtlFormat.hasDepthAndStencilBits()) 325 { 326 refOut->get()->setColorWritableMask(GetEmulatedColorWriteMask(mtlFormat)); 327 } 328 329 size_t estimatedBytes = EstimateTextureSizeInBytes( 330 mtlFormat, desc.width, desc.height, desc.depth, desc.sampleCount, desc.mipmapLevelCount); 331 if (refOut) 332 { 333 refOut->get()->setEstimatedByteSize(memoryLess ? 0 : estimatedBytes); 334 } 335 336 return angle::Result::Continue; 337} 338 339angle::Result Texture::MakeTexture(ContextMtl *context, 340 const Format &mtlFormat, 341 MTLTextureDescriptor *desc, 342 IOSurfaceRef surfaceRef, 343 NSUInteger slice, 344 bool renderTargetOnly, 345 TextureRef *refOut) 346{ 347 348 ASSERT(refOut); 349 Texture *newTexture = new Texture(context, desc, surfaceRef, slice, renderTargetOnly); 350 ANGLE_MTL_CHECK(context, newTexture->valid(), GL_OUT_OF_MEMORY); 351 refOut->reset(newTexture); 352 if (!mtlFormat.hasDepthAndStencilBits()) 353 { 354 refOut->get()->setColorWritableMask(GetEmulatedColorWriteMask(mtlFormat)); 355 } 356 357 size_t estimatedBytes = EstimateTextureSizeInBytes( 358 mtlFormat, desc.width, desc.height, desc.depth, desc.sampleCount, desc.mipmapLevelCount); 359 refOut->get()->setEstimatedByteSize(estimatedBytes); 360 361 return angle::Result::Continue; 362} 363 364bool needMultisampleColorFormatShaderReadWorkaround(ContextMtl *context, MTLTextureDescriptor *desc) 365{ 366 return desc.sampleCount > 1 && 367 context->getDisplay() 368 ->getFeatures() 369 .multisampleColorFormatShaderReadWorkaround.enabled && 370 context->getNativeFormatCaps(desc.pixelFormat).colorRenderable; 371} 372 373/** static */ 374TextureRef Texture::MakeFromMetal(id<MTLTexture> metalTexture) 375{ 376 ANGLE_MTL_OBJC_SCOPE 377 { 378 return TextureRef(new Texture(metalTexture)); 379 } 380} 381 382Texture::Texture(id<MTLTexture> metalTexture) 383 : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll)) 384{ 385 set(metalTexture); 386} 387 388Texture::Texture(std::shared_ptr<UsageRef> usageRef, 389 id<MTLTexture> metalTexture, 390 std::shared_ptr<MTLColorWriteMask> colorWriteMask) 391 : Resource(std::move(usageRef)), mColorWritableMask(std::move(colorWriteMask)) 392{ 393 set(metalTexture); 394} 395 396Texture::Texture(ContextMtl *context, 397 MTLTextureDescriptor *desc, 398 uint32_t mips, 399 bool renderTargetOnly, 400 bool allowFormatView) 401 : Texture(context, desc, mips, renderTargetOnly, allowFormatView, false) 402{} 403 404Texture::Texture(ContextMtl *context, 405 MTLTextureDescriptor *desc, 406 uint32_t mips, 407 bool renderTargetOnly, 408 bool allowFormatView, 409 bool memoryLess) 410 : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll)) 411{ 412 ANGLE_MTL_OBJC_SCOPE 413 { 414 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 415 416 if (mips > 1 && mips < desc.mipmapLevelCount) 417 { 418 desc.mipmapLevelCount = mips; 419 } 420 421 // Every texture will support being rendered for now 422 desc.usage = 0; 423 424 if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable()) 425 { 426 desc.usage |= MTLTextureUsageRenderTarget; 427 } 428 429 if (memoryLess) 430 { 431#if (TARGET_OS_IOS || TARGET_OS_TV) && !TARGET_OS_MACCATALYST 432 desc.resourceOptions = MTLResourceStorageModeMemoryless; 433#else 434 desc.resourceOptions = MTLResourceStorageModePrivate; 435#endif 436 } 437 else if (context->getNativeFormatCaps(desc.pixelFormat).depthRenderable || 438 desc.textureType == MTLTextureType2DMultisample) 439 { 440 // Metal doesn't support host access to depth stencil texture's data 441 desc.resourceOptions = MTLResourceStorageModePrivate; 442 } 443 444 if (!renderTargetOnly || needMultisampleColorFormatShaderReadWorkaround(context, desc)) 445 { 446 desc.usage = desc.usage | MTLTextureUsageShaderRead; 447 if (context->getNativeFormatCaps(desc.pixelFormat).writable) 448 { 449 desc.usage = desc.usage | MTLTextureUsageShaderWrite; 450 } 451 } 452 if (desc.pixelFormat == MTLPixelFormatDepth32Float_Stencil8) 453 { 454 ASSERT(allowFormatView); 455 } 456#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 457 if (desc.pixelFormat == MTLPixelFormatDepth24Unorm_Stencil8) 458 { 459 ASSERT(allowFormatView); 460 } 461#endif 462 463 if (allowFormatView) 464 { 465 desc.usage = desc.usage | MTLTextureUsagePixelFormatView; 466 } 467 468 set(metalDevice.newTextureWithDescriptor(desc)); 469 470 mCreationDesc.retainAssign(desc); 471 } 472} 473 474Texture::Texture(ContextMtl *context, 475 MTLTextureDescriptor *desc, 476 IOSurfaceRef iosurface, 477 NSUInteger plane, 478 bool renderTargetOnly) 479 : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll)) 480{ 481 ANGLE_MTL_OBJC_SCOPE 482 { 483 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 484 485 // Every texture will support being rendered for now 486 desc.usage = MTLTextureUsagePixelFormatView; 487 488 if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable()) 489 { 490 desc.usage |= MTLTextureUsageRenderTarget; 491 } 492 493#if (TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH) && !TARGET_OS_MACCATALYST 494 desc.resourceOptions = MTLResourceStorageModeShared; 495#else 496 desc.resourceOptions = MTLResourceStorageModeManaged; 497#endif 498 499 if (!renderTargetOnly) 500 { 501 desc.usage = desc.usage | MTLTextureUsageShaderRead; 502 if (context->getNativeFormatCaps(desc.pixelFormat).writable) 503 { 504 desc.usage = desc.usage | MTLTextureUsageShaderWrite; 505 } 506 } 507 set(metalDevice.newTextureWithDescriptor(desc, iosurface, plane)); 508 } 509} 510 511Texture::Texture(Texture *original, MTLPixelFormat pixelFormat) 512 : Resource(original), 513 mColorWritableMask(original->mColorWritableMask) // Share color write mask property 514{ 515 ANGLE_MTL_OBJC_SCOPE 516 { 517 auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat]; 518 519 set([view ANGLE_MTL_AUTORELEASE]); 520 // Texture views consume no additional memory 521 mEstimatedByteSize = 0; 522 } 523} 524 525Texture::Texture(Texture *original, 526 MTLPixelFormat pixelFormat, 527 MTLTextureType textureType, 528 NSRange levels, 529 NSRange slices) 530 : Resource(original), 531 mColorWritableMask(original->mColorWritableMask) // Share color write mask property 532{ 533 ANGLE_MTL_OBJC_SCOPE 534 { 535 auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat 536 textureType:textureType 537 levels:levels 538 slices:slices]; 539 540 set([view ANGLE_MTL_AUTORELEASE]); 541 // Texture views consume no additional memory 542 mEstimatedByteSize = 0; 543 } 544} 545 546Texture::Texture(Texture *original, 547 MTLPixelFormat pixelFormat, 548 MTLTextureType textureType, 549 NSRange levels, 550 NSRange slices, 551 const TextureSwizzleChannels &swizzle) 552 : Resource(original), 553 mColorWritableMask(original->mColorWritableMask) // Share color write mask property 554{ 555#if ANGLE_MTL_SWIZZLE_AVAILABLE 556 ANGLE_MTL_OBJC_SCOPE 557 { 558 auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat 559 textureType:textureType 560 levels:levels 561 slices:slices 562 swizzle:swizzle]; 563 564 set([view ANGLE_MTL_AUTORELEASE]); 565 // Texture views consume no additional memory 566 mEstimatedByteSize = 0; 567 } 568#else 569 UNREACHABLE(); 570#endif 571} 572 573void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder) 574{ 575 InvokeCPUMemSync(context, blitEncoder, this); 576} 577 578void Texture::syncContentIfNeeded(ContextMtl *context) 579{ 580 EnsureCPUMemWillBeSynced(context, this); 581} 582 583bool Texture::isCPUAccessible() const 584{ 585#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 586 if (get().storageMode == MTLStorageModeManaged) 587 { 588 return true; 589 } 590#endif 591 return get().storageMode == MTLStorageModeShared; 592} 593 594bool Texture::isShaderReadable() const 595{ 596 return get().usage & MTLTextureUsageShaderRead; 597} 598 599bool Texture::isShaderWritable() const 600{ 601 return get().usage & MTLTextureUsageShaderWrite; 602} 603 604bool Texture::supportFormatView() const 605{ 606 return get().usage & MTLTextureUsagePixelFormatView; 607} 608 609void Texture::replace2DRegion(ContextMtl *context, 610 const MTLRegion ®ion, 611 const MipmapNativeLevel &mipmapLevel, 612 uint32_t slice, 613 const uint8_t *data, 614 size_t bytesPerRow) 615{ 616 ASSERT(region.size.depth == 1); 617 replaceRegion(context, region, mipmapLevel, slice, data, bytesPerRow, 0); 618} 619 620void Texture::replaceRegion(ContextMtl *context, 621 const MTLRegion ®ion, 622 const MipmapNativeLevel &mipmapLevel, 623 uint32_t slice, 624 const uint8_t *data, 625 size_t bytesPerRow, 626 size_t bytesPer2DImage) 627{ 628 if (mipmapLevel.get() >= this->mipmapLevels()) 629 { 630 return; 631 } 632 633 ASSERT(isCPUAccessible()); 634 635 CommandQueue &cmdQueue = context->cmdQueue(); 636 637 syncContentIfNeeded(context); 638 639 // NOTE(hqle): what if multiple contexts on multiple threads are using this texture? 640 if (this->isBeingUsedByGPU(context)) 641 { 642 context->flushCommandBuffer(mtl::NoWait); 643 } 644 645 cmdQueue.ensureResourceReadyForCPU(this); 646 647 if (textureType() != MTLTextureType3D) 648 { 649 bytesPer2DImage = 0; 650 } 651 652 [get() replaceRegion:region 653 mipmapLevel:mipmapLevel.get() 654 slice:slice 655 withBytes:data 656 bytesPerRow:bytesPerRow 657 bytesPerImage:bytesPer2DImage]; 658} 659 660void Texture::getBytes(ContextMtl *context, 661 size_t bytesPerRow, 662 size_t bytesPer2DInage, 663 const MTLRegion ®ion, 664 const MipmapNativeLevel &mipmapLevel, 665 uint32_t slice, 666 uint8_t *dataOut) 667{ 668 ASSERT(isCPUAccessible()); 669 670 CommandQueue &cmdQueue = context->cmdQueue(); 671 672 syncContentIfNeeded(context); 673 674 // NOTE(hqle): what if multiple contexts on multiple threads are using this texture? 675 if (this->isBeingUsedByGPU(context)) 676 { 677 context->flushCommandBuffer(mtl::NoWait); 678 } 679 680 cmdQueue.ensureResourceReadyForCPU(this); 681 682 [get() getBytes:dataOut 683 bytesPerRow:bytesPerRow 684 bytesPerImage:bytesPer2DInage 685 fromRegion:region 686 mipmapLevel:mipmapLevel.get() 687 slice:slice]; 688} 689 690TextureRef Texture::createCubeFaceView(uint32_t face) 691{ 692 ANGLE_MTL_OBJC_SCOPE 693 { 694 switch (textureType()) 695 { 696 case MTLTextureTypeCube: 697 return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D, 698 NSMakeRange(0, mipmapLevels()), 699 NSMakeRange(face, 1))); 700 default: 701 UNREACHABLE(); 702 return nullptr; 703 } 704 } 705} 706 707TextureRef Texture::createSliceMipView(uint32_t slice, const MipmapNativeLevel &level) 708{ 709 ANGLE_MTL_OBJC_SCOPE 710 { 711 switch (textureType()) 712 { 713 case MTLTextureTypeCube: 714 case MTLTextureType2D: 715 case MTLTextureType2DArray: 716 return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D, 717 NSMakeRange(level.get(), 1), NSMakeRange(slice, 1))); 718 default: 719 UNREACHABLE(); 720 return nullptr; 721 } 722 } 723} 724 725TextureRef Texture::createMipView(const MipmapNativeLevel &level) 726{ 727 ANGLE_MTL_OBJC_SCOPE 728 { 729 NSUInteger slices = cubeFacesOrArrayLength(); 730 return TextureRef(new Texture(this, pixelFormat(), textureType(), 731 NSMakeRange(level.get(), 1), NSMakeRange(0, slices))); 732 } 733} 734 735TextureRef Texture::createMipsView(const MipmapNativeLevel &baseLevel, uint32_t levels) 736{ 737 ANGLE_MTL_OBJC_SCOPE 738 { 739 NSUInteger slices = cubeFacesOrArrayLength(); 740 return TextureRef(new Texture(this, pixelFormat(), textureType(), 741 NSMakeRange(baseLevel.get(), levels), 742 NSMakeRange(0, slices))); 743 } 744} 745 746TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format) 747{ 748 ASSERT(supportFormatView()); 749 return TextureRef(new Texture(this, format)); 750} 751 752TextureRef Texture::createShaderImageView2D(const MipmapNativeLevel &level, 753 int layer, 754 MTLPixelFormat format) 755{ 756 ASSERT(isShaderReadable()); 757 ASSERT(isShaderWritable()); 758 ASSERT(format == pixelFormat() || supportFormatView()); 759 ASSERT(textureType() != MTLTextureType3D); 760 return TextureRef(new Texture(this, format, MTLTextureType2D, NSMakeRange(level.get(), 1), 761 NSMakeRange(layer, 1))); 762} 763 764TextureRef Texture::createViewWithCompatibleFormat(MTLPixelFormat format) 765{ 766 return TextureRef(new Texture(this, format)); 767} 768 769TextureRef Texture::createMipsSwizzleView(const MipmapNativeLevel &baseLevel, 770 uint32_t levels, 771 MTLPixelFormat format, 772 const TextureSwizzleChannels &swizzle) 773{ 774#if ANGLE_MTL_SWIZZLE_AVAILABLE 775 return TextureRef(new Texture(this, format, textureType(), NSMakeRange(baseLevel.get(), levels), 776 NSMakeRange(0, cubeFacesOrArrayLength()), swizzle)); 777#else 778 WARN() << "Texture swizzle is not supported on pre iOS 13.0 and macOS 15.0"; 779 UNIMPLEMENTED(); 780 return createMipsView(baseLevel, levels); 781#endif 782} 783 784MTLPixelFormat Texture::pixelFormat() const 785{ 786 return get().pixelFormat; 787} 788 789MTLTextureType Texture::textureType() const 790{ 791 return get().textureType; 792} 793 794uint32_t Texture::mipmapLevels() const 795{ 796 return static_cast<uint32_t>(get().mipmapLevelCount); 797} 798 799uint32_t Texture::arrayLength() const 800{ 801 return static_cast<uint32_t>(get().arrayLength); 802} 803 804uint32_t Texture::cubeFaces() const 805{ 806 if (textureType() == MTLTextureTypeCube) 807 { 808 return 6; 809 } 810 return 1; 811} 812 813uint32_t Texture::cubeFacesOrArrayLength() const 814{ 815 if (textureType() == MTLTextureTypeCube) 816 { 817 return 6; 818 } 819 return arrayLength(); 820} 821 822uint32_t Texture::width(const MipmapNativeLevel &level) const 823{ 824 return static_cast<uint32_t>(GetMipSize(get().width, level)); 825} 826 827uint32_t Texture::height(const MipmapNativeLevel &level) const 828{ 829 return static_cast<uint32_t>(GetMipSize(get().height, level)); 830} 831 832uint32_t Texture::depth(const MipmapNativeLevel &level) const 833{ 834 return static_cast<uint32_t>(GetMipSize(get().depth, level)); 835} 836 837gl::Extents Texture::size(const MipmapNativeLevel &level) const 838{ 839 gl::Extents re; 840 841 re.width = width(level); 842 re.height = height(level); 843 re.depth = depth(level); 844 845 return re; 846} 847 848gl::Extents Texture::size(const ImageNativeIndex &index) const 849{ 850 gl::Extents extents = size(index.getNativeLevel()); 851 852 if (index.hasLayer()) 853 { 854 extents.depth = 1; 855 } 856 857 return extents; 858} 859 860uint32_t Texture::samples() const 861{ 862 return static_cast<uint32_t>(get().sampleCount); 863} 864 865bool Texture::hasIOSurface() const 866{ 867 return (get().iosurface) != nullptr; 868} 869 870bool Texture::sameTypeAndDimemsionsAs(const TextureRef &other) const 871{ 872 return textureType() == other->textureType() && pixelFormat() == other->pixelFormat() && 873 mipmapLevels() == other->mipmapLevels() && 874 cubeFacesOrArrayLength() == other->cubeFacesOrArrayLength() && 875 widthAt0() == other->widthAt0() && heightAt0() == other->heightAt0() && 876 depthAt0() == other->depthAt0(); 877} 878 879angle::Result Texture::resize(ContextMtl *context, uint32_t width, uint32_t height) 880{ 881 // Resizing texture view is not supported. 882 ASSERT(mCreationDesc); 883 884 ANGLE_MTL_OBJC_SCOPE 885 { 886 MTLTextureDescriptor *newDesc = [[mCreationDesc.get() copy] ANGLE_MTL_AUTORELEASE]; 887 newDesc.width = width; 888 newDesc.height = height; 889 auto newTexture = context->getMetalDevice().newTextureWithDescriptor(newDesc); 890 ANGLE_CHECK_GL_ALLOC(context, newTexture); 891 mCreationDesc.retainAssign(newDesc); 892 set(newTexture); 893 // Reset reference counter 894 Resource::reset(); 895 } 896 897 return angle::Result::Continue; 898} 899 900TextureRef Texture::getLinearColorView() 901{ 902 if (mLinearColorView) 903 { 904 return mLinearColorView; 905 } 906 907 switch (pixelFormat()) 908 { 909 case MTLPixelFormatRGBA8Unorm_sRGB: 910 mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatRGBA8Unorm); 911 break; 912 case MTLPixelFormatBGRA8Unorm_sRGB: 913 mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatBGRA8Unorm); 914 break; 915 default: 916 // NOTE(hqle): Not all sRGB formats are supported yet. 917 UNREACHABLE(); 918 } 919 920 return mLinearColorView; 921} 922 923TextureRef Texture::getReadableCopy(ContextMtl *context, 924 mtl::BlitCommandEncoder *encoder, 925 const uint32_t levelToCopy, 926 const uint32_t sliceToCopy, 927 const MTLRegion &areaToCopy) 928{ 929 gl::Extents firstLevelSize = size(kZeroNativeMipLevel); 930 if (!mReadCopy || mReadCopy->get().width < static_cast<size_t>(firstLevelSize.width) || 931 mReadCopy->get().height < static_cast<size_t>(firstLevelSize.height)) 932 { 933 // Create a texture that big enough to store the first level data and any smaller level 934 ANGLE_MTL_OBJC_SCOPE 935 { 936 auto desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE]; 937 desc.textureType = get().textureType; 938 desc.pixelFormat = get().pixelFormat; 939 desc.width = firstLevelSize.width; 940 desc.height = firstLevelSize.height; 941 desc.depth = 1; 942 desc.arrayLength = 1; 943 desc.resourceOptions = MTLResourceStorageModePrivate; 944 desc.sampleCount = get().sampleCount; 945 desc.usage = MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView; 946 mReadCopy.reset(new Texture(context->getMetalDevice().newTextureWithDescriptor(desc))); 947 } // ANGLE_MTL_OBJC_SCOPE 948 } 949 950 ASSERT(encoder); 951 952 encoder->copyTexture(shared_from_this(), sliceToCopy, mtl::MipmapNativeLevel(levelToCopy), 953 mReadCopy, 0, mtl::kZeroNativeMipLevel, 1, 1); 954 955 return mReadCopy; 956} 957 958void Texture::releaseReadableCopy() 959{ 960 mReadCopy = nullptr; 961} 962 963TextureRef Texture::getStencilView() 964{ 965 if (mStencilView) 966 { 967 return mStencilView; 968 } 969 970 switch (pixelFormat()) 971 { 972 case MTLPixelFormatStencil8: 973 case MTLPixelFormatX32_Stencil8: 974#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 975 case MTLPixelFormatX24_Stencil8: 976#endif 977 return mStencilView = shared_from_this(); 978#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 979 case MTLPixelFormatDepth24Unorm_Stencil8: 980 mStencilView = createViewWithDifferentFormat(MTLPixelFormatX24_Stencil8); 981 break; 982#endif 983 case MTLPixelFormatDepth32Float_Stencil8: 984 mStencilView = createViewWithDifferentFormat(MTLPixelFormatX32_Stencil8); 985 break; 986 default: 987 UNREACHABLE(); 988 } 989 990 return mStencilView; 991} 992 993TextureRef Texture::parentTexture() 994{ 995 if (mParentTexture) 996 { 997 return mParentTexture; 998 } 999 1000 if (!get().parentTexture) 1001 { 1002 // Doesn't have parent. 1003 return nullptr; 1004 } 1005 1006 // Lazily construct parent's Texture object from parent's MTLTexture. 1007 // Note that the constructed Texture object is not the same as the same original object that 1008 // creates this view. However, it will share the same usageRef and MTLTexture with the 1009 // original Texture object. We do this to avoid cyclic reference between original Texture 1010 // and its view. 1011 // 1012 // For example, the original Texture object might keep a ref to its stencil view. Had we 1013 // kept the original object's ref in the stencil view, there would have been a cyclic 1014 // reference. 1015 // 1016 // This is OK because even though the Texture objects are not the same, they refer to same 1017 // MTLTexture and usageRef. 1018 mParentTexture.reset(new Texture(mUsageRef, get().parentTexture, mColorWritableMask)); 1019 1020 return mParentTexture; 1021} 1022MipmapNativeLevel Texture::parentRelativeLevel() 1023{ 1024 return mtl::GetNativeMipLevel(static_cast<uint32_t>(get().parentRelativeLevel), 0); 1025} 1026uint32_t Texture::parentRelativeSlice() 1027{ 1028 return static_cast<uint32_t>(get().parentRelativeSlice); 1029} 1030 1031void Texture::set(id<MTLTexture> metalTexture) 1032{ 1033 ParentClass::set(metalTexture); 1034 // Reset stencil view 1035 mStencilView = nullptr; 1036 mLinearColorView = nullptr; 1037 1038 mParentTexture = nullptr; 1039} 1040 1041// Buffer implementation 1042 1043MTLStorageMode Buffer::getStorageModeForSharedBuffer(ContextMtl *contextMtl) 1044{ 1045#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1046 if (ANGLE_UNLIKELY(contextMtl->getDisplay()->getFeatures().forceBufferGPUStorage.enabled)) 1047 { 1048 return MTLStorageModeManaged; 1049 } 1050#endif 1051 return MTLStorageModeShared; 1052} 1053 1054MTLStorageMode Buffer::getStorageModeForUsage(ContextMtl *contextMtl, Usage usage) 1055{ 1056#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1057 bool hasCpuAccess = false; 1058 switch (usage) 1059 { 1060 case Usage::StaticCopy: 1061 case Usage::StaticDraw: 1062 case Usage::StaticRead: 1063 case Usage::DynamicRead: 1064 case Usage::StreamRead: 1065 hasCpuAccess = true; 1066 break; 1067 default: 1068 break; 1069 } 1070 const auto &features = contextMtl->getDisplay()->getFeatures(); 1071 if (hasCpuAccess) 1072 { 1073 if (features.alwaysUseManagedStorageModeForBuffers.enabled || 1074 ANGLE_UNLIKELY(features.forceBufferGPUStorage.enabled)) 1075 { 1076 return MTLStorageModeManaged; 1077 } 1078 return MTLStorageModeShared; 1079 } 1080 if (contextMtl->getMetalDevice().hasUnifiedMemory() || 1081 features.alwaysUseSharedStorageModeForBuffers.enabled) 1082 { 1083 return MTLStorageModeShared; 1084 } 1085 return MTLStorageModeManaged; 1086#else 1087 ANGLE_UNUSED_VARIABLE(contextMtl); 1088 ANGLE_UNUSED_VARIABLE(usage); 1089 return MTLStorageModeShared; 1090#endif 1091} 1092 1093angle::Result Buffer::MakeBuffer(ContextMtl *context, 1094 size_t size, 1095 const uint8_t *data, 1096 BufferRef *bufferOut) 1097{ 1098 auto storageMode = getStorageModeForUsage(context, Usage::DynamicDraw); 1099 return MakeBufferWithStorageMode(context, storageMode, size, data, bufferOut); 1100} 1101 1102angle::Result Buffer::MakeBufferWithStorageMode(ContextMtl *context, 1103 MTLStorageMode storageMode, 1104 size_t size, 1105 const uint8_t *data, 1106 BufferRef *bufferOut) 1107{ 1108 bufferOut->reset(new Buffer(context, storageMode, size, data)); 1109 1110 if (!(*bufferOut) || !(*bufferOut)->get()) 1111 { 1112 ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY); 1113 } 1114 1115 return angle::Result::Continue; 1116} 1117 1118Buffer::Buffer(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data) 1119{ 1120 (void)reset(context, storageMode, size, data); 1121} 1122 1123angle::Result Buffer::reset(ContextMtl *context, 1124 MTLStorageMode storageMode, 1125 size_t size, 1126 const uint8_t *data) 1127{ 1128 auto options = resourceOptionsForStorageMode(storageMode); 1129 set([&]() -> AutoObjCPtr<id<MTLBuffer>> { 1130 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 1131 if (size > [metalDevice maxBufferLength]) 1132 { 1133 return nullptr; 1134 } 1135 if (data) 1136 { 1137 return metalDevice.newBufferWithBytes(data, size, options); 1138 } 1139 return metalDevice.newBufferWithLength(size, options); 1140 }()); 1141 // Reset command buffer's reference serial 1142 Resource::reset(); 1143 1144 return angle::Result::Continue; 1145} 1146 1147void Buffer::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder) 1148{ 1149 InvokeCPUMemSync(context, blitEncoder, this); 1150} 1151 1152const uint8_t *Buffer::mapReadOnly(ContextMtl *context) 1153{ 1154 return mapWithOpt(context, true, false); 1155} 1156 1157uint8_t *Buffer::map(ContextMtl *context) 1158{ 1159 return mapWithOpt(context, false, false); 1160} 1161 1162uint8_t *Buffer::mapWithOpt(ContextMtl *context, bool readonly, bool noSync) 1163{ 1164 mMapReadOnly = readonly; 1165 1166 if (!noSync && (isCPUReadMemSyncPending() || isCPUReadMemNeedSync() || !readonly)) 1167 { 1168 CommandQueue &cmdQueue = context->cmdQueue(); 1169 1170 EnsureCPUMemWillBeSynced(context, this); 1171 1172 if (this->isBeingUsedByGPU(context)) 1173 { 1174 context->flushCommandBuffer(mtl::NoWait); 1175 } 1176 1177 cmdQueue.ensureResourceReadyForCPU(this); 1178 resetCPUReadMemSyncPending(); 1179 } 1180 1181 return reinterpret_cast<uint8_t *>([get() contents]); 1182} 1183 1184void Buffer::unmap(ContextMtl *context) 1185{ 1186 flush(context, 0, size()); 1187 1188 // Reset read only flag 1189 mMapReadOnly = true; 1190} 1191 1192void Buffer::unmapNoFlush(ContextMtl *context) 1193{ 1194 mMapReadOnly = true; 1195} 1196 1197void Buffer::unmapAndFlushSubset(ContextMtl *context, size_t offsetWritten, size_t sizeWritten) 1198{ 1199#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1200 flush(context, offsetWritten, sizeWritten); 1201#endif 1202 mMapReadOnly = true; 1203} 1204 1205void Buffer::flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten) 1206{ 1207#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1208 if (!mMapReadOnly) 1209 { 1210 if (get().storageMode == MTLStorageModeManaged) 1211 { 1212 size_t bufferSize = size(); 1213 size_t startOffset = std::min(offsetWritten, bufferSize); 1214 size_t endOffset = std::min(offsetWritten + sizeWritten, bufferSize); 1215 size_t clampedSize = endOffset - startOffset; 1216 if (clampedSize > 0) 1217 { 1218 [get() didModifyRange:NSMakeRange(startOffset, clampedSize)]; 1219 } 1220 } 1221 } 1222#endif 1223} 1224 1225size_t Buffer::size() const 1226{ 1227 return get().length; 1228} 1229 1230MTLStorageMode Buffer::storageMode() const 1231{ 1232 return get().storageMode; 1233} 1234} // namespace mtl 1235} // namespace rx 1236