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