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