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_utils.mm: 7// Implements utilities functions that create Metal shaders, convert from angle enums 8// to Metal enums and so on. 9// 10 11#include "libANGLE/renderer/metal/mtl_utils.h" 12 13#include <Availability.h> 14#include <TargetConditionals.h> 15#include <stdio.h> 16 17#include "common/MemoryBuffer.h" 18#include "common/string_utils.h" 19#include "common/system_utils.h" 20#include "gpu_info_util/SystemInfo_internal.h" 21#include "libANGLE/histogram_macros.h" 22#include "libANGLE/renderer/metal/ContextMtl.h" 23#include "libANGLE/renderer/metal/DisplayMtl.h" 24#include "libANGLE/renderer/metal/RenderTargetMtl.h" 25#include "libANGLE/renderer/metal/mtl_render_utils.h" 26#include "libANGLE/renderer/metal/process.h" 27#include "platform/PlatformMethods.h" 28 29// Compiler can turn on programmatical frame capture in release build by defining 30// ANGLE_METAL_FRAME_CAPTURE flag. 31#if defined(NDEBUG) && !defined(ANGLE_METAL_FRAME_CAPTURE) 32# define ANGLE_METAL_FRAME_CAPTURE_ENABLED 0 33#else 34# define ANGLE_METAL_FRAME_CAPTURE_ENABLED 1 35#endif 36 37namespace rx 38{ 39 40ANGLE_APPLE_UNUSED 41bool IsFrameCaptureEnabled() 42{ 43#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED 44 return false; 45#else 46 // We only support frame capture programmatically if the ANGLE_METAL_FRAME_CAPTURE 47 // environment flag is set. Otherwise, it will slow down the rendering. This allows user to 48 // finely control whether they want to capture the frame for particular application or not. 49 auto var = std::getenv("ANGLE_METAL_FRAME_CAPTURE"); 50 static const bool enabled = var ? (strcmp(var, "1") == 0) : false; 51 52 return enabled; 53#endif 54} 55 56ANGLE_APPLE_UNUSED 57std::string GetMetalCaptureFile() 58{ 59#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED 60 return {}; 61#else 62 auto var = std::getenv("ANGLE_METAL_FRAME_CAPTURE_FILE"); 63 const std::string filePath = var ? var : ""; 64 65 return filePath; 66#endif 67} 68 69ANGLE_APPLE_UNUSED 70size_t MaxAllowedFrameCapture() 71{ 72#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED 73 return 0; 74#else 75 auto var = std::getenv("ANGLE_METAL_FRAME_CAPTURE_MAX"); 76 static const size_t maxFrames = var ? std::atoi(var) : 100; 77 78 return maxFrames; 79#endif 80} 81 82ANGLE_APPLE_UNUSED 83size_t MinAllowedFrameCapture() 84{ 85#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED 86 return 0; 87#else 88 auto var = std::getenv("ANGLE_METAL_FRAME_CAPTURE_MIN"); 89 static const size_t minFrame = var ? std::atoi(var) : 0; 90 91 return minFrame; 92#endif 93} 94 95ANGLE_APPLE_UNUSED 96bool FrameCaptureDeviceScope() 97{ 98#if !ANGLE_METAL_FRAME_CAPTURE_ENABLED 99 return false; 100#else 101 auto var = std::getenv("ANGLE_METAL_FRAME_CAPTURE_SCOPE"); 102 static const bool scopeDevice = var ? (strcmp(var, "device") == 0) : false; 103 104 return scopeDevice; 105#endif 106} 107 108// Ensure that .gputrace files have RW permissions for the user or, if a 109// directory, RWX permissions for the user. 110ANGLE_APPLE_UNUSED 111static inline void FixGPUTracePathPermissions(NSString *path, bool isDir) 112{ 113 // Ensure we're only change permissions on files in a gputrace bundle. 114 if (![path containsString:@".gputrace"]) 115 { 116 return; 117 } 118 119 NSError *error = nil; 120 NSDictionary<NSFileAttributeKey, id> *attributes = 121 [NSFileManager.defaultManager attributesOfItemAtPath:path error:&error]; 122 NSNumber *oldPerms = static_cast<NSNumber *>(attributes[NSFilePosixPermissions]); 123 if (!oldPerms) 124 { 125 NSString *msg = 126 attributes ? @"NSFilePosixPermissions unavailable" : error.localizedDescription; 127 NSLog(@"Unable to read permissions for %@ (%@)", path, msg); 128 return; 129 } 130 131 NSUInteger newPerms = oldPerms.unsignedIntegerValue | S_IRUSR | S_IWUSR; 132 if (isDir) 133 { 134 newPerms |= S_IXUSR; 135 } 136 137 if (![NSFileManager.defaultManager setAttributes:@{ 138 NSFilePosixPermissions : @(newPerms) 139 } 140 ofItemAtPath:path 141 error:&error]) 142 { 143 NSLog(@"Unable to set permissions=%3lo for %@ (%@)", static_cast<unsigned long>(newPerms), 144 path, error.localizedDescription); 145 } 146} 147 148ANGLE_APPLE_UNUSED 149static inline void FixGPUTraceBundlePermissions(NSString *bundlePath) 150{ 151 FixGPUTracePathPermissions(bundlePath, true); 152 for (NSString *file in [NSFileManager.defaultManager enumeratorAtPath:bundlePath]) 153 { 154 FixGPUTracePathPermissions([NSString pathWithComponents:@[ bundlePath, file ]], false); 155 } 156} 157 158ANGLE_APPLE_UNUSED 159std::atomic<size_t> gFrameCaptured(0); 160 161ANGLE_APPLE_UNUSED 162NSString *gFrameCapturePath; 163 164ANGLE_APPLE_UNUSED 165void StartFrameCapture(id<MTLDevice> metalDevice, id<MTLCommandQueue> metalCmdQueue) 166{ 167#if ANGLE_METAL_FRAME_CAPTURE_ENABLED 168 if (!IsFrameCaptureEnabled()) 169 { 170 return; 171 } 172 173 if (gFrameCaptured >= MaxAllowedFrameCapture()) 174 { 175 return; 176 } 177 178 MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager]; 179 if (captureManager.isCapturing) 180 { 181 return; 182 } 183 184 gFrameCaptured++; 185 186 if (gFrameCaptured < MinAllowedFrameCapture()) 187 { 188 return; 189 } 190 191 auto captureDescriptor = mtl::adoptObjCObj([[MTLCaptureDescriptor alloc] init]); 192 captureDescriptor.get().captureObject = metalDevice; 193 const std::string filePath = GetMetalCaptureFile(); 194 NSString *frameCapturePath = nil; 195 if (filePath != "") 196 { 197 frameCapturePath = 198 [NSString stringWithFormat:@"%s%zu.gputrace", filePath.c_str(), gFrameCaptured - 1]; 199 captureDescriptor.get().destination = MTLCaptureDestinationGPUTraceDocument; 200 captureDescriptor.get().outputURL = [NSURL fileURLWithPath:frameCapturePath 201 isDirectory:false]; 202 } 203 else 204 { 205 // This will pause execution only if application is being debugged inside Xcode 206 captureDescriptor.get().destination = MTLCaptureDestinationDeveloperTools; 207 } 208 209 NSError *error; 210 if ([captureManager startCaptureWithDescriptor:captureDescriptor.get() error:&error]) 211 { 212 ASSERT(!gFrameCapturePath); 213 gFrameCapturePath = frameCapturePath; 214 } 215 else 216 { 217 NSLog(@"Failed to start capture, error %@", error); 218 } 219#endif // ANGLE_METAL_FRAME_CAPTURE_ENABLED 220} 221 222void StartFrameCapture(ContextMtl *context) 223{ 224 StartFrameCapture(context->getMetalDevice(), context->cmdQueue().get()); 225} 226 227void StopFrameCapture() 228{ 229#if ANGLE_METAL_FRAME_CAPTURE_ENABLED 230 if (!IsFrameCaptureEnabled()) 231 { 232 return; 233 } 234 MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager]; 235 if (captureManager.isCapturing) 236 { 237 [captureManager stopCapture]; 238 if (gFrameCapturePath) 239 { 240 FixGPUTraceBundlePermissions(gFrameCapturePath); 241 [gFrameCapturePath ANGLE_MTL_RELEASE]; 242 gFrameCapturePath = nil; 243 } 244 } 245#endif 246} 247 248namespace mtl 249{ 250 251constexpr char kANGLEPrintMSLEnv[] = "ANGLE_METAL_PRINT_MSL_ENABLE"; 252constexpr char kANGLEMSLVersionMajorEnv[] = "ANGLE_MSL_VERSION_MAJOR"; 253constexpr char kANGLEMSLVersionMinorEnv[] = "ANGLE_MSL_VERSION_MINOR"; 254 255namespace 256{ 257 258uint32_t GetDeviceVendorIdFromName(id<MTLDevice> metalDevice) 259{ 260 struct Vendor 261 { 262 NSString *const trademark; 263 uint32_t vendorId; 264 }; 265 266 constexpr Vendor kVendors[] = { 267 {@"AMD", angle::kVendorID_AMD}, {@"Apple", angle::kVendorID_Apple}, 268 {@"Radeon", angle::kVendorID_AMD}, {@"Intel", angle::kVendorID_Intel}, 269 {@"Geforce", angle::kVendorID_NVIDIA}, {@"Quadro", angle::kVendorID_NVIDIA}}; 270 ANGLE_MTL_OBJC_SCOPE 271 { 272 if (metalDevice) 273 { 274 for (const Vendor &it : kVendors) 275 { 276 if ([metalDevice.name rangeOfString:it.trademark].location != NSNotFound) 277 { 278 return it.vendorId; 279 } 280 } 281 } 282 283 return 0; 284 } 285} 286 287#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 288uint32_t GetDeviceVendorIdFromIOKit(id<MTLDevice> device) 289{ 290 return angle::GetVendorIDFromMetalDeviceRegistryID(device.registryID); 291} 292#endif 293 294void GetSliceAndDepth(const ImageNativeIndex &index, GLint *layer, GLint *startDepth) 295{ 296 *layer = *startDepth = 0; 297 if (!index.hasLayer()) 298 { 299 return; 300 } 301 302 switch (index.getType()) 303 { 304 case gl::TextureType::CubeMap: 305 *layer = index.cubeMapFaceIndex(); 306 break; 307 case gl::TextureType::_2DArray: 308 *layer = index.getLayerIndex(); 309 break; 310 case gl::TextureType::_3D: 311 *startDepth = index.getLayerIndex(); 312 break; 313 default: 314 break; 315 } 316} 317GLint GetSliceOrDepth(const ImageNativeIndex &index) 318{ 319 GLint layer, startDepth; 320 GetSliceAndDepth(index, &layer, &startDepth); 321 322 return std::max(layer, startDepth); 323} 324 325bool GetCompressedBufferSizeAndRowLengthForTextureWithFormat(const TextureRef &texture, 326 const Format &textureObjFormat, 327 const ImageNativeIndex &index, 328 size_t *bytesPerRowOut, 329 size_t *bytesPerImageOut) 330{ 331 gl::Extents size = texture->size(index); 332 ASSERT(size.depth == 1); 333 GLuint bufferRowInBytes; 334 if (!textureObjFormat.intendedInternalFormat().computeCompressedImageRowPitch( 335 size.width, &bufferRowInBytes)) 336 { 337 return false; 338 } 339 GLuint bufferSizeInBytes; 340 if (!textureObjFormat.intendedInternalFormat().computeCompressedImageDepthPitch( 341 size.height, bufferRowInBytes, &bufferSizeInBytes)) 342 { 343 return false; 344 } 345 *bytesPerRowOut = bufferRowInBytes; 346 *bytesPerImageOut = bufferSizeInBytes; 347 return true; 348} 349static angle::Result InitializeCompressedTextureContents(const gl::Context *context, 350 const TextureRef &texture, 351 const Format &textureObjFormat, 352 const ImageNativeIndex &index, 353 const uint layer, 354 const uint startDepth) 355{ 356 assert(textureObjFormat.actualAngleFormat().isBlock); 357 size_t bytesPerRow = 0; 358 size_t bytesPerImage = 0; 359 if (!GetCompressedBufferSizeAndRowLengthForTextureWithFormat(texture, textureObjFormat, index, 360 &bytesPerRow, &bytesPerImage)) 361 { 362 return angle::Result::Stop; 363 } 364 ContextMtl *contextMtl = mtl::GetImpl(context); 365 gl::Extents extents = texture->size(index); 366 if (texture->isCPUAccessible()) 367 { 368 if (textureObjFormat.isPVRTC()) 369 { 370 // Replace Region Validation: rowBytes must be 0 371 bytesPerRow = 0; 372 } 373 374 angle::MemoryBuffer buffer; 375 if (!buffer.resize(bytesPerImage)) 376 { 377 return angle::Result::Stop; 378 } 379 buffer.fill(0); 380 for (NSUInteger d = 0; d < static_cast<NSUInteger>(extents.depth); ++d) 381 { 382 auto mtlTextureRegion = MTLRegionMake2D(0, 0, extents.width, extents.height); 383 mtlTextureRegion.origin.z = d + startDepth; 384 texture->replaceRegion(contextMtl, mtlTextureRegion, index.getNativeLevel(), layer, 385 buffer.data(), bytesPerRow, 0); 386 } 387 } 388 else 389 { 390 mtl::BufferRef zeroBuffer; 391 ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, bytesPerImage, nullptr, &zeroBuffer)); 392 mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder(); 393 for (NSUInteger d = 0; d < static_cast<NSUInteger>(extents.depth); ++d) 394 { 395 auto blitOrigin = MTLOriginMake(0, 0, d + startDepth); 396 blitEncoder->copyBufferToTexture(zeroBuffer, 0, bytesPerRow, 0, 397 MTLSizeMake(extents.width, extents.height, 1), texture, 398 layer, index.getNativeLevel(), blitOrigin, 0); 399 } 400 blitEncoder->endEncoding(); 401 } 402 return angle::Result::Continue; 403} 404 405} // namespace 406 407bool PreferStagedTextureUploads(const gl::Context *context, 408 const TextureRef &texture, 409 const Format &textureObjFormat, 410 StagingPurpose purpose) 411{ 412 // The simulator MUST upload all textures as staged. 413 if (TARGET_OS_SIMULATOR) 414 { 415 return true; 416 } 417 418 ContextMtl *contextMtl = mtl::GetImpl(context); 419 const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures(); 420 421 const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat(); 422 if (intendedInternalFormat.compressed || textureObjFormat.actualAngleFormat().isBlock) 423 { 424 return false; 425 } 426 427 // If the intended internal format is luminance, we can still 428 // initialize the texture using the GPU. However, if we're 429 // uploading data to it, we avoid using a staging buffer, due to 430 // the (current) need to re-pack the data from L8 -> RGBA8 and LA8 431 // -> RGBA8. This could be better optimized by emulating L8 432 // textures with R8 and LA8 with RG8, and using swizzlig for the 433 // resulting textures. 434 if (intendedInternalFormat.isLUMA()) 435 { 436 return (purpose == StagingPurpose::Initialization); 437 } 438 439 if (features.disableStagedInitializationOfPackedTextureFormats.enabled) 440 { 441 switch (intendedInternalFormat.sizedInternalFormat) 442 { 443 case GL_RGB9_E5: 444 case GL_R11F_G11F_B10F: 445 return false; 446 447 default: 448 break; 449 } 450 } 451 452 return (texture->hasIOSurface() && features.uploadDataToIosurfacesWithStagingBuffers.enabled) || 453 features.alwaysPreferStagedTextureUploads.enabled; 454} 455 456angle::Result InitializeTextureContents(const gl::Context *context, 457 const TextureRef &texture, 458 const Format &textureObjFormat, 459 const ImageNativeIndex &index) 460{ 461 ASSERT(texture && texture->valid()); 462 // Only one slice can be initialized at a time. 463 ASSERT(!index.isLayered() || index.getType() == gl::TextureType::_3D); 464 ContextMtl *contextMtl = mtl::GetImpl(context); 465 466 const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat(); 467 468 bool preferGPUInitialization = PreferStagedTextureUploads(context, texture, textureObjFormat, 469 StagingPurpose::Initialization); 470 471 // This function is called in many places to initialize the content of a texture. 472 // So it's better we do the initial check here instead of let the callers do it themselves: 473 if (!textureObjFormat.valid()) 474 { 475 return angle::Result::Continue; 476 } 477 478 if ((textureObjFormat.hasDepthOrStencilBits() && !textureObjFormat.getCaps().depthRenderable) || 479 !textureObjFormat.getCaps().colorRenderable) 480 { 481 // Texture is not appropriately color- or depth-renderable, so do not attempt 482 // to use GPU initialization (clears for initialization). 483 preferGPUInitialization = false; 484 } 485 486 gl::Extents size = texture->size(index); 487 488 // Intiialize the content to black 489 GLint layer, startDepth; 490 GetSliceAndDepth(index, &layer, &startDepth); 491 492 // Use compressed texture initialization only when both the intended and the actual ANGLE 493 // formats are compressed. Emulated opaque ETC2 formats use uncompressed fallbacks and require 494 // custom initialization. 495 if (intendedInternalFormat.compressed && textureObjFormat.actualAngleFormat().isBlock) 496 { 497 return InitializeCompressedTextureContents(context, texture, textureObjFormat, index, layer, 498 startDepth); 499 } 500 else if (texture->isCPUAccessible() && index.getType() != gl::TextureType::_2DMultisample && 501 index.getType() != gl::TextureType::_2DMultisampleArray && !preferGPUInitialization) 502 { 503 const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId); 504 const size_t dstRowPitch = dstFormat.pixelBytes * size.width; 505 angle::MemoryBuffer conversionRow; 506 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); 507 508 if (textureObjFormat.initFunction) 509 { 510 textureObjFormat.initFunction(size.width, 1, 1, conversionRow.data(), dstRowPitch, 0); 511 } 512 else 513 { 514 const angle::Format &srcFormat = angle::Format::Get( 515 intendedInternalFormat.alphaBits > 0 ? angle::FormatID::R8G8B8A8_UNORM 516 : angle::FormatID::R8G8B8_UNORM); 517 const size_t srcRowPitch = srcFormat.pixelBytes * size.width; 518 angle::MemoryBuffer srcRow; 519 ANGLE_CHECK_GL_ALLOC(contextMtl, srcRow.resize(srcRowPitch)); 520 memset(srcRow.data(), 0, srcRowPitch); 521 522 CopyImageCHROMIUM(srcRow.data(), srcRowPitch, srcFormat.pixelBytes, 0, 523 srcFormat.pixelReadFunction, conversionRow.data(), dstRowPitch, 524 dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction, 525 intendedInternalFormat.format, dstFormat.componentType, size.width, 1, 526 1, false, false, false); 527 } 528 529 auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1); 530 531 for (NSUInteger d = 0; d < static_cast<NSUInteger>(size.depth); ++d) 532 { 533 mtlRowRegion.origin.z = d + startDepth; 534 for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r) 535 { 536 mtlRowRegion.origin.y = r; 537 538 // Upload to texture 539 texture->replace2DRegion(contextMtl, mtlRowRegion, index.getNativeLevel(), layer, 540 conversionRow.data(), dstRowPitch); 541 } 542 } 543 } 544 else 545 { 546 ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat, index, 547 MTLColorWriteMaskAll)); 548 } 549 550 return angle::Result::Continue; 551} 552 553angle::Result InitializeTextureContentsGPU(const gl::Context *context, 554 const TextureRef &texture, 555 const Format &textureObjFormat, 556 const ImageNativeIndex &index, 557 MTLColorWriteMask channelsToInit) 558{ 559 // Only one slice can be initialized at a time. 560 ASSERT(!index.isLayered() || index.getType() == gl::TextureType::_3D); 561 if (index.isLayered() && index.getType() == gl::TextureType::_3D) 562 { 563 ImageNativeIndexIterator ite = 564 index.getLayerIterator(texture->depth(index.getNativeLevel())); 565 while (ite.hasNext()) 566 { 567 ImageNativeIndex depthLayerIndex = ite.next(); 568 ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat, 569 depthLayerIndex, MTLColorWriteMaskAll)); 570 } 571 572 return angle::Result::Continue; 573 } 574 575 if (textureObjFormat.hasDepthOrStencilBits()) 576 { 577 // Depth stencil texture needs dedicated function. 578 return InitializeDepthStencilTextureContentsGPU(context, texture, textureObjFormat, index); 579 } 580 581 ContextMtl *contextMtl = mtl::GetImpl(context); 582 GLint sliceOrDepth = GetSliceOrDepth(index); 583 584 // Use clear render command 585 RenderTargetMtl tempRtt; 586 tempRtt.set(texture, index.getNativeLevel(), sliceOrDepth, textureObjFormat); 587 588 int clearAlpha = 0; 589 if (!textureObjFormat.intendedAngleFormat().alphaBits) 590 { 591 // if intended format doesn't have alpha, set it to 1.0. 592 clearAlpha = kEmulatedAlphaValue; 593 } 594 595 RenderCommandEncoder *encoder; 596 if (channelsToInit == MTLColorWriteMaskAll) 597 { 598 // If all channels will be initialized, use clear loadOp. 599 Optional<MTLClearColor> blackColor = MTLClearColorMake(0, 0, 0, clearAlpha); 600 encoder = contextMtl->getRenderTargetCommandEncoderWithClear(tempRtt, blackColor); 601 } 602 else 603 { 604 // temporarily enable color channels requested via channelsToInit. Some emulated format has 605 // some channels write mask disabled when the texture is created. 606 MTLColorWriteMask oldMask = texture->getColorWritableMask(); 607 texture->setColorWritableMask(channelsToInit); 608 609 // If there are some channels don't need to be initialized, we must use clearWithDraw. 610 encoder = contextMtl->getRenderTargetCommandEncoder(tempRtt); 611 612 const angle::Format &angleFormat = textureObjFormat.actualAngleFormat(); 613 614 ClearRectParams clearParams; 615 ClearColorValue clearColor; 616 if (angleFormat.isSint()) 617 { 618 clearColor.setAsInt(0, 0, 0, clearAlpha); 619 } 620 else if (angleFormat.isUint()) 621 { 622 clearColor.setAsUInt(0, 0, 0, clearAlpha); 623 } 624 else 625 { 626 clearColor.setAsFloat(0, 0, 0, clearAlpha); 627 } 628 clearParams.clearColor = clearColor; 629 clearParams.dstTextureSize = texture->sizeAt0(); 630 clearParams.enabledBuffers.set(0); 631 clearParams.clearArea = gl::Rectangle(0, 0, texture->widthAt0(), texture->heightAt0()); 632 633 ANGLE_TRY( 634 contextMtl->getDisplay()->getUtils().clearWithDraw(context, encoder, clearParams)); 635 636 // Restore texture's intended write mask 637 texture->setColorWritableMask(oldMask); 638 } 639 encoder->setStoreAction(MTLStoreActionStore); 640 641 return angle::Result::Continue; 642} 643 644angle::Result InitializeDepthStencilTextureContentsGPU(const gl::Context *context, 645 const TextureRef &texture, 646 const Format &textureObjFormat, 647 const ImageNativeIndex &index) 648{ 649 const MipmapNativeLevel &level = index.getNativeLevel(); 650 // Use clear operation 651 ContextMtl *contextMtl = mtl::GetImpl(context); 652 const angle::Format &angleFormat = textureObjFormat.actualAngleFormat(); 653 RenderTargetMtl rtMTL; 654 655 uint32_t layer = index.hasLayer() ? index.getLayerIndex() : 0; 656 rtMTL.set(texture, level, layer, textureObjFormat); 657 mtl::RenderPassDesc rpDesc; 658 if (angleFormat.depthBits) 659 { 660 rtMTL.toRenderPassAttachmentDesc(&rpDesc.depthAttachment); 661 rpDesc.depthAttachment.loadAction = MTLLoadActionClear; 662 } 663 if (angleFormat.stencilBits) 664 { 665 rtMTL.toRenderPassAttachmentDesc(&rpDesc.stencilAttachment); 666 rpDesc.stencilAttachment.loadAction = MTLLoadActionClear; 667 } 668 rpDesc.rasterSampleCount = texture->samples(); 669 670 // End current render pass 671 contextMtl->endEncoding(true); 672 673 RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(rpDesc); 674 encoder->setStoreAction(MTLStoreActionStore); 675 676 return angle::Result::Continue; 677} 678 679angle::Result ReadTexturePerSliceBytes(const gl::Context *context, 680 const TextureRef &texture, 681 size_t bytesPerRow, 682 const gl::Rectangle &fromRegion, 683 const MipmapNativeLevel &mipLevel, 684 uint32_t sliceOrDepth, 685 uint8_t *dataOut) 686{ 687 ASSERT(texture && texture->valid()); 688 ContextMtl *contextMtl = mtl::GetImpl(context); 689 GLint layer = 0; 690 GLint startDepth = 0; 691 switch (texture->textureType()) 692 { 693 case MTLTextureTypeCube: 694 case MTLTextureType2DArray: 695 layer = sliceOrDepth; 696 break; 697 case MTLTextureType3D: 698 startDepth = sliceOrDepth; 699 break; 700 default: 701 break; 702 } 703 704 MTLRegion mtlRegion = MTLRegionMake3D(fromRegion.x, fromRegion.y, startDepth, fromRegion.width, 705 fromRegion.height, 1); 706 707 texture->getBytes(contextMtl, bytesPerRow, 0, mtlRegion, mipLevel, layer, dataOut); 708 709 return angle::Result::Continue; 710} 711 712angle::Result ReadTexturePerSliceBytesToBuffer(const gl::Context *context, 713 const TextureRef &texture, 714 size_t bytesPerRow, 715 const gl::Rectangle &fromRegion, 716 const MipmapNativeLevel &mipLevel, 717 uint32_t sliceOrDepth, 718 uint32_t dstOffset, 719 const BufferRef &dstBuffer) 720{ 721 ASSERT(texture && texture->valid()); 722 ContextMtl *contextMtl = mtl::GetImpl(context); 723 GLint layer = 0; 724 GLint startDepth = 0; 725 switch (texture->textureType()) 726 { 727 case MTLTextureTypeCube: 728 case MTLTextureType2DArray: 729 layer = sliceOrDepth; 730 break; 731 case MTLTextureType3D: 732 startDepth = sliceOrDepth; 733 break; 734 default: 735 break; 736 } 737 738 MTLRegion mtlRegion = MTLRegionMake3D(fromRegion.x, fromRegion.y, startDepth, fromRegion.width, 739 fromRegion.height, 1); 740 741 BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder(); 742 blitEncoder->copyTextureToBuffer(texture, layer, mipLevel, mtlRegion.origin, mtlRegion.size, 743 dstBuffer, dstOffset, bytesPerRow, 0, MTLBlitOptionNone); 744 745 return angle::Result::Continue; 746} 747 748MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar) 749{ 750 MTLViewport re; 751 752 re.originX = rect.x; 753 re.originY = rect.y; 754 re.width = rect.width; 755 re.height = rect.height; 756 re.znear = znear; 757 re.zfar = zfar; 758 759 return re; 760} 761 762MTLViewport GetViewportFlipY(const gl::Rectangle &rect, 763 NSUInteger screenHeight, 764 double znear, 765 double zfar) 766{ 767 MTLViewport re; 768 769 re.originX = rect.x; 770 re.originY = static_cast<double>(screenHeight) - rect.y1(); 771 re.width = rect.width; 772 re.height = rect.height; 773 re.znear = znear; 774 re.zfar = zfar; 775 776 return re; 777} 778 779MTLViewport GetViewport(const gl::Rectangle &rect, 780 NSUInteger screenHeight, 781 bool flipY, 782 double znear, 783 double zfar) 784{ 785 if (flipY) 786 { 787 return GetViewportFlipY(rect, screenHeight, znear, zfar); 788 } 789 790 return GetViewport(rect, znear, zfar); 791} 792 793MTLScissorRect GetScissorRect(const gl::Rectangle &rect, NSUInteger screenHeight, bool flipY) 794{ 795 MTLScissorRect re; 796 797 re.x = rect.x; 798 re.y = flipY ? (screenHeight - rect.y1()) : rect.y; 799 re.width = rect.width; 800 re.height = rect.height; 801 802 return re; 803} 804 805uint32_t GetDeviceVendorId(id<MTLDevice> metalDevice) 806{ 807 uint32_t vendorId = 0; 808#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 809 vendorId = GetDeviceVendorIdFromIOKit(metalDevice); 810#endif 811 if (!vendorId) 812 { 813 vendorId = GetDeviceVendorIdFromName(metalDevice); 814 } 815 816 return vendorId; 817} 818 819static MTLLanguageVersion GetUserSetOrHighestMSLVersion(const MTLLanguageVersion currentVersion) 820{ 821 const std::string major_str = angle::GetEnvironmentVar(kANGLEMSLVersionMajorEnv); 822 const std::string minor_str = angle::GetEnvironmentVar(kANGLEMSLVersionMinorEnv); 823 if (major_str != "" && minor_str != "") 824 { 825 const int major = std::stoi(major_str); 826 const int minor = std::stoi(minor_str); 827#if !defined(NDEBUG) 828 NSLog(@"Forcing MSL Version: MTLLanguageVersion%d_%d\n", major, minor); 829#endif 830 switch (major) 831 { 832 case 1: 833 switch (minor) 834 { 835 case 0: 836#if !defined(NDEBUG) 837 NSLog(@"MSL 1.0 is deprecated, using MSL 1.1 instead\n"); 838#endif 839 return MTLLanguageVersion1_1; 840 case 1: 841 return MTLLanguageVersion1_1; 842 case 2: 843 return MTLLanguageVersion1_2; 844 default: 845 assert(0 && "Unsupported MSL Minor Language Version."); 846 } 847 break; 848 case 2: 849 switch (minor) 850 { 851 case 0: 852 return MTLLanguageVersion2_0; 853 case 1: 854 return MTLLanguageVersion2_1; 855 case 2: 856 return MTLLanguageVersion2_2; 857 case 3: 858 return MTLLanguageVersion2_3; 859 case 4: 860 if (@available(macOS 12.0, *)) 861 { 862 return MTLLanguageVersion2_4; 863 } 864 assert(0 && "MSL 2.4 requires macOS 12."); 865 break; 866 default: 867 assert(0 && "Unsupported MSL Minor Language Version."); 868 } 869 break; 870 default: 871 assert(0 && "Unsupported MSL Major Language Version."); 872 } 873 } 874 return currentVersion; 875} 876 877AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary( 878 id<MTLDevice> metalDevice, 879 std::string_view source, 880 const std::map<std::string, std::string> &substitutionMacros, 881 bool disableFastMath, 882 bool usesInvariance, 883 AutoObjCPtr<NSError *> *errorOut) 884{ 885 AutoObjCPtr<id<MTLLibrary>> result; 886 ANGLE_MTL_OBJC_SCOPE 887 { 888 NSError *nsError = nil; 889 AutoObjCPtr nsSource = 890 adoptObjCObj([[NSString alloc] initWithBytesNoCopy:const_cast<char *>(source.data()) 891 length:source.length() 892 encoding:NSUTF8StringEncoding 893 freeWhenDone:NO]); 894 AutoObjCPtr options = adoptObjCObj([[MTLCompileOptions alloc] init]); 895 896 // Mark all positions in VS with attribute invariant as non-optimizable 897 options.get().preserveInvariance = usesInvariance; 898 899 if (disableFastMath) 900 { 901 options.get().fastMathEnabled = false; 902 } 903 904 options.get().languageVersion = 905 GetUserSetOrHighestMSLVersion(options.get().languageVersion); 906 907 if (!substitutionMacros.empty()) 908 { 909 auto macroDict = [NSMutableDictionary dictionary]; 910 for (const auto ¯o : substitutionMacros) 911 { 912 [macroDict setObject:@(macro.second.c_str()) forKey:@(macro.first.c_str())]; 913 } 914 options.get().preprocessorMacros = macroDict; 915 } 916 917 auto *platform = ANGLEPlatformCurrent(); 918 double startTime = platform->currentTime(platform); 919 920 result = adoptObjCObj([metalDevice newLibraryWithSource:nsSource.get() 921 options:options.get() 922 error:&nsError]); 923 if (angle::GetBoolEnvironmentVar(kANGLEPrintMSLEnv)) 924 { 925 NSLog(@"%@\n", nsSource.get()); 926 } 927 *errorOut = std::move(nsError); 928 929 int us = static_cast<int>((platform->currentTime(platform) - startTime) * 1e6); 930 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.MetalShaderCompilationTimeUs", us); 931 } 932 return result; 933} 934 935std::string CompileShaderLibraryToFile(const std::string &source, 936 const std::map<std::string, std::string> ¯os, 937 bool disableFastMath, 938 bool usesInvariance) 939{ 940 auto tmpDir = angle::GetTempDirectory(); 941 if (!tmpDir.valid()) 942 { 943 FATAL() << "angle::GetTempDirectory() failed"; 944 } 945 // NOTE: metal/metallib seem to require extensions, otherwise they interpret the files 946 // differently. 947 auto metalFileName = 948 angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".metal"); 949 auto airFileName = angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".air"); 950 auto metallibFileName = 951 angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".metallib"); 952 if (!metalFileName.valid() || !airFileName.valid() || !metallibFileName.valid()) 953 { 954 FATAL() << "Unable to generate temporary files for compiling metal"; 955 } 956 // Save the source. 957 { 958 FILE *fp = fopen(metalFileName.value().c_str(), "wb"); 959 ASSERT(fp); 960 fwrite(source.c_str(), sizeof(char), metalFileName.value().length(), fp); 961 fclose(fp); 962 } 963 964 // metal -> air 965 std::vector<std::string> metalToAirArgv{"/usr/bin/xcrun", 966 "/usr/bin/xcrun", 967 "-sdk", 968 "macosx", 969 "metal", 970 "-std=macos-metal2.0", 971 "-mmacosx-version-min=10.13", 972 "-c", 973 metalFileName.value(), 974 "-o", 975 airFileName.value()}; 976 // Macros are passed using `-D key=value`. 977 for (const auto ¯o : macros) 978 { 979 metalToAirArgv.push_back("-D"); 980 // TODO: not sure if this needs to escape strings or what (for example, might 981 // a space cause problems)? 982 metalToAirArgv.push_back(macro.first + "=" + macro.second); 983 } 984 // TODO: is this right, not sure if MTLCompileOptions.fastMathEnabled is same as -ffast-math. 985 if (!disableFastMath) 986 { 987 metalToAirArgv.push_back("-ffast-math"); 988 } 989 if (usesInvariance) 990 { 991 metalToAirArgv.push_back("-fpreserve-invariance"); 992 } 993 Process metalToAirProcess(metalToAirArgv); 994 int exitCode = -1; 995 if (!metalToAirProcess.DidLaunch() || !metalToAirProcess.WaitForExit(exitCode) || exitCode != 0) 996 { 997 FATAL() << "Generating air file failed"; 998 } 999 1000 // air -> metallib 1001 const std::vector<std::string> airToMetallibArgv{ 1002 "xcrun", "/usr/bin/xcrun", "-sdk", "macosx", 1003 "metallib", airFileName.value(), "-o", metallibFileName.value()}; 1004 Process air_to_metallib_process(airToMetallibArgv); 1005 if (!air_to_metallib_process.DidLaunch() || !air_to_metallib_process.WaitForExit(exitCode) || 1006 exitCode != 0) 1007 { 1008 FATAL() << "Ggenerating metallib file failed"; 1009 } 1010 return metallibFileName.value(); 1011} 1012 1013AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromBinary(id<MTLDevice> metalDevice, 1014 const uint8_t *data, 1015 size_t length, 1016 AutoObjCPtr<NSError *> *errorOut) 1017{ 1018 AutoObjCPtr<id<MTLLibrary>> result; 1019 ANGLE_MTL_OBJC_SCOPE 1020 { 1021 NSError *nsError = nil; 1022 AutoObjCPtr binaryData = adoptObjCObj( 1023 dispatch_data_create(data, length, nullptr, DISPATCH_DATA_DESTRUCTOR_DEFAULT)); 1024 result = adoptObjCObj([metalDevice newLibraryWithData:binaryData.get() error:&nsError]); 1025 *errorOut = std::move(nsError); 1026 } 1027 return result; 1028} 1029 1030AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromStaticBinary(id<MTLDevice> metalDevice, 1031 const uint8_t *data, 1032 size_t length, 1033 AutoObjCPtr<NSError *> *errorOut) 1034{ 1035 AutoObjCPtr<id<MTLLibrary>> result; 1036 ANGLE_MTL_OBJC_SCOPE 1037 { 1038 NSError *nsError = nil; 1039 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 1040 AutoObjCPtr binaryData = adoptObjCObj(dispatch_data_create(data, length, queue, 1041 ^{ 1042 })); 1043 result = adoptObjCObj([metalDevice newLibraryWithData:binaryData.get() error:&nsError]); 1044 *errorOut = std::move(nsError); 1045 } 1046 return result; 1047} 1048MTLTextureType GetTextureType(gl::TextureType glType) 1049{ 1050 switch (glType) 1051 { 1052 case gl::TextureType::_2D: 1053 return MTLTextureType2D; 1054 case gl::TextureType::_2DArray: 1055 return MTLTextureType2DArray; 1056 case gl::TextureType::_3D: 1057 return MTLTextureType3D; 1058 case gl::TextureType::CubeMap: 1059 return MTLTextureTypeCube; 1060 default: 1061 return MTLTextureTypeInvalid; 1062 } 1063} 1064 1065MTLSamplerMinMagFilter GetFilter(GLenum filter) 1066{ 1067 switch (filter) 1068 { 1069 case GL_LINEAR_MIPMAP_LINEAR: 1070 case GL_LINEAR_MIPMAP_NEAREST: 1071 case GL_LINEAR: 1072 return MTLSamplerMinMagFilterLinear; 1073 case GL_NEAREST_MIPMAP_LINEAR: 1074 case GL_NEAREST_MIPMAP_NEAREST: 1075 case GL_NEAREST: 1076 return MTLSamplerMinMagFilterNearest; 1077 default: 1078 UNIMPLEMENTED(); 1079 return MTLSamplerMinMagFilterNearest; 1080 } 1081} 1082 1083MTLSamplerMipFilter GetMipmapFilter(GLenum filter) 1084{ 1085 switch (filter) 1086 { 1087 case GL_LINEAR: 1088 case GL_NEAREST: 1089 return MTLSamplerMipFilterNotMipmapped; 1090 case GL_LINEAR_MIPMAP_LINEAR: 1091 case GL_NEAREST_MIPMAP_LINEAR: 1092 return MTLSamplerMipFilterLinear; 1093 case GL_NEAREST_MIPMAP_NEAREST: 1094 case GL_LINEAR_MIPMAP_NEAREST: 1095 return MTLSamplerMipFilterNearest; 1096 default: 1097 UNIMPLEMENTED(); 1098 return MTLSamplerMipFilterNotMipmapped; 1099 } 1100} 1101 1102MTLSamplerAddressMode GetSamplerAddressMode(GLenum wrap) 1103{ 1104 switch (wrap) 1105 { 1106 case GL_CLAMP_TO_EDGE: 1107 return MTLSamplerAddressModeClampToEdge; 1108 case GL_MIRROR_CLAMP_TO_EDGE_EXT: 1109 return MTLSamplerAddressModeMirrorClampToEdge; 1110 case GL_REPEAT: 1111 return MTLSamplerAddressModeRepeat; 1112 case GL_MIRRORED_REPEAT: 1113 return MTLSamplerAddressModeMirrorRepeat; 1114 default: 1115 UNIMPLEMENTED(); 1116 return MTLSamplerAddressModeClampToEdge; 1117 } 1118} 1119 1120MTLBlendFactor GetBlendFactor(gl::BlendFactorType factor) 1121{ 1122 switch (factor) 1123 { 1124 case gl::BlendFactorType::Zero: 1125 return MTLBlendFactorZero; 1126 case gl::BlendFactorType::One: 1127 return MTLBlendFactorOne; 1128 case gl::BlendFactorType::SrcColor: 1129 return MTLBlendFactorSourceColor; 1130 case gl::BlendFactorType::OneMinusSrcColor: 1131 return MTLBlendFactorOneMinusSourceColor; 1132 case gl::BlendFactorType::SrcAlpha: 1133 return MTLBlendFactorSourceAlpha; 1134 case gl::BlendFactorType::OneMinusSrcAlpha: 1135 return MTLBlendFactorOneMinusSourceAlpha; 1136 case gl::BlendFactorType::DstColor: 1137 return MTLBlendFactorDestinationColor; 1138 case gl::BlendFactorType::OneMinusDstColor: 1139 return MTLBlendFactorOneMinusDestinationColor; 1140 case gl::BlendFactorType::DstAlpha: 1141 return MTLBlendFactorDestinationAlpha; 1142 case gl::BlendFactorType::OneMinusDstAlpha: 1143 return MTLBlendFactorOneMinusDestinationAlpha; 1144 case gl::BlendFactorType::SrcAlphaSaturate: 1145 return MTLBlendFactorSourceAlphaSaturated; 1146 case gl::BlendFactorType::ConstantColor: 1147 return MTLBlendFactorBlendColor; 1148 case gl::BlendFactorType::OneMinusConstantColor: 1149 return MTLBlendFactorOneMinusBlendColor; 1150 case gl::BlendFactorType::ConstantAlpha: 1151 return MTLBlendFactorBlendAlpha; 1152 case gl::BlendFactorType::OneMinusConstantAlpha: 1153 return MTLBlendFactorOneMinusBlendAlpha; 1154 case gl::BlendFactorType::Src1Color: 1155 return MTLBlendFactorSource1Color; 1156 case gl::BlendFactorType::OneMinusSrc1Color: 1157 return MTLBlendFactorOneMinusSource1Color; 1158 case gl::BlendFactorType::Src1Alpha: 1159 return MTLBlendFactorSource1Alpha; 1160 case gl::BlendFactorType::OneMinusSrc1Alpha: 1161 return MTLBlendFactorOneMinusSource1Alpha; 1162 default: 1163 UNREACHABLE(); 1164 return MTLBlendFactorZero; 1165 } 1166} 1167 1168MTLBlendOperation GetBlendOp(gl::BlendEquationType op) 1169{ 1170 switch (op) 1171 { 1172 case gl::BlendEquationType::Add: 1173 return MTLBlendOperationAdd; 1174 case gl::BlendEquationType::Subtract: 1175 return MTLBlendOperationSubtract; 1176 case gl::BlendEquationType::ReverseSubtract: 1177 return MTLBlendOperationReverseSubtract; 1178 case gl::BlendEquationType::Min: 1179 return MTLBlendOperationMin; 1180 case gl::BlendEquationType::Max: 1181 return MTLBlendOperationMax; 1182 default: 1183 UNREACHABLE(); 1184 return MTLBlendOperationAdd; 1185 } 1186} 1187 1188MTLCompareFunction GetCompareFunc(GLenum func) 1189{ 1190 switch (func) 1191 { 1192 case GL_NEVER: 1193 return MTLCompareFunctionNever; 1194 case GL_ALWAYS: 1195 return MTLCompareFunctionAlways; 1196 case GL_LESS: 1197 return MTLCompareFunctionLess; 1198 case GL_LEQUAL: 1199 return MTLCompareFunctionLessEqual; 1200 case GL_EQUAL: 1201 return MTLCompareFunctionEqual; 1202 case GL_GREATER: 1203 return MTLCompareFunctionGreater; 1204 case GL_GEQUAL: 1205 return MTLCompareFunctionGreaterEqual; 1206 case GL_NOTEQUAL: 1207 return MTLCompareFunctionNotEqual; 1208 default: 1209 UNREACHABLE(); 1210 return MTLCompareFunctionAlways; 1211 } 1212} 1213 1214MTLStencilOperation GetStencilOp(GLenum op) 1215{ 1216 switch (op) 1217 { 1218 case GL_KEEP: 1219 return MTLStencilOperationKeep; 1220 case GL_ZERO: 1221 return MTLStencilOperationZero; 1222 case GL_REPLACE: 1223 return MTLStencilOperationReplace; 1224 case GL_INCR: 1225 return MTLStencilOperationIncrementClamp; 1226 case GL_DECR: 1227 return MTLStencilOperationDecrementClamp; 1228 case GL_INCR_WRAP: 1229 return MTLStencilOperationIncrementWrap; 1230 case GL_DECR_WRAP: 1231 return MTLStencilOperationDecrementWrap; 1232 case GL_INVERT: 1233 return MTLStencilOperationInvert; 1234 default: 1235 UNREACHABLE(); 1236 return MTLStencilOperationKeep; 1237 } 1238} 1239 1240MTLWinding GetFrontfaceWinding(GLenum frontFaceMode, bool invert) 1241{ 1242 switch (frontFaceMode) 1243 { 1244 case GL_CW: 1245 return invert ? MTLWindingCounterClockwise : MTLWindingClockwise; 1246 case GL_CCW: 1247 return invert ? MTLWindingClockwise : MTLWindingCounterClockwise; 1248 default: 1249 UNREACHABLE(); 1250 return MTLWindingClockwise; 1251 } 1252} 1253 1254MTLPrimitiveTopologyClass GetPrimitiveTopologyClass(gl::PrimitiveMode mode) 1255{ 1256 // NOTE(hqle): Support layered renderring in future. 1257 // In non-layered rendering mode, unspecified is enough. 1258 return MTLPrimitiveTopologyClassUnspecified; 1259} 1260 1261MTLPrimitiveType GetPrimitiveType(gl::PrimitiveMode mode) 1262{ 1263 switch (mode) 1264 { 1265 case gl::PrimitiveMode::Triangles: 1266 return MTLPrimitiveTypeTriangle; 1267 case gl::PrimitiveMode::Points: 1268 return MTLPrimitiveTypePoint; 1269 case gl::PrimitiveMode::Lines: 1270 return MTLPrimitiveTypeLine; 1271 case gl::PrimitiveMode::LineStrip: 1272 case gl::PrimitiveMode::LineLoop: 1273 return MTLPrimitiveTypeLineStrip; 1274 case gl::PrimitiveMode::TriangleStrip: 1275 return MTLPrimitiveTypeTriangleStrip; 1276 case gl::PrimitiveMode::TriangleFan: 1277 // NOTE(hqle): Emulate triangle fan. 1278 default: 1279 return MTLPrimitiveTypeInvalid; 1280 } 1281} 1282 1283MTLIndexType GetIndexType(gl::DrawElementsType type) 1284{ 1285 switch (type) 1286 { 1287 case gl::DrawElementsType::UnsignedShort: 1288 return MTLIndexTypeUInt16; 1289 case gl::DrawElementsType::UnsignedInt: 1290 return MTLIndexTypeUInt32; 1291 case gl::DrawElementsType::UnsignedByte: 1292 // NOTE(hqle): Convert to supported type 1293 default: 1294 return MTLIndexTypeInvalid; 1295 } 1296} 1297 1298MTLTextureSwizzle GetTextureSwizzle(GLenum swizzle) 1299{ 1300 switch (swizzle) 1301 { 1302 case GL_RED: 1303 return MTLTextureSwizzleRed; 1304 case GL_GREEN: 1305 return MTLTextureSwizzleGreen; 1306 case GL_BLUE: 1307 return MTLTextureSwizzleBlue; 1308 case GL_ALPHA: 1309 return MTLTextureSwizzleAlpha; 1310 case GL_ZERO: 1311 return MTLTextureSwizzleZero; 1312 case GL_ONE: 1313 return MTLTextureSwizzleOne; 1314 default: 1315 UNREACHABLE(); 1316 return MTLTextureSwizzleZero; 1317 } 1318} 1319 1320MTLColorWriteMask GetEmulatedColorWriteMask(const mtl::Format &mtlFormat, bool *isEmulatedOut) 1321{ 1322 const angle::Format &intendedFormat = mtlFormat.intendedAngleFormat(); 1323 const angle::Format &actualFormat = mtlFormat.actualAngleFormat(); 1324 bool isFormatEmulated = false; 1325 MTLColorWriteMask colorWritableMask = MTLColorWriteMaskAll; 1326 if (intendedFormat.alphaBits == 0 && actualFormat.alphaBits) 1327 { 1328 isFormatEmulated = true; 1329 // Disable alpha write to this texture 1330 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskAlpha); 1331 } 1332 if (intendedFormat.luminanceBits == 0) 1333 { 1334 if (intendedFormat.redBits == 0 && actualFormat.redBits) 1335 { 1336 isFormatEmulated = true; 1337 // Disable red write to this texture 1338 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskRed); 1339 } 1340 if (intendedFormat.greenBits == 0 && actualFormat.greenBits) 1341 { 1342 isFormatEmulated = true; 1343 // Disable green write to this texture 1344 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskGreen); 1345 } 1346 if (intendedFormat.blueBits == 0 && actualFormat.blueBits) 1347 { 1348 isFormatEmulated = true; 1349 // Disable blue write to this texture 1350 colorWritableMask = colorWritableMask & (~MTLColorWriteMaskBlue); 1351 } 1352 } 1353 1354 *isEmulatedOut = isFormatEmulated; 1355 1356 return colorWritableMask; 1357} 1358 1359MTLColorWriteMask GetEmulatedColorWriteMask(const mtl::Format &mtlFormat) 1360{ 1361 // Ignore isFormatEmulated boolean value 1362 bool isFormatEmulated; 1363 return GetEmulatedColorWriteMask(mtlFormat, &isFormatEmulated); 1364} 1365 1366bool IsFormatEmulated(const mtl::Format &mtlFormat) 1367{ 1368 bool isFormatEmulated; 1369 (void)GetEmulatedColorWriteMask(mtlFormat, &isFormatEmulated); 1370 return isFormatEmulated; 1371} 1372 1373size_t EstimateTextureSizeInBytes(const mtl::Format &mtlFormat, 1374 size_t width, 1375 size_t height, 1376 size_t depth, 1377 size_t sampleCount, 1378 size_t numMips) 1379{ 1380 size_t textureSizeInBytes; 1381 if (mtlFormat.getCaps().compressed) 1382 { 1383 GLuint textureSize; 1384 gl::Extents size((int)width, (int)height, (int)depth); 1385 if (!mtlFormat.intendedInternalFormat().computeCompressedImageSize(size, &textureSize)) 1386 { 1387 return 0; 1388 } 1389 textureSizeInBytes = textureSize; 1390 } 1391 else 1392 { 1393 textureSizeInBytes = mtlFormat.getCaps().pixelBytes * width * height * depth * sampleCount; 1394 } 1395 if (numMips > 1) 1396 { 1397 // Estimate mipmap size. 1398 textureSizeInBytes = textureSizeInBytes * 4 / 3; 1399 } 1400 return textureSizeInBytes; 1401} 1402 1403MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask) 1404{ 1405 MTLClearColor re = color; 1406 1407 if (!(colorMask & MTLColorWriteMaskAlpha)) 1408 { 1409 re.alpha = kEmulatedAlphaValue; 1410 } 1411 1412 return re; 1413} 1414 1415NSUInteger GetMaxRenderTargetSizeForDeviceInBytes(const mtl::ContextDevice &device) 1416{ 1417 if (SupportsAppleGPUFamily(device, 4)) 1418 { 1419 return 64; 1420 } 1421 else if (SupportsAppleGPUFamily(device, 2)) 1422 { 1423 return 32; 1424 } 1425 else 1426 { 1427 return 16; 1428 } 1429} 1430 1431NSUInteger GetMaxNumberOfRenderTargetsForDevice(const mtl::ContextDevice &device) 1432{ 1433 if (SupportsAppleGPUFamily(device, 2) || SupportsMacGPUFamily(device, 1)) 1434 { 1435 return 8; 1436 } 1437 else 1438 { 1439 return 4; 1440 } 1441} 1442 1443bool DeviceHasMaximumRenderTargetSize(id<MTLDevice> device) 1444{ 1445 return !SupportsMacGPUFamily(device, 1); 1446} 1447 1448bool SupportsAppleGPUFamily(id<MTLDevice> device, uint8_t appleFamily) 1449{ 1450 MTLGPUFamily family; 1451 switch (appleFamily) 1452 { 1453 case 1: 1454 family = MTLGPUFamilyApple1; 1455 break; 1456 case 2: 1457 family = MTLGPUFamilyApple2; 1458 break; 1459 case 3: 1460 family = MTLGPUFamilyApple3; 1461 break; 1462 case 4: 1463 family = MTLGPUFamilyApple4; 1464 break; 1465 case 5: 1466 family = MTLGPUFamilyApple5; 1467 break; 1468 case 6: 1469 family = MTLGPUFamilyApple6; 1470 break; 1471 case 7: 1472 family = MTLGPUFamilyApple7; 1473 break; 1474 default: 1475 return false; 1476 } 1477 return [device supportsFamily:family]; 1478} 1479 1480bool SupportsMacGPUFamily(id<MTLDevice> device, uint8_t macFamily) 1481{ 1482#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 1483 switch (macFamily) 1484 { 1485 case 1: 1486# if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000 1487 return [device supportsFamily:MTLGPUFamilyMacCatalyst1]; 1488# elif TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 130000 1489 return [device supportsFamily:MTLGPUFamilyMac1]; 1490# else 1491 return [device supportsFamily:MTLGPUFamilyMac2]; 1492# endif 1493 case 2: 1494# if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000 1495 return [device supportsFamily:MTLGPUFamilyMacCatalyst2]; 1496# else 1497 return [device supportsFamily:MTLGPUFamilyMac2]; 1498# endif 1499 default: 1500 break; 1501 } 1502#endif 1503 return false; 1504} 1505 1506static NSUInteger getNextLocationForFormat(const FormatCaps &caps, 1507 bool isMSAA, 1508 NSUInteger currentRenderTargetSize) 1509{ 1510 assert(!caps.compressed); 1511 uint8_t alignment = caps.alignment; 1512 NSUInteger pixelBytes = caps.pixelBytes; 1513 NSUInteger pixelBytesMSAA = caps.pixelBytesMSAA; 1514 pixelBytes = isMSAA ? pixelBytesMSAA : pixelBytes; 1515 1516 currentRenderTargetSize = (currentRenderTargetSize + (alignment - 1)) & ~(alignment - 1); 1517 currentRenderTargetSize += pixelBytes; 1518 return currentRenderTargetSize; 1519} 1520 1521static NSUInteger getNextLocationForAttachment(const mtl::RenderPassAttachmentDesc &attachment, 1522 const Context *context, 1523 NSUInteger currentRenderTargetSize) 1524{ 1525 mtl::TextureRef texture = 1526 attachment.implicitMSTexture ? attachment.implicitMSTexture : attachment.texture; 1527 1528 if (texture) 1529 { 1530 MTLPixelFormat pixelFormat = texture->pixelFormat(); 1531 bool isMsaa = texture->samples(); 1532 const FormatCaps &caps = context->getDisplay()->getNativeFormatCaps(pixelFormat); 1533 currentRenderTargetSize = getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize); 1534 } 1535 return currentRenderTargetSize; 1536} 1537 1538NSUInteger ComputeTotalSizeUsedForMTLRenderPassDescriptor(const mtl::RenderPassDesc &descriptor, 1539 const Context *context, 1540 const mtl::ContextDevice &device) 1541{ 1542 NSUInteger currentRenderTargetSize = 0; 1543 1544 for (NSUInteger i = 0; i < GetMaxNumberOfRenderTargetsForDevice(device); i++) 1545 { 1546 currentRenderTargetSize = getNextLocationForAttachment(descriptor.colorAttachments[i], 1547 context, currentRenderTargetSize); 1548 } 1549 if (descriptor.depthAttachment.texture == descriptor.stencilAttachment.texture) 1550 { 1551 currentRenderTargetSize = getNextLocationForAttachment(descriptor.depthAttachment, context, 1552 currentRenderTargetSize); 1553 } 1554 else 1555 { 1556 currentRenderTargetSize = getNextLocationForAttachment(descriptor.depthAttachment, context, 1557 currentRenderTargetSize); 1558 currentRenderTargetSize = getNextLocationForAttachment(descriptor.stencilAttachment, 1559 context, currentRenderTargetSize); 1560 } 1561 1562 return currentRenderTargetSize; 1563} 1564 1565NSUInteger ComputeTotalSizeUsedForMTLRenderPipelineDescriptor( 1566 const MTLRenderPipelineDescriptor *descriptor, 1567 const Context *context, 1568 const mtl::ContextDevice &device) 1569{ 1570 NSUInteger currentRenderTargetSize = 0; 1571 bool isMsaa = descriptor.rasterSampleCount > 1; 1572 for (NSUInteger i = 0; i < GetMaxNumberOfRenderTargetsForDevice(device); i++) 1573 { 1574 MTLRenderPipelineColorAttachmentDescriptor *color = descriptor.colorAttachments[i]; 1575 if (color.pixelFormat != MTLPixelFormatInvalid) 1576 { 1577 const FormatCaps &caps = context->getDisplay()->getNativeFormatCaps(color.pixelFormat); 1578 currentRenderTargetSize = 1579 getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize); 1580 } 1581 } 1582 if (descriptor.depthAttachmentPixelFormat == descriptor.stencilAttachmentPixelFormat) 1583 { 1584 if (descriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid) 1585 { 1586 const FormatCaps &caps = 1587 context->getDisplay()->getNativeFormatCaps(descriptor.depthAttachmentPixelFormat); 1588 currentRenderTargetSize = 1589 getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize); 1590 } 1591 } 1592 else 1593 { 1594 if (descriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid) 1595 { 1596 const FormatCaps &caps = 1597 context->getDisplay()->getNativeFormatCaps(descriptor.depthAttachmentPixelFormat); 1598 currentRenderTargetSize = 1599 getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize); 1600 } 1601 if (descriptor.stencilAttachmentPixelFormat != MTLPixelFormatInvalid) 1602 { 1603 const FormatCaps &caps = 1604 context->getDisplay()->getNativeFormatCaps(descriptor.stencilAttachmentPixelFormat); 1605 currentRenderTargetSize = 1606 getNextLocationForFormat(caps, isMsaa, currentRenderTargetSize); 1607 } 1608 } 1609 return currentRenderTargetSize; 1610} 1611 1612gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion) 1613{ 1614 return gl::Box(static_cast<int>(mtlRegion.origin.x), static_cast<int>(mtlRegion.origin.y), 1615 static_cast<int>(mtlRegion.origin.z), static_cast<int>(mtlRegion.size.width), 1616 static_cast<int>(mtlRegion.size.height), static_cast<int>(mtlRegion.size.depth)); 1617} 1618 1619MipmapNativeLevel GetNativeMipLevel(GLuint level, GLuint base) 1620{ 1621 ASSERT(level >= base); 1622 return MipmapNativeLevel(level - base); 1623} 1624 1625GLuint GetGLMipLevel(const MipmapNativeLevel &nativeLevel, GLuint base) 1626{ 1627 return nativeLevel.get() + base; 1628} 1629 1630angle::Result TriangleFanBoundCheck(ContextMtl *context, size_t numTris) 1631{ 1632 bool indexCheck = 1633 (numTris > std::numeric_limits<unsigned int>::max() / (sizeof(unsigned int) * 3)); 1634 ANGLE_CHECK(context, !indexCheck, 1635 "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, " 1636 "too many indices required.", 1637 GL_OUT_OF_MEMORY); 1638 return angle::Result::Continue; 1639} 1640 1641angle::Result GetTriangleFanIndicesCount(ContextMtl *context, 1642 GLsizei vetexCount, 1643 uint32_t *numElemsOut) 1644{ 1645 size_t numTris = vetexCount - 2; 1646 ANGLE_TRY(TriangleFanBoundCheck(context, numTris)); 1647 size_t numIndices = numTris * 3; 1648 ANGLE_CHECK(context, numIndices <= std::numeric_limits<uint32_t>::max(), 1649 "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, " 1650 "too many indices required.", 1651 GL_OUT_OF_MEMORY); 1652 1653 *numElemsOut = static_cast<uint32_t>(numIndices); 1654 return angle::Result::Continue; 1655} 1656 1657angle::Result CreateMslShader(mtl::Context *context, 1658 id<MTLLibrary> shaderLib, 1659 NSString *shaderName, 1660 MTLFunctionConstantValues *funcConstants, 1661 id<MTLFunction> *shaderOut) 1662{ 1663 NSError *nsErr = nil; 1664 1665 id<MTLFunction> mtlShader; 1666 if (funcConstants) 1667 { 1668 mtlShader = [shaderLib newFunctionWithName:shaderName 1669 constantValues:funcConstants 1670 error:&nsErr]; 1671 } 1672 else 1673 { 1674 mtlShader = [shaderLib newFunctionWithName:shaderName]; 1675 } 1676 1677 [mtlShader ANGLE_MTL_AUTORELEASE]; 1678 if (nsErr && !mtlShader) 1679 { 1680 std::ostringstream ss; 1681 ss << "Internal error compiling Metal shader:\n" 1682 << nsErr.localizedDescription.UTF8String << "\n"; 1683 1684 ERR() << ss.str(); 1685 1686 ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION); 1687 } 1688 *shaderOut = mtlShader; 1689 return angle::Result::Continue; 1690} 1691 1692angle::Result CreateMslShader(Context *context, 1693 id<MTLLibrary> shaderLib, 1694 NSString *shaderName, 1695 MTLFunctionConstantValues *funcConstants, 1696 AutoObjCPtr<id<MTLFunction>> *shaderOut) 1697{ 1698 id<MTLFunction> outFunction; 1699 ANGLE_TRY(CreateMslShader(context, shaderLib, shaderName, funcConstants, &outFunction)); 1700 shaderOut->retainAssign(outFunction); 1701 return angle::Result::Continue; 1702} 1703} // namespace mtl 1704} // namespace rx 1705