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_state_cache.mm: 7// Implements StateCache, RenderPipelineCache and various 8// C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors. 9// 10 11#include "libANGLE/renderer/metal/mtl_state_cache.h" 12 13#include <sstream> 14 15#include "common/debug.h" 16#include "common/hash_utils.h" 17#include "libANGLE/renderer/metal/ContextMtl.h" 18#include "libANGLE/renderer/metal/mtl_resources.h" 19#include "libANGLE/renderer/metal/mtl_utils.h" 20#include "platform/autogen/FeaturesMtl_autogen.h" 21 22#define ANGLE_OBJC_CP_PROPERTY(DST, SRC, PROPERTY) \ 23 (DST).PROPERTY = static_cast<__typeof__((DST).PROPERTY)>(ToObjC((SRC).PROPERTY)) 24 25#define ANGLE_PROP_EQ(LHS, RHS, PROP) ((LHS).PROP == (RHS).PROP) 26 27namespace rx 28{ 29namespace mtl 30{ 31 32namespace 33{ 34 35template <class T> 36inline T ToObjC(const T p) 37{ 38 return p; 39} 40 41inline AutoObjCPtr<MTLStencilDescriptor *> ToObjC(const StencilDesc &desc) 42{ 43 auto objCDesc = adoptObjCObj<MTLStencilDescriptor>([[MTLStencilDescriptor alloc] init]); 44 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stencilFailureOperation); 45 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthFailureOperation); 46 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthStencilPassOperation); 47 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stencilCompareFunction); 48 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, readMask); 49 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, writeMask); 50 return objCDesc; 51} 52 53inline AutoObjCPtr<MTLDepthStencilDescriptor *> ToObjC(const DepthStencilDesc &desc) 54{ 55 auto objCDesc = 56 adoptObjCObj<MTLDepthStencilDescriptor>([[MTLDepthStencilDescriptor alloc] init]); 57 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, backFaceStencil); 58 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, frontFaceStencil); 59 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthCompareFunction); 60 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthWriteEnabled); 61 return objCDesc; 62} 63 64inline AutoObjCPtr<MTLSamplerDescriptor *> ToObjC(const SamplerDesc &desc) 65{ 66 auto objCDesc = adoptObjCObj<MTLSamplerDescriptor>([[MTLSamplerDescriptor alloc] init]); 67 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, rAddressMode); 68 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, sAddressMode); 69 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, tAddressMode); 70 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, minFilter); 71 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, magFilter); 72 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, mipFilter); 73 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, maxAnisotropy); 74 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, compareFunction); 75 return objCDesc; 76} 77 78inline AutoObjCPtr<MTLVertexAttributeDescriptor *> ToObjC(const VertexAttributeDesc &desc) 79{ 80 auto objCDesc = adoptObjCObj([[MTLVertexAttributeDescriptor alloc] init]); 81 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, format); 82 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, offset); 83 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, bufferIndex); 84 ASSERT(desc.bufferIndex >= kVboBindingIndexStart); 85 return objCDesc; 86} 87 88inline AutoObjCPtr<MTLVertexBufferLayoutDescriptor *> ToObjC(const VertexBufferLayoutDesc &desc) 89{ 90 auto objCDesc = adoptObjCObj([[MTLVertexBufferLayoutDescriptor alloc] init]); 91 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stepFunction); 92 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stepRate); 93 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stride); 94 return objCDesc; 95} 96 97inline AutoObjCPtr<MTLVertexDescriptor *> ToObjC(const VertexDesc &desc) 98{ 99 auto objCDesc = adoptObjCObj<MTLVertexDescriptor>([[MTLVertexDescriptor alloc] init]); 100 [objCDesc reset]; 101 102 for (uint8_t i = 0; i < desc.numAttribs; ++i) 103 { 104 [objCDesc.get().attributes setObject:ToObjC(desc.attributes[i]) atIndexedSubscript:i]; 105 } 106 107 for (uint8_t i = 0; i < desc.numBufferLayouts; ++i) 108 { 109 // Ignore if stepFunction is kVertexStepFunctionInvalid. 110 // If we don't set this slot, it will apparently be disabled by metal runtime. 111 if (desc.layouts[i].stepFunction != kVertexStepFunctionInvalid) 112 { 113 [objCDesc.get().layouts setObject:ToObjC(desc.layouts[i]) atIndexedSubscript:i]; 114 } 115 } 116 117 return objCDesc; 118} 119 120inline AutoObjCPtr<MTLRenderPipelineColorAttachmentDescriptor *> ToObjC( 121 const RenderPipelineColorAttachmentDesc &desc) 122{ 123 auto objCDesc = adoptObjCObj([[MTLRenderPipelineColorAttachmentDescriptor alloc] init]); 124 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, pixelFormat); 125 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, writeMask); 126 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, alphaBlendOperation); 127 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, rgbBlendOperation); 128 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, destinationAlphaBlendFactor); 129 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, destinationRGBBlendFactor); 130 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, sourceAlphaBlendFactor); 131 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, sourceRGBBlendFactor); 132 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, blendingEnabled); 133 return objCDesc; 134} 135 136id<MTLTexture> ToObjC(const TextureRef &texture) 137{ 138 auto textureRef = texture; 139 return textureRef ? textureRef->get() : nil; 140} 141 142void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src, 143 MTLRenderPassAttachmentDescriptor *dst) 144{ 145 const TextureRef &implicitMsTexture = src.implicitMSTexture; 146 147 if (implicitMsTexture) 148 { 149 dst.texture = ToObjC(implicitMsTexture); 150 dst.level = 0; 151 dst.slice = 0; 152 dst.depthPlane = 0; 153 dst.resolveTexture = ToObjC(src.texture); 154 dst.resolveLevel = src.level.get(); 155 if (dst.resolveTexture.textureType == MTLTextureType3D) 156 { 157 dst.resolveDepthPlane = src.sliceOrDepth; 158 dst.resolveSlice = 0; 159 } 160 else 161 { 162 dst.resolveSlice = src.sliceOrDepth; 163 dst.resolveDepthPlane = 0; 164 } 165 } 166 else 167 { 168 dst.texture = ToObjC(src.texture); 169 dst.level = src.level.get(); 170 if (dst.texture.textureType == MTLTextureType3D) 171 { 172 dst.depthPlane = src.sliceOrDepth; 173 dst.slice = 0; 174 } 175 else 176 { 177 dst.slice = src.sliceOrDepth; 178 dst.depthPlane = 0; 179 } 180 dst.resolveTexture = nil; 181 dst.resolveLevel = 0; 182 dst.resolveSlice = 0; 183 dst.resolveDepthPlane = 0; 184 } 185 186 ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction); 187 ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction); 188 ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions); 189} 190 191void ToObjC(const RenderPassColorAttachmentDesc &desc, 192 MTLRenderPassColorAttachmentDescriptor *objCDesc) 193{ 194 BaseRenderPassAttachmentDescToObjC(desc, objCDesc); 195 196 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearColor); 197} 198 199void ToObjC(const RenderPassDepthAttachmentDesc &desc, 200 MTLRenderPassDepthAttachmentDescriptor *objCDesc) 201{ 202 BaseRenderPassAttachmentDescToObjC(desc, objCDesc); 203 204 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearDepth); 205} 206 207void ToObjC(const RenderPassStencilAttachmentDesc &desc, 208 MTLRenderPassStencilAttachmentDescriptor *objCDesc) 209{ 210 BaseRenderPassAttachmentDescToObjC(desc, objCDesc); 211 212 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil); 213} 214 215MTLColorWriteMask AdjustColorWriteMaskForSharedExponent(MTLColorWriteMask mask) 216{ 217 // For RGB9_E5 color buffers, ANGLE frontend validation ignores alpha writemask value. 218 // Metal validation is more strict and allows only all-enabled or all-disabled. 219 ASSERT((mask == MTLColorWriteMaskAll) || 220 (mask == (MTLColorWriteMaskAll ^ MTLColorWriteMaskAlpha)) || 221 (mask == MTLColorWriteMaskAlpha) || (mask == MTLColorWriteMaskNone)); 222 return (mask & MTLColorWriteMaskBlue) ? MTLColorWriteMaskAll : MTLColorWriteMaskNone; 223} 224 225} // namespace 226 227// StencilDesc implementation 228bool StencilDesc::operator==(const StencilDesc &rhs) const 229{ 230 return ANGLE_PROP_EQ(*this, rhs, stencilFailureOperation) && 231 ANGLE_PROP_EQ(*this, rhs, depthFailureOperation) && 232 ANGLE_PROP_EQ(*this, rhs, depthStencilPassOperation) && 233 234 ANGLE_PROP_EQ(*this, rhs, stencilCompareFunction) && 235 236 ANGLE_PROP_EQ(*this, rhs, readMask) && ANGLE_PROP_EQ(*this, rhs, writeMask); 237} 238 239void StencilDesc::reset() 240{ 241 stencilFailureOperation = depthFailureOperation = depthStencilPassOperation = 242 MTLStencilOperationKeep; 243 244 stencilCompareFunction = MTLCompareFunctionAlways; 245 readMask = writeMask = std::numeric_limits<uint32_t>::max() & mtl::kStencilMaskAll; 246} 247 248// DepthStencilDesc implementation 249DepthStencilDesc::DepthStencilDesc() 250{ 251 memset(this, 0, sizeof(*this)); 252} 253DepthStencilDesc::DepthStencilDesc(const DepthStencilDesc &src) 254{ 255 memcpy(this, &src, sizeof(*this)); 256} 257DepthStencilDesc::DepthStencilDesc(DepthStencilDesc &&src) 258{ 259 memcpy(this, &src, sizeof(*this)); 260} 261 262DepthStencilDesc &DepthStencilDesc::operator=(const DepthStencilDesc &src) 263{ 264 memcpy(this, &src, sizeof(*this)); 265 return *this; 266} 267 268bool DepthStencilDesc::operator==(const DepthStencilDesc &rhs) const 269{ 270 return ANGLE_PROP_EQ(*this, rhs, backFaceStencil) && 271 ANGLE_PROP_EQ(*this, rhs, frontFaceStencil) && 272 273 ANGLE_PROP_EQ(*this, rhs, depthCompareFunction) && 274 275 ANGLE_PROP_EQ(*this, rhs, depthWriteEnabled); 276} 277 278void DepthStencilDesc::reset() 279{ 280 frontFaceStencil.reset(); 281 backFaceStencil.reset(); 282 283 depthCompareFunction = MTLCompareFunctionAlways; 284 depthWriteEnabled = true; 285} 286 287void DepthStencilDesc::updateDepthTestEnabled(const gl::DepthStencilState &dsState) 288{ 289 if (!dsState.depthTest) 290 { 291 depthCompareFunction = MTLCompareFunctionAlways; 292 depthWriteEnabled = false; 293 } 294 else 295 { 296 updateDepthCompareFunc(dsState); 297 updateDepthWriteEnabled(dsState); 298 } 299} 300 301void DepthStencilDesc::updateDepthWriteEnabled(const gl::DepthStencilState &dsState) 302{ 303 depthWriteEnabled = dsState.depthTest && dsState.depthMask; 304} 305 306void DepthStencilDesc::updateDepthCompareFunc(const gl::DepthStencilState &dsState) 307{ 308 if (!dsState.depthTest) 309 { 310 return; 311 } 312 depthCompareFunction = GetCompareFunc(dsState.depthFunc); 313} 314 315void DepthStencilDesc::updateStencilTestEnabled(const gl::DepthStencilState &dsState) 316{ 317 if (!dsState.stencilTest) 318 { 319 frontFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways; 320 frontFaceStencil.depthFailureOperation = MTLStencilOperationKeep; 321 frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep; 322 frontFaceStencil.writeMask = 0; 323 324 backFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways; 325 backFaceStencil.depthFailureOperation = MTLStencilOperationKeep; 326 backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep; 327 backFaceStencil.writeMask = 0; 328 } 329 else 330 { 331 updateStencilFrontFuncs(dsState); 332 updateStencilFrontOps(dsState); 333 updateStencilFrontWriteMask(dsState); 334 updateStencilBackFuncs(dsState); 335 updateStencilBackOps(dsState); 336 updateStencilBackWriteMask(dsState); 337 } 338} 339 340void DepthStencilDesc::updateStencilFrontOps(const gl::DepthStencilState &dsState) 341{ 342 if (!dsState.stencilTest) 343 { 344 return; 345 } 346 frontFaceStencil.stencilFailureOperation = GetStencilOp(dsState.stencilFail); 347 frontFaceStencil.depthFailureOperation = GetStencilOp(dsState.stencilPassDepthFail); 348 frontFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilPassDepthPass); 349} 350 351void DepthStencilDesc::updateStencilBackOps(const gl::DepthStencilState &dsState) 352{ 353 if (!dsState.stencilTest) 354 { 355 return; 356 } 357 backFaceStencil.stencilFailureOperation = GetStencilOp(dsState.stencilBackFail); 358 backFaceStencil.depthFailureOperation = GetStencilOp(dsState.stencilBackPassDepthFail); 359 backFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilBackPassDepthPass); 360} 361 362void DepthStencilDesc::updateStencilFrontFuncs(const gl::DepthStencilState &dsState) 363{ 364 if (!dsState.stencilTest) 365 { 366 return; 367 } 368 frontFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilFunc); 369 frontFaceStencil.readMask = dsState.stencilMask & mtl::kStencilMaskAll; 370} 371 372void DepthStencilDesc::updateStencilBackFuncs(const gl::DepthStencilState &dsState) 373{ 374 if (!dsState.stencilTest) 375 { 376 return; 377 } 378 backFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilBackFunc); 379 backFaceStencil.readMask = dsState.stencilBackMask & mtl::kStencilMaskAll; 380} 381 382void DepthStencilDesc::updateStencilFrontWriteMask(const gl::DepthStencilState &dsState) 383{ 384 if (!dsState.stencilTest) 385 { 386 return; 387 } 388 frontFaceStencil.writeMask = dsState.stencilWritemask & mtl::kStencilMaskAll; 389} 390 391void DepthStencilDesc::updateStencilBackWriteMask(const gl::DepthStencilState &dsState) 392{ 393 if (!dsState.stencilTest) 394 { 395 return; 396 } 397 backFaceStencil.writeMask = dsState.stencilBackWritemask & mtl::kStencilMaskAll; 398} 399 400size_t DepthStencilDesc::hash() const 401{ 402 return angle::ComputeGenericHash(*this); 403} 404 405// SamplerDesc implementation 406SamplerDesc::SamplerDesc() 407{ 408 memset(this, 0, sizeof(*this)); 409} 410SamplerDesc::SamplerDesc(const SamplerDesc &src) 411{ 412 memcpy(this, &src, sizeof(*this)); 413} 414SamplerDesc::SamplerDesc(SamplerDesc &&src) 415{ 416 memcpy(this, &src, sizeof(*this)); 417} 418 419SamplerDesc::SamplerDesc(const gl::SamplerState &glState) : SamplerDesc() 420{ 421 rAddressMode = GetSamplerAddressMode(glState.getWrapR()); 422 sAddressMode = GetSamplerAddressMode(glState.getWrapS()); 423 tAddressMode = GetSamplerAddressMode(glState.getWrapT()); 424 425 minFilter = GetFilter(glState.getMinFilter()); 426 magFilter = GetFilter(glState.getMagFilter()); 427 mipFilter = GetMipmapFilter(glState.getMinFilter()); 428 429 maxAnisotropy = static_cast<uint32_t>(glState.getMaxAnisotropy()); 430 431 compareFunction = GetCompareFunc(glState.getCompareFunc()); 432} 433 434SamplerDesc &SamplerDesc::operator=(const SamplerDesc &src) 435{ 436 memcpy(this, &src, sizeof(*this)); 437 return *this; 438} 439 440void SamplerDesc::reset() 441{ 442 rAddressMode = MTLSamplerAddressModeClampToEdge; 443 sAddressMode = MTLSamplerAddressModeClampToEdge; 444 tAddressMode = MTLSamplerAddressModeClampToEdge; 445 446 minFilter = MTLSamplerMinMagFilterNearest; 447 magFilter = MTLSamplerMinMagFilterNearest; 448 mipFilter = MTLSamplerMipFilterNearest; 449 450 maxAnisotropy = 1; 451 452 compareFunction = MTLCompareFunctionNever; 453} 454 455bool SamplerDesc::operator==(const SamplerDesc &rhs) const 456{ 457 return ANGLE_PROP_EQ(*this, rhs, rAddressMode) && ANGLE_PROP_EQ(*this, rhs, sAddressMode) && 458 ANGLE_PROP_EQ(*this, rhs, tAddressMode) && 459 460 ANGLE_PROP_EQ(*this, rhs, minFilter) && ANGLE_PROP_EQ(*this, rhs, magFilter) && 461 ANGLE_PROP_EQ(*this, rhs, mipFilter) && 462 463 ANGLE_PROP_EQ(*this, rhs, maxAnisotropy) && 464 465 ANGLE_PROP_EQ(*this, rhs, compareFunction); 466} 467 468size_t SamplerDesc::hash() const 469{ 470 return angle::ComputeGenericHash(*this); 471} 472 473// BlendDesc implementation 474bool BlendDesc::operator==(const BlendDesc &rhs) const 475{ 476 return ANGLE_PROP_EQ(*this, rhs, writeMask) && 477 478 ANGLE_PROP_EQ(*this, rhs, alphaBlendOperation) && 479 ANGLE_PROP_EQ(*this, rhs, rgbBlendOperation) && 480 481 ANGLE_PROP_EQ(*this, rhs, destinationAlphaBlendFactor) && 482 ANGLE_PROP_EQ(*this, rhs, destinationRGBBlendFactor) && 483 ANGLE_PROP_EQ(*this, rhs, sourceAlphaBlendFactor) && 484 ANGLE_PROP_EQ(*this, rhs, sourceRGBBlendFactor) && 485 486 ANGLE_PROP_EQ(*this, rhs, blendingEnabled); 487} 488 489void BlendDesc::reset() 490{ 491 reset(MTLColorWriteMaskAll); 492} 493 494void BlendDesc::reset(MTLColorWriteMask _writeMask) 495{ 496 writeMask = _writeMask; 497 498 blendingEnabled = false; 499 alphaBlendOperation = rgbBlendOperation = MTLBlendOperationAdd; 500 501 destinationAlphaBlendFactor = destinationRGBBlendFactor = MTLBlendFactorZero; 502 sourceAlphaBlendFactor = sourceRGBBlendFactor = MTLBlendFactorOne; 503} 504 505void BlendDesc::updateWriteMask(const uint8_t angleMask) 506{ 507 ASSERT(angleMask == (angleMask & 0xF)); 508 509// ANGLE's packed color mask is abgr (matches Vulkan & D3D11), while Metal expects rgba. 510#if defined(__aarch64__) 511 // ARM64 can reverse bits in a single instruction 512 writeMask = __builtin_bitreverse8(angleMask) >> 4; 513#else 514 /* On other architectures, Clang generates a polyfill that uses more 515 instructions than the following expression optimized for a 4-bit value. 516 517 (abgr * 0x41) & 0x14A: 518 .......abgr + 519 .abgr...... & 520 00101001010 = 521 ..b.r..a.g. 522 523 (b.r..a.g.) * 0x111: 524 b.r..a.g. + 525 b.r..a.g..... + 526 b.r..a.g......... = 527 b.r.bargbarg.a.g. 528 ^^^^ 529 */ 530 writeMask = ((((angleMask * 0x41) & 0x14A) * 0x111) >> 7) & 0xF; 531#endif 532} 533 534// RenderPipelineColorAttachmentDesc implementation 535bool RenderPipelineColorAttachmentDesc::operator==( 536 const RenderPipelineColorAttachmentDesc &rhs) const 537{ 538 if (!BlendDesc::operator==(rhs)) 539 { 540 return false; 541 } 542 return ANGLE_PROP_EQ(*this, rhs, pixelFormat); 543} 544 545void RenderPipelineColorAttachmentDesc::reset() 546{ 547 reset(MTLPixelFormatInvalid); 548} 549 550void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format) 551{ 552 reset(format, MTLColorWriteMaskAll); 553} 554 555void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, MTLColorWriteMask _writeMask) 556{ 557 this->pixelFormat = format; 558 559 if (format == MTLPixelFormatRGB9E5Float) 560 { 561 _writeMask = AdjustColorWriteMaskForSharedExponent(_writeMask); 562 } 563 564 BlendDesc::reset(_writeMask); 565} 566 567void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, const BlendDesc &blendDesc) 568{ 569 this->pixelFormat = format; 570 571 BlendDesc::operator=(blendDesc); 572 573 if (format == MTLPixelFormatRGB9E5Float) 574 { 575 writeMask = AdjustColorWriteMaskForSharedExponent(writeMask); 576 } 577} 578 579// RenderPipelineOutputDesc implementation 580bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) const 581{ 582 if (numColorAttachments != rhs.numColorAttachments) 583 { 584 return false; 585 } 586 587 for (uint8_t i = 0; i < numColorAttachments; ++i) 588 { 589 if (colorAttachments[i] != rhs.colorAttachments[i]) 590 { 591 return false; 592 } 593 } 594 595 return ANGLE_PROP_EQ(*this, rhs, depthAttachmentPixelFormat) && 596 ANGLE_PROP_EQ(*this, rhs, stencilAttachmentPixelFormat); 597} 598 599void RenderPipelineOutputDesc::updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers) 600{ 601 for (uint32_t colorIndex = 0; colorIndex < this->numColorAttachments; ++colorIndex) 602 { 603 if (!enabledBuffers.test(colorIndex)) 604 { 605 this->colorAttachments[colorIndex].writeMask = MTLColorWriteMaskNone; 606 } 607 } 608} 609 610// RenderPipelineDesc implementation 611RenderPipelineDesc::RenderPipelineDesc() 612{ 613 memset(this, 0, sizeof(*this)); 614 outputDescriptor.sampleCount = 1; 615 rasterizationType = RenderPipelineRasterization::Enabled; 616} 617 618RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src) 619{ 620 memcpy(this, &src, sizeof(*this)); 621} 622 623RenderPipelineDesc::RenderPipelineDesc(RenderPipelineDesc &&src) 624{ 625 memcpy(this, &src, sizeof(*this)); 626} 627 628RenderPipelineDesc &RenderPipelineDesc::operator=(const RenderPipelineDesc &src) 629{ 630 memcpy(this, &src, sizeof(*this)); 631 return *this; 632} 633 634bool RenderPipelineDesc::operator==(const RenderPipelineDesc &rhs) const 635{ 636 // NOTE(hqle): Use a faster way to compare, i.e take into account 637 // the number of active vertex attributes & render targets. 638 // If that way is used, hash() method must be changed also. 639 return memcmp(this, &rhs, sizeof(*this)) == 0; 640} 641 642size_t RenderPipelineDesc::hash() const 643{ 644 return angle::ComputeGenericHash(*this); 645} 646 647bool RenderPipelineDesc::rasterizationEnabled() const 648{ 649 return rasterizationType != RenderPipelineRasterization::Disabled; 650} 651 652AutoObjCPtr<MTLRenderPipelineDescriptor *> RenderPipelineDesc::createMetalDesc( 653 id<MTLFunction> vertexShader, 654 id<MTLFunction> fragmentShader) const 655{ 656 auto objCDesc = adoptObjCObj([[MTLRenderPipelineDescriptor alloc] init]); 657 [objCDesc reset]; 658 659 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, vertexDescriptor); 660 661 for (uint8_t i = 0; i < outputDescriptor.numColorAttachments; ++i) 662 { 663 [objCDesc.get().colorAttachments setObject:ToObjC(outputDescriptor.colorAttachments[i]) 664 atIndexedSubscript:i]; 665 } 666 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, depthAttachmentPixelFormat); 667 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, stencilAttachmentPixelFormat); 668 ANGLE_APPLE_ALLOW_DEPRECATED_BEGIN 669 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, sampleCount); 670 ANGLE_APPLE_ALLOW_DEPRECATED_END 671 672#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 673 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, inputPrimitiveTopology); 674#endif 675 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, alphaToCoverageEnabled); 676 677 // rasterizationEnabled will be true for both EmulatedDiscard & Enabled. 678 objCDesc.get().rasterizationEnabled = rasterizationEnabled(); 679 680 objCDesc.get().vertexFunction = vertexShader; 681 objCDesc.get().fragmentFunction = objCDesc.get().rasterizationEnabled ? fragmentShader : nil; 682 683 return objCDesc; 684} 685 686// RenderPassDesc implementation 687RenderPassAttachmentDesc::RenderPassAttachmentDesc() 688{ 689 reset(); 690} 691 692void RenderPassAttachmentDesc::reset() 693{ 694 texture.reset(); 695 implicitMSTexture.reset(); 696 level = mtl::kZeroNativeMipLevel; 697 sliceOrDepth = 0; 698 blendable = false; 699 loadAction = MTLLoadActionLoad; 700 storeAction = MTLStoreActionStore; 701 storeActionOptions = MTLStoreActionOptionNone; 702} 703 704bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions( 705 const RenderPassAttachmentDesc &other) const 706{ 707 return texture == other.texture && implicitMSTexture == other.implicitMSTexture && 708 level == other.level && sliceOrDepth == other.sliceOrDepth && 709 blendable == other.blendable; 710} 711 712bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const 713{ 714 if (!equalIgnoreLoadStoreOptions(other)) 715 { 716 return false; 717 } 718 719 return loadAction == other.loadAction && storeAction == other.storeAction && 720 storeActionOptions == other.storeActionOptions; 721} 722 723void RenderPassDesc::populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const 724{ 725 WriteMaskArray writeMaskArray; 726 writeMaskArray.fill(MTLColorWriteMaskAll); 727 populateRenderPipelineOutputDesc(writeMaskArray, outDesc); 728} 729 730void RenderPassDesc::populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray, 731 RenderPipelineOutputDesc *outDesc) const 732{ 733 // Default blend state with replaced color write masks. 734 BlendDescArray blendDescArray; 735 for (size_t i = 0; i < blendDescArray.size(); i++) 736 { 737 blendDescArray[i].reset(writeMaskArray[i]); 738 } 739 populateRenderPipelineOutputDesc(blendDescArray, outDesc); 740} 741 742void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray, 743 RenderPipelineOutputDesc *outDesc) const 744{ 745 RenderPipelineOutputDesc &outputDescriptor = *outDesc; 746 outputDescriptor.numColorAttachments = this->numColorAttachments; 747 outputDescriptor.sampleCount = this->sampleCount; 748 for (uint32_t i = 0; i < this->numColorAttachments; ++i) 749 { 750 auto &renderPassColorAttachment = this->colorAttachments[i]; 751 auto texture = renderPassColorAttachment.texture; 752 753 if (texture) 754 { 755 if (renderPassColorAttachment.blendable && 756 blendDescArray[i].writeMask != MTLColorWriteMaskNone) 757 { 758 // Copy parameters from blend state 759 outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(), 760 blendDescArray[i]); 761 } 762 else 763 { 764 // Disable blending if the attachment's render target doesn't support blending 765 // or if all its color channels are masked out. The latter is needed because: 766 // 767 // * When blending is enabled and *Source1* blend factors are used, Metal 768 // requires a fragment shader to bind both primary and secondary outputs 769 // 770 // * ANGLE frontend validation allows draw calls on draw buffers without 771 // bound fragment outputs if all their color channels are masked out 772 // 773 // * When all color channels are masked out, blending has no effect anyway 774 // 775 // Besides disabling blending, use default values for factors and 776 // operations to reduce the number of unique pipeline states. 777 outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(), 778 blendDescArray[i].writeMask); 779 } 780 781 // Combine the masks. This is useful when the texture is not supposed to have alpha 782 // channel such as GL_RGB8, however, Metal doesn't natively support 24 bit RGB, so 783 // we need to use RGBA texture, and then disable alpha write to this texture 784 outputDescriptor.colorAttachments[i].writeMask &= texture->getColorWritableMask(); 785 } 786 else 787 { 788 789 outputDescriptor.colorAttachments[i].blendingEnabled = false; 790 outputDescriptor.colorAttachments[i].pixelFormat = MTLPixelFormatInvalid; 791 } 792 } 793 794 // Reset the unused output slots to ensure consistent hash value 795 for (uint32_t i = this->numColorAttachments; i < outputDescriptor.colorAttachments.size(); ++i) 796 { 797 outputDescriptor.colorAttachments[i].reset(); 798 } 799 800 auto depthTexture = this->depthAttachment.texture; 801 outputDescriptor.depthAttachmentPixelFormat = 802 depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid; 803 804 auto stencilTexture = this->stencilAttachment.texture; 805 outputDescriptor.stencilAttachmentPixelFormat = 806 stencilTexture ? stencilTexture->pixelFormat() : MTLPixelFormatInvalid; 807} 808 809bool RenderPassDesc::equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const 810{ 811 if (numColorAttachments != other.numColorAttachments) 812 { 813 return false; 814 } 815 816 for (uint32_t i = 0; i < numColorAttachments; ++i) 817 { 818 auto &renderPassColorAttachment = colorAttachments[i]; 819 auto &otherRPAttachment = other.colorAttachments[i]; 820 if (!renderPassColorAttachment.equalIgnoreLoadStoreOptions(otherRPAttachment)) 821 { 822 return false; 823 } 824 } 825 826 if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight) 827 { 828 return false; 829 } 830 831 return depthAttachment.equalIgnoreLoadStoreOptions(other.depthAttachment) && 832 stencilAttachment.equalIgnoreLoadStoreOptions(other.stencilAttachment); 833} 834 835bool RenderPassDesc::operator==(const RenderPassDesc &other) const 836{ 837 if (numColorAttachments != other.numColorAttachments) 838 { 839 return false; 840 } 841 842 for (uint32_t i = 0; i < numColorAttachments; ++i) 843 { 844 auto &renderPassColorAttachment = colorAttachments[i]; 845 auto &otherRPAttachment = other.colorAttachments[i]; 846 if (renderPassColorAttachment != (otherRPAttachment)) 847 { 848 return false; 849 } 850 } 851 852 if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight) 853 { 854 return false; 855 } 856 857 return depthAttachment == other.depthAttachment && stencilAttachment == other.stencilAttachment; 858} 859 860// Convert to Metal object 861void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc, 862 uint32_t deviceMaxRenderTargets) const 863{ 864 ASSERT(deviceMaxRenderTargets <= kMaxRenderTargets); 865 866 for (uint32_t i = 0; i < numColorAttachments; ++i) 867 { 868 ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]); 869 } 870 for (uint32_t i = numColorAttachments; i < deviceMaxRenderTargets; ++i) 871 { 872 // Inactive render target 873 objCDesc.colorAttachments[i].texture = nil; 874 objCDesc.colorAttachments[i].level = 0; 875 objCDesc.colorAttachments[i].slice = 0; 876 objCDesc.colorAttachments[i].depthPlane = 0; 877 objCDesc.colorAttachments[i].loadAction = MTLLoadActionDontCare; 878 objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare; 879 } 880 881 ToObjC(depthAttachment, objCDesc.depthAttachment); 882 ToObjC(stencilAttachment, objCDesc.stencilAttachment); 883 884 if ((defaultWidth | defaultHeight) != 0) 885 { 886 objCDesc.renderTargetWidth = defaultWidth; 887 objCDesc.renderTargetHeight = defaultHeight; 888 objCDesc.defaultRasterSampleCount = 1; 889 } 890} 891 892// ProvokingVertexPipelineDesc 893ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc() 894{ 895 memset(this, 0, sizeof(*this)); 896} 897ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc( 898 const ProvokingVertexComputePipelineDesc &src) 899{ 900 memcpy(this, &src, sizeof(*this)); 901} 902ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc( 903 ProvokingVertexComputePipelineDesc &&src) 904{ 905 memcpy(this, &src, sizeof(*this)); 906} 907ProvokingVertexComputePipelineDesc &ProvokingVertexComputePipelineDesc::operator=( 908 const ProvokingVertexComputePipelineDesc &src) 909{ 910 memcpy(this, &src, sizeof(*this)); 911 return *this; 912} 913bool ProvokingVertexComputePipelineDesc::operator==( 914 const ProvokingVertexComputePipelineDesc &rhs) const 915{ 916 return memcmp(this, &rhs, sizeof(*this)) == 0; 917} 918bool ProvokingVertexComputePipelineDesc::operator!=( 919 const ProvokingVertexComputePipelineDesc &rhs) const 920{ 921 return !(*this == rhs); 922} 923size_t ProvokingVertexComputePipelineDesc::hash() const 924{ 925 return angle::ComputeGenericHash(*this); 926} 927 928// StateCache implementation 929StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {} 930 931StateCache::~StateCache() {} 932 933AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getNullDepthStencilState( 934 const mtl::ContextDevice &device) 935{ 936 if (!mNullDepthStencilState) 937 { 938 DepthStencilDesc desc; 939 desc.reset(); 940 ASSERT(desc.frontFaceStencil.stencilCompareFunction == MTLCompareFunctionAlways); 941 desc.depthWriteEnabled = false; 942 mNullDepthStencilState = getDepthStencilState(device, desc); 943 } 944 return mNullDepthStencilState; 945} 946 947AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getDepthStencilState( 948 const mtl::ContextDevice &device, 949 const DepthStencilDesc &desc) 950{ 951 auto ite = mDepthStencilStates.find(desc); 952 if (ite == mDepthStencilStates.end()) 953 { 954 auto re = mDepthStencilStates.insert( 955 std::make_pair(desc, device.newDepthStencilStateWithDescriptor(ToObjC(desc)))); 956 if (!re.second) 957 { 958 return nil; 959 } 960 961 ite = re.first; 962 } 963 964 return ite->second; 965} 966 967AutoObjCPtr<id<MTLSamplerState>> StateCache::getSamplerState(const mtl::ContextDevice &device, 968 const SamplerDesc &desc) 969{ 970 auto ite = mSamplerStates.find(desc); 971 if (ite == mSamplerStates.end()) 972 { 973 auto objCDesc = ToObjC(desc); 974 if (!mFeatures.allowRuntimeSamplerCompareMode.enabled) 975 { 976 // Runtime sampler compare mode is not supported, fallback to never. 977 objCDesc.get().compareFunction = MTLCompareFunctionNever; 978 } 979 auto re = mSamplerStates.insert( 980 std::make_pair(desc, device.newSamplerStateWithDescriptor(objCDesc))); 981 if (!re.second) 982 return nil; 983 984 ite = re.first; 985 } 986 987 return ite->second; 988} 989 990AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(ContextMtl *context) 991{ 992 return getNullSamplerState(context->getMetalDevice()); 993} 994 995AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(const mtl::ContextDevice &device) 996{ 997 SamplerDesc desc; 998 desc.reset(); 999 1000 return getSamplerState(device, desc); 1001} 1002 1003void StateCache::clear() 1004{ 1005 mNullDepthStencilState = nil; 1006 mDepthStencilStates.clear(); 1007 mSamplerStates.clear(); 1008} 1009 1010} // namespace mtl 1011} // namespace rx 1012