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 MTLTextureDescriptor *desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE]; 227 desc.textureType = MTLTextureType2DMultisample; 228 desc.pixelFormat = format.metalFormat; 229 desc.width = width; 230 desc.height = height; 231 desc.mipmapLevelCount = 1; 232 desc.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_MTL_CHECK(context, newTexture->valid(), GL_OUT_OF_MEMORY); 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_MTL_CHECK(context, newTexture->valid(), GL_OUT_OF_MEMORY); 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 481 mCreationDesc.retainAssign(desc); 482 } 483} 484 485Texture::Texture(ContextMtl *context, 486 MTLTextureDescriptor *desc, 487 IOSurfaceRef iosurface, 488 NSUInteger plane, 489 bool renderTargetOnly) 490 : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll)) 491{ 492 ANGLE_MTL_OBJC_SCOPE 493 { 494 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 495 496 // Every texture will support being rendered for now 497 desc.usage = MTLTextureUsagePixelFormatView; 498 499 if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable()) 500 { 501 desc.usage |= MTLTextureUsageRenderTarget; 502 } 503 504#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 505 desc.resourceOptions = MTLResourceStorageModeManaged; 506#else 507 desc.resourceOptions = MTLResourceStorageModeShared; 508#endif 509 510 if (!renderTargetOnly) 511 { 512 desc.usage = desc.usage | MTLTextureUsageShaderRead; 513 if (context->getNativeFormatCaps(desc.pixelFormat).writable) 514 { 515 desc.usage = desc.usage | MTLTextureUsageShaderWrite; 516 } 517 } 518 set(metalDevice.newTextureWithDescriptor(desc, iosurface, plane)); 519 } 520} 521 522Texture::Texture(Texture *original, MTLPixelFormat pixelFormat) 523 : Resource(original), 524 mColorWritableMask(original->mColorWritableMask) // Share color write mask property 525{ 526 ANGLE_MTL_OBJC_SCOPE 527 { 528 auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat]; 529 530 set([view ANGLE_MTL_AUTORELEASE]); 531 // Texture views consume no additional memory 532 mEstimatedByteSize = 0; 533 } 534} 535 536Texture::Texture(Texture *original, 537 MTLPixelFormat pixelFormat, 538 MTLTextureType textureType, 539 NSRange levels, 540 NSRange slices) 541 : Resource(original), 542 mColorWritableMask(original->mColorWritableMask) // Share color write mask property 543{ 544 ANGLE_MTL_OBJC_SCOPE 545 { 546 auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat 547 textureType:textureType 548 levels:levels 549 slices:slices]; 550 551 set([view ANGLE_MTL_AUTORELEASE]); 552 // Texture views consume no additional memory 553 mEstimatedByteSize = 0; 554 } 555} 556 557Texture::Texture(Texture *original, 558 MTLPixelFormat pixelFormat, 559 MTLTextureType textureType, 560 NSRange levels, 561 NSRange slices, 562 const MTLTextureSwizzleChannels &swizzle) 563 : Resource(original), 564 mColorWritableMask(original->mColorWritableMask) // Share color write mask property 565{ 566 ANGLE_MTL_OBJC_SCOPE 567 { 568 auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat 569 textureType:textureType 570 levels:levels 571 slices:slices 572 swizzle:swizzle]; 573 574 set([view ANGLE_MTL_AUTORELEASE]); 575 // Texture views consume no additional memory 576 mEstimatedByteSize = 0; 577 } 578} 579 580void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder) 581{ 582 InvokeCPUMemSync(context, blitEncoder, this); 583} 584 585void Texture::syncContentIfNeeded(ContextMtl *context) 586{ 587 EnsureCPUMemWillBeSynced(context, this); 588} 589 590bool Texture::isCPUAccessible() const 591{ 592#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 593 if (get().storageMode == MTLStorageModeManaged) 594 { 595 return true; 596 } 597#endif 598 return get().storageMode == MTLStorageModeShared; 599} 600 601bool Texture::isShaderReadable() const 602{ 603 return get().usage & MTLTextureUsageShaderRead; 604} 605 606bool Texture::isShaderWritable() const 607{ 608 return get().usage & MTLTextureUsageShaderWrite; 609} 610 611bool Texture::supportFormatView() const 612{ 613 return get().usage & MTLTextureUsagePixelFormatView; 614} 615 616void Texture::replace2DRegion(ContextMtl *context, 617 const MTLRegion ®ion, 618 const MipmapNativeLevel &mipmapLevel, 619 uint32_t slice, 620 const uint8_t *data, 621 size_t bytesPerRow) 622{ 623 ASSERT(region.size.depth == 1); 624 replaceRegion(context, region, mipmapLevel, slice, data, bytesPerRow, 0); 625} 626 627void Texture::replaceRegion(ContextMtl *context, 628 const MTLRegion ®ion, 629 const MipmapNativeLevel &mipmapLevel, 630 uint32_t slice, 631 const uint8_t *data, 632 size_t bytesPerRow, 633 size_t bytesPer2DImage) 634{ 635 if (mipmapLevel.get() >= this->mipmapLevels()) 636 { 637 return; 638 } 639 640 ASSERT(isCPUAccessible()); 641 642 CommandQueue &cmdQueue = context->cmdQueue(); 643 644 syncContentIfNeeded(context); 645 646 // NOTE(hqle): what if multiple contexts on multiple threads are using this texture? 647 if (this->isBeingUsedByGPU(context)) 648 { 649 context->flushCommandBuffer(mtl::NoWait); 650 } 651 652 cmdQueue.ensureResourceReadyForCPU(this); 653 654 if (textureType() != MTLTextureType3D) 655 { 656 bytesPer2DImage = 0; 657 } 658 659 [get() replaceRegion:region 660 mipmapLevel:mipmapLevel.get() 661 slice:slice 662 withBytes:data 663 bytesPerRow:bytesPerRow 664 bytesPerImage:bytesPer2DImage]; 665} 666 667void Texture::getBytes(ContextMtl *context, 668 size_t bytesPerRow, 669 size_t bytesPer2DInage, 670 const MTLRegion ®ion, 671 const MipmapNativeLevel &mipmapLevel, 672 uint32_t slice, 673 uint8_t *dataOut) 674{ 675 ASSERT(isCPUAccessible()); 676 677 CommandQueue &cmdQueue = context->cmdQueue(); 678 679 syncContentIfNeeded(context); 680 681 // NOTE(hqle): what if multiple contexts on multiple threads are using this texture? 682 if (this->isBeingUsedByGPU(context)) 683 { 684 context->flushCommandBuffer(mtl::NoWait); 685 } 686 687 cmdQueue.ensureResourceReadyForCPU(this); 688 689 [get() getBytes:dataOut 690 bytesPerRow:bytesPerRow 691 bytesPerImage:bytesPer2DInage 692 fromRegion:region 693 mipmapLevel:mipmapLevel.get() 694 slice:slice]; 695} 696 697TextureRef Texture::createCubeFaceView(uint32_t face) 698{ 699 ANGLE_MTL_OBJC_SCOPE 700 { 701 switch (textureType()) 702 { 703 case MTLTextureTypeCube: 704 return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D, 705 NSMakeRange(0, mipmapLevels()), 706 NSMakeRange(face, 1))); 707 default: 708 UNREACHABLE(); 709 return nullptr; 710 } 711 } 712} 713 714TextureRef Texture::createSliceMipView(uint32_t slice, const MipmapNativeLevel &level) 715{ 716 ANGLE_MTL_OBJC_SCOPE 717 { 718 switch (textureType()) 719 { 720 case MTLTextureTypeCube: 721 case MTLTextureType2D: 722 case MTLTextureType2DArray: 723 return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D, 724 NSMakeRange(level.get(), 1), NSMakeRange(slice, 1))); 725 default: 726 UNREACHABLE(); 727 return nullptr; 728 } 729 } 730} 731 732TextureRef Texture::createMipView(const MipmapNativeLevel &level) 733{ 734 ANGLE_MTL_OBJC_SCOPE 735 { 736 NSUInteger slices = cubeFacesOrArrayLength(); 737 return TextureRef(new Texture(this, pixelFormat(), textureType(), 738 NSMakeRange(level.get(), 1), NSMakeRange(0, slices))); 739 } 740} 741 742TextureRef Texture::createMipsView(const MipmapNativeLevel &baseLevel, uint32_t levels) 743{ 744 ANGLE_MTL_OBJC_SCOPE 745 { 746 NSUInteger slices = cubeFacesOrArrayLength(); 747 return TextureRef(new Texture(this, pixelFormat(), textureType(), 748 NSMakeRange(baseLevel.get(), levels), 749 NSMakeRange(0, slices))); 750 } 751} 752 753TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format) 754{ 755 ASSERT(supportFormatView()); 756 return TextureRef(new Texture(this, format)); 757} 758 759TextureRef Texture::createShaderImageView2D(const MipmapNativeLevel &level, 760 int layer, 761 MTLPixelFormat format) 762{ 763 ASSERT(isShaderReadable()); 764 ASSERT(isShaderWritable()); 765 ASSERT(format == pixelFormat() || supportFormatView()); 766 ASSERT(textureType() != MTLTextureType3D); 767 return TextureRef(new Texture(this, format, MTLTextureType2D, NSMakeRange(level.get(), 1), 768 NSMakeRange(layer, 1))); 769} 770 771TextureRef Texture::createViewWithCompatibleFormat(MTLPixelFormat format) 772{ 773 return TextureRef(new Texture(this, format)); 774} 775 776TextureRef Texture::createMipsSwizzleView(const MipmapNativeLevel &baseLevel, 777 uint32_t levels, 778 MTLPixelFormat format, 779 const MTLTextureSwizzleChannels &swizzle) 780{ 781 return TextureRef(new Texture(this, format, textureType(), NSMakeRange(baseLevel.get(), levels), 782 NSMakeRange(0, cubeFacesOrArrayLength()), swizzle)); 783} 784 785MTLPixelFormat Texture::pixelFormat() const 786{ 787 return get().pixelFormat; 788} 789 790MTLTextureType Texture::textureType() const 791{ 792 return get().textureType; 793} 794 795uint32_t Texture::mipmapLevels() const 796{ 797 return static_cast<uint32_t>(get().mipmapLevelCount); 798} 799 800uint32_t Texture::arrayLength() const 801{ 802 return static_cast<uint32_t>(get().arrayLength); 803} 804 805uint32_t Texture::cubeFaces() const 806{ 807 if (textureType() == MTLTextureTypeCube) 808 { 809 return 6; 810 } 811 return 1; 812} 813 814uint32_t Texture::cubeFacesOrArrayLength() const 815{ 816 if (textureType() == MTLTextureTypeCube) 817 { 818 return 6; 819 } 820 return arrayLength(); 821} 822 823uint32_t Texture::width(const MipmapNativeLevel &level) const 824{ 825 return static_cast<uint32_t>(GetMipSize(get().width, level)); 826} 827 828uint32_t Texture::height(const MipmapNativeLevel &level) const 829{ 830 return static_cast<uint32_t>(GetMipSize(get().height, level)); 831} 832 833uint32_t Texture::depth(const MipmapNativeLevel &level) const 834{ 835 return static_cast<uint32_t>(GetMipSize(get().depth, level)); 836} 837 838gl::Extents Texture::size(const MipmapNativeLevel &level) const 839{ 840 gl::Extents re; 841 842 re.width = width(level); 843 re.height = height(level); 844 re.depth = depth(level); 845 846 return re; 847} 848 849gl::Extents Texture::size(const ImageNativeIndex &index) const 850{ 851 gl::Extents extents = size(index.getNativeLevel()); 852 853 if (index.hasLayer()) 854 { 855 extents.depth = 1; 856 } 857 858 return extents; 859} 860 861uint32_t Texture::samples() const 862{ 863 return static_cast<uint32_t>(get().sampleCount); 864} 865 866bool Texture::hasIOSurface() const 867{ 868 return (get().iosurface) != nullptr; 869} 870 871bool Texture::sameTypeAndDimemsionsAs(const TextureRef &other) const 872{ 873 return textureType() == other->textureType() && pixelFormat() == other->pixelFormat() && 874 mipmapLevels() == other->mipmapLevels() && 875 cubeFacesOrArrayLength() == other->cubeFacesOrArrayLength() && 876 widthAt0() == other->widthAt0() && heightAt0() == other->heightAt0() && 877 depthAt0() == other->depthAt0(); 878} 879 880angle::Result Texture::resize(ContextMtl *context, uint32_t width, uint32_t height) 881{ 882 // Resizing texture view is not supported. 883 ASSERT(mCreationDesc); 884 885 ANGLE_MTL_OBJC_SCOPE 886 { 887 MTLTextureDescriptor *newDesc = [[mCreationDesc.get() copy] ANGLE_MTL_AUTORELEASE]; 888 newDesc.width = width; 889 newDesc.height = height; 890 auto newTexture = context->getMetalDevice().newTextureWithDescriptor(newDesc); 891 ANGLE_CHECK_GL_ALLOC(context, newTexture); 892 mCreationDesc.retainAssign(newDesc); 893 set(newTexture); 894 // Reset reference counter 895 Resource::reset(); 896 } 897 898 return angle::Result::Continue; 899} 900 901TextureRef Texture::getLinearColorView() 902{ 903 if (mLinearColorView) 904 { 905 return mLinearColorView; 906 } 907 908 switch (pixelFormat()) 909 { 910 case MTLPixelFormatRGBA8Unorm_sRGB: 911 mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatRGBA8Unorm); 912 break; 913 case MTLPixelFormatBGRA8Unorm_sRGB: 914 mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatBGRA8Unorm); 915 break; 916 default: 917 // NOTE(hqle): Not all sRGB formats are supported yet. 918 UNREACHABLE(); 919 } 920 921 return mLinearColorView; 922} 923 924TextureRef Texture::getReadableCopy(ContextMtl *context, 925 mtl::BlitCommandEncoder *encoder, 926 const uint32_t levelToCopy, 927 const uint32_t sliceToCopy, 928 const MTLRegion &areaToCopy) 929{ 930 gl::Extents firstLevelSize = size(kZeroNativeMipLevel); 931 if (!mReadCopy || mReadCopy->get().width < static_cast<size_t>(firstLevelSize.width) || 932 mReadCopy->get().height < static_cast<size_t>(firstLevelSize.height)) 933 { 934 // Create a texture that big enough to store the first level data and any smaller level 935 ANGLE_MTL_OBJC_SCOPE 936 { 937 auto desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE]; 938 desc.textureType = get().textureType; 939 desc.pixelFormat = get().pixelFormat; 940 desc.width = firstLevelSize.width; 941 desc.height = firstLevelSize.height; 942 desc.depth = 1; 943 desc.arrayLength = 1; 944 desc.resourceOptions = MTLResourceStorageModePrivate; 945 desc.sampleCount = get().sampleCount; 946 desc.usage = MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView; 947 mReadCopy.reset(new Texture(context->getMetalDevice().newTextureWithDescriptor(desc))); 948 } // ANGLE_MTL_OBJC_SCOPE 949 } 950 951 ASSERT(encoder); 952 953 encoder->copyTexture(shared_from_this(), sliceToCopy, mtl::MipmapNativeLevel(levelToCopy), 954 mReadCopy, 0, mtl::kZeroNativeMipLevel, 1, 1); 955 956 return mReadCopy; 957} 958 959void Texture::releaseReadableCopy() 960{ 961 mReadCopy = nullptr; 962} 963 964TextureRef Texture::getStencilView() 965{ 966 if (mStencilView) 967 { 968 return mStencilView; 969 } 970 971 switch (pixelFormat()) 972 { 973 case MTLPixelFormatStencil8: 974 case MTLPixelFormatX32_Stencil8: 975#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 976 case MTLPixelFormatX24_Stencil8: 977#endif 978 // This texture is already a stencil texture. Return its ref directly. 979 return shared_from_this(); 980#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 981 case MTLPixelFormatDepth24Unorm_Stencil8: 982 mStencilView = createViewWithDifferentFormat(MTLPixelFormatX24_Stencil8); 983 break; 984#endif 985 case MTLPixelFormatDepth32Float_Stencil8: 986 mStencilView = createViewWithDifferentFormat(MTLPixelFormatX32_Stencil8); 987 break; 988 default: 989 UNREACHABLE(); 990 } 991 992 return mStencilView; 993} 994 995TextureRef Texture::parentTexture() 996{ 997 if (mParentTexture) 998 { 999 return mParentTexture; 1000 } 1001 1002 if (!get().parentTexture) 1003 { 1004 // Doesn't have parent. 1005 return nullptr; 1006 } 1007 1008 // Lazily construct parent's Texture object from parent's MTLTexture. 1009 // Note that the constructed Texture object is not the same as the same original object that 1010 // creates this view. However, it will share the same usageRef and MTLTexture with the 1011 // original Texture object. We do this to avoid cyclic reference between original Texture 1012 // and its view. 1013 // 1014 // For example, the original Texture object might keep a ref to its stencil view. Had we 1015 // kept the original object's ref in the stencil view, there would have been a cyclic 1016 // reference. 1017 // 1018 // This is OK because even though the Texture objects are not the same, they refer to same 1019 // MTLTexture and usageRef. 1020 mParentTexture.reset(new Texture(mUsageRef, get().parentTexture, mColorWritableMask)); 1021 1022 return mParentTexture; 1023} 1024MipmapNativeLevel Texture::parentRelativeLevel() 1025{ 1026 return mtl::GetNativeMipLevel(static_cast<uint32_t>(get().parentRelativeLevel), 0); 1027} 1028uint32_t Texture::parentRelativeSlice() 1029{ 1030 return static_cast<uint32_t>(get().parentRelativeSlice); 1031} 1032 1033void Texture::set(id<MTLTexture> metalTexture) 1034{ 1035 ParentClass::set(metalTexture); 1036 // Reset stencil view 1037 mStencilView = nullptr; 1038 mLinearColorView = nullptr; 1039 1040 mParentTexture = nullptr; 1041} 1042 1043// Buffer implementation 1044 1045MTLStorageMode Buffer::getStorageModeForSharedBuffer(ContextMtl *contextMtl) 1046{ 1047#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1048 if (ANGLE_UNLIKELY(contextMtl->getDisplay()->getFeatures().forceBufferGPUStorage.enabled)) 1049 { 1050 return MTLStorageModeManaged; 1051 } 1052#endif 1053 return MTLStorageModeShared; 1054} 1055 1056MTLStorageMode Buffer::getStorageModeForUsage(ContextMtl *contextMtl, Usage usage) 1057{ 1058#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1059 bool hasCpuAccess = false; 1060 switch (usage) 1061 { 1062 case Usage::StaticCopy: 1063 case Usage::StaticDraw: 1064 case Usage::StaticRead: 1065 case Usage::DynamicRead: 1066 case Usage::StreamRead: 1067 hasCpuAccess = true; 1068 break; 1069 default: 1070 break; 1071 } 1072 const auto &features = contextMtl->getDisplay()->getFeatures(); 1073 if (hasCpuAccess) 1074 { 1075 if (features.alwaysUseManagedStorageModeForBuffers.enabled || 1076 ANGLE_UNLIKELY(features.forceBufferGPUStorage.enabled)) 1077 { 1078 return MTLStorageModeManaged; 1079 } 1080 return MTLStorageModeShared; 1081 } 1082 if (contextMtl->getMetalDevice().hasUnifiedMemory() || 1083 features.alwaysUseSharedStorageModeForBuffers.enabled) 1084 { 1085 return MTLStorageModeShared; 1086 } 1087 return MTLStorageModeManaged; 1088#else 1089 ANGLE_UNUSED_VARIABLE(contextMtl); 1090 ANGLE_UNUSED_VARIABLE(usage); 1091 return MTLStorageModeShared; 1092#endif 1093} 1094 1095angle::Result Buffer::MakeBuffer(ContextMtl *context, 1096 size_t size, 1097 const uint8_t *data, 1098 BufferRef *bufferOut) 1099{ 1100 auto storageMode = getStorageModeForUsage(context, Usage::DynamicDraw); 1101 return MakeBufferWithStorageMode(context, storageMode, size, data, bufferOut); 1102} 1103 1104angle::Result Buffer::MakeBufferWithStorageMode(ContextMtl *context, 1105 MTLStorageMode storageMode, 1106 size_t size, 1107 const uint8_t *data, 1108 BufferRef *bufferOut) 1109{ 1110 bufferOut->reset(new Buffer(context, storageMode, size, data)); 1111 1112 if (!(*bufferOut) || !(*bufferOut)->get()) 1113 { 1114 ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY); 1115 } 1116 1117 return angle::Result::Continue; 1118} 1119 1120Buffer::Buffer(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data) 1121{ 1122 (void)reset(context, storageMode, size, data); 1123} 1124 1125angle::Result Buffer::reset(ContextMtl *context, 1126 MTLStorageMode storageMode, 1127 size_t size, 1128 const uint8_t *data) 1129{ 1130 auto options = resourceOptionsForStorageMode(storageMode); 1131 set([&]() -> AutoObjCPtr<id<MTLBuffer>> { 1132 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 1133 if (size > [metalDevice maxBufferLength]) 1134 { 1135 return nullptr; 1136 } 1137 if (data) 1138 { 1139 return metalDevice.newBufferWithBytes(data, size, options); 1140 } 1141 return metalDevice.newBufferWithLength(size, options); 1142 }()); 1143 // Reset command buffer's reference serial 1144 Resource::reset(); 1145 1146 return angle::Result::Continue; 1147} 1148 1149void Buffer::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder) 1150{ 1151 InvokeCPUMemSync(context, blitEncoder, this); 1152} 1153 1154const uint8_t *Buffer::mapReadOnly(ContextMtl *context) 1155{ 1156 return mapWithOpt(context, true, false); 1157} 1158 1159uint8_t *Buffer::map(ContextMtl *context) 1160{ 1161 return mapWithOpt(context, false, false); 1162} 1163 1164uint8_t *Buffer::mapWithOpt(ContextMtl *context, bool readonly, bool noSync) 1165{ 1166 mMapReadOnly = readonly; 1167 1168 if (!noSync && (isCPUReadMemSyncPending() || isCPUReadMemNeedSync() || !readonly)) 1169 { 1170 CommandQueue &cmdQueue = context->cmdQueue(); 1171 1172 EnsureCPUMemWillBeSynced(context, this); 1173 1174 if (this->isBeingUsedByGPU(context)) 1175 { 1176 context->flushCommandBuffer(mtl::NoWait); 1177 } 1178 1179 cmdQueue.ensureResourceReadyForCPU(this); 1180 resetCPUReadMemSyncPending(); 1181 } 1182 1183 return reinterpret_cast<uint8_t *>([get() contents]); 1184} 1185 1186void Buffer::unmap(ContextMtl *context) 1187{ 1188 flush(context, 0, size()); 1189 1190 // Reset read only flag 1191 mMapReadOnly = true; 1192} 1193 1194void Buffer::unmapNoFlush(ContextMtl *context) 1195{ 1196 mMapReadOnly = true; 1197} 1198 1199void Buffer::unmapAndFlushSubset(ContextMtl *context, size_t offsetWritten, size_t sizeWritten) 1200{ 1201#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1202 flush(context, offsetWritten, sizeWritten); 1203#endif 1204 mMapReadOnly = true; 1205} 1206 1207void Buffer::flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten) 1208{ 1209#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1210 if (!mMapReadOnly) 1211 { 1212 if (get().storageMode == MTLStorageModeManaged) 1213 { 1214 size_t bufferSize = size(); 1215 size_t startOffset = std::min(offsetWritten, bufferSize); 1216 size_t endOffset = std::min(offsetWritten + sizeWritten, bufferSize); 1217 size_t clampedSize = endOffset - startOffset; 1218 if (clampedSize > 0) 1219 { 1220 [get() didModifyRange:NSMakeRange(startOffset, clampedSize)]; 1221 } 1222 } 1223 } 1224#endif 1225} 1226 1227size_t Buffer::size() const 1228{ 1229 return get().length; 1230} 1231 1232MTLStorageMode Buffer::storageMode() const 1233{ 1234 return get().storageMode; 1235} 1236} // namespace mtl 1237} // namespace rx 1238