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