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