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.rasterSampleCount = 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_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, rasterSampleCount); 669 670 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, inputPrimitiveTopology); 671 ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, alphaToCoverageEnabled); 672 673 // rasterizationEnabled will be true for both EmulatedDiscard & Enabled. 674 objCDesc.get().rasterizationEnabled = rasterizationEnabled(); 675 676 objCDesc.get().vertexFunction = vertexShader; 677 objCDesc.get().fragmentFunction = objCDesc.get().rasterizationEnabled ? fragmentShader : nil; 678 679 return objCDesc; 680} 681 682// RenderPassDesc implementation 683RenderPassAttachmentDesc::RenderPassAttachmentDesc() 684{ 685 reset(); 686} 687 688void RenderPassAttachmentDesc::reset() 689{ 690 texture.reset(); 691 implicitMSTexture.reset(); 692 level = mtl::kZeroNativeMipLevel; 693 sliceOrDepth = 0; 694 blendable = false; 695 loadAction = MTLLoadActionLoad; 696 storeAction = MTLStoreActionStore; 697 storeActionOptions = MTLStoreActionOptionNone; 698} 699 700bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions( 701 const RenderPassAttachmentDesc &other) const 702{ 703 return texture == other.texture && implicitMSTexture == other.implicitMSTexture && 704 level == other.level && sliceOrDepth == other.sliceOrDepth && 705 blendable == other.blendable; 706} 707 708bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const 709{ 710 if (!equalIgnoreLoadStoreOptions(other)) 711 { 712 return false; 713 } 714 715 return loadAction == other.loadAction && storeAction == other.storeAction && 716 storeActionOptions == other.storeActionOptions; 717} 718 719void RenderPassDesc::populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const 720{ 721 WriteMaskArray writeMaskArray; 722 writeMaskArray.fill(MTLColorWriteMaskAll); 723 populateRenderPipelineOutputDesc(writeMaskArray, outDesc); 724} 725 726void RenderPassDesc::populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray, 727 RenderPipelineOutputDesc *outDesc) const 728{ 729 // Default blend state with replaced color write masks. 730 BlendDescArray blendDescArray; 731 for (size_t i = 0; i < blendDescArray.size(); i++) 732 { 733 blendDescArray[i].reset(writeMaskArray[i]); 734 } 735 populateRenderPipelineOutputDesc(blendDescArray, outDesc); 736} 737 738void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray, 739 RenderPipelineOutputDesc *outDesc) const 740{ 741 RenderPipelineOutputDesc &outputDescriptor = *outDesc; 742 outputDescriptor.numColorAttachments = this->numColorAttachments; 743 outputDescriptor.rasterSampleCount = this->rasterSampleCount; 744 for (uint32_t i = 0; i < this->numColorAttachments; ++i) 745 { 746 auto &renderPassColorAttachment = this->colorAttachments[i]; 747 auto texture = renderPassColorAttachment.texture; 748 749 if (texture) 750 { 751 if (renderPassColorAttachment.blendable && 752 blendDescArray[i].writeMask != MTLColorWriteMaskNone) 753 { 754 // Copy parameters from blend state 755 outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(), 756 blendDescArray[i]); 757 } 758 else 759 { 760 // Disable blending if the attachment's render target doesn't support blending 761 // or if all its color channels are masked out. The latter is needed because: 762 // 763 // * When blending is enabled and *Source1* blend factors are used, Metal 764 // requires a fragment shader to bind both primary and secondary outputs 765 // 766 // * ANGLE frontend validation allows draw calls on draw buffers without 767 // bound fragment outputs if all their color channels are masked out 768 // 769 // * When all color channels are masked out, blending has no effect anyway 770 // 771 // Besides disabling blending, use default values for factors and 772 // operations to reduce the number of unique pipeline states. 773 outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(), 774 blendDescArray[i].writeMask); 775 } 776 777 // Combine the masks. This is useful when the texture is not supposed to have alpha 778 // channel such as GL_RGB8, however, Metal doesn't natively support 24 bit RGB, so 779 // we need to use RGBA texture, and then disable alpha write to this texture 780 outputDescriptor.colorAttachments[i].writeMask &= texture->getColorWritableMask(); 781 } 782 else 783 { 784 785 outputDescriptor.colorAttachments[i].blendingEnabled = false; 786 outputDescriptor.colorAttachments[i].pixelFormat = MTLPixelFormatInvalid; 787 } 788 } 789 790 // Reset the unused output slots to ensure consistent hash value 791 for (uint32_t i = this->numColorAttachments; i < outputDescriptor.colorAttachments.size(); ++i) 792 { 793 outputDescriptor.colorAttachments[i].reset(); 794 } 795 796 auto depthTexture = this->depthAttachment.texture; 797 outputDescriptor.depthAttachmentPixelFormat = 798 depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid; 799 800 auto stencilTexture = this->stencilAttachment.texture; 801 outputDescriptor.stencilAttachmentPixelFormat = 802 stencilTexture ? stencilTexture->pixelFormat() : MTLPixelFormatInvalid; 803} 804 805bool RenderPassDesc::equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const 806{ 807 if (numColorAttachments != other.numColorAttachments) 808 { 809 return false; 810 } 811 812 for (uint32_t i = 0; i < numColorAttachments; ++i) 813 { 814 auto &renderPassColorAttachment = colorAttachments[i]; 815 auto &otherRPAttachment = other.colorAttachments[i]; 816 if (!renderPassColorAttachment.equalIgnoreLoadStoreOptions(otherRPAttachment)) 817 { 818 return false; 819 } 820 } 821 822 if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight) 823 { 824 return false; 825 } 826 827 return depthAttachment.equalIgnoreLoadStoreOptions(other.depthAttachment) && 828 stencilAttachment.equalIgnoreLoadStoreOptions(other.stencilAttachment); 829} 830 831bool RenderPassDesc::operator==(const RenderPassDesc &other) const 832{ 833 if (numColorAttachments != other.numColorAttachments) 834 { 835 return false; 836 } 837 838 for (uint32_t i = 0; i < numColorAttachments; ++i) 839 { 840 auto &renderPassColorAttachment = colorAttachments[i]; 841 auto &otherRPAttachment = other.colorAttachments[i]; 842 if (renderPassColorAttachment != (otherRPAttachment)) 843 { 844 return false; 845 } 846 } 847 848 if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight) 849 { 850 return false; 851 } 852 853 return depthAttachment == other.depthAttachment && stencilAttachment == other.stencilAttachment; 854} 855 856// Convert to Metal object 857void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc, 858 uint32_t deviceMaxRenderTargets) const 859{ 860 ASSERT(deviceMaxRenderTargets <= kMaxRenderTargets); 861 862 for (uint32_t i = 0; i < numColorAttachments; ++i) 863 { 864 ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]); 865 } 866 for (uint32_t i = numColorAttachments; i < deviceMaxRenderTargets; ++i) 867 { 868 // Inactive render target 869 objCDesc.colorAttachments[i].texture = nil; 870 objCDesc.colorAttachments[i].level = 0; 871 objCDesc.colorAttachments[i].slice = 0; 872 objCDesc.colorAttachments[i].depthPlane = 0; 873 objCDesc.colorAttachments[i].loadAction = MTLLoadActionDontCare; 874 objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare; 875 } 876 877 ToObjC(depthAttachment, objCDesc.depthAttachment); 878 ToObjC(stencilAttachment, objCDesc.stencilAttachment); 879 880 if ((defaultWidth | defaultHeight) != 0) 881 { 882 objCDesc.renderTargetWidth = defaultWidth; 883 objCDesc.renderTargetHeight = defaultHeight; 884 objCDesc.defaultRasterSampleCount = 1; 885 } 886} 887 888// ProvokingVertexPipelineDesc 889ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc() 890{ 891 memset(this, 0, sizeof(*this)); 892} 893ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc( 894 const ProvokingVertexComputePipelineDesc &src) 895{ 896 memcpy(this, &src, sizeof(*this)); 897} 898ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc( 899 ProvokingVertexComputePipelineDesc &&src) 900{ 901 memcpy(this, &src, sizeof(*this)); 902} 903ProvokingVertexComputePipelineDesc &ProvokingVertexComputePipelineDesc::operator=( 904 const ProvokingVertexComputePipelineDesc &src) 905{ 906 memcpy(this, &src, sizeof(*this)); 907 return *this; 908} 909bool ProvokingVertexComputePipelineDesc::operator==( 910 const ProvokingVertexComputePipelineDesc &rhs) const 911{ 912 return memcmp(this, &rhs, sizeof(*this)) == 0; 913} 914bool ProvokingVertexComputePipelineDesc::operator!=( 915 const ProvokingVertexComputePipelineDesc &rhs) const 916{ 917 return !(*this == rhs); 918} 919size_t ProvokingVertexComputePipelineDesc::hash() const 920{ 921 return angle::ComputeGenericHash(*this); 922} 923 924// StateCache implementation 925StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {} 926 927StateCache::~StateCache() {} 928 929AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getNullDepthStencilState( 930 const mtl::ContextDevice &device) 931{ 932 if (!mNullDepthStencilState) 933 { 934 DepthStencilDesc desc; 935 desc.reset(); 936 ASSERT(desc.frontFaceStencil.stencilCompareFunction == MTLCompareFunctionAlways); 937 desc.depthWriteEnabled = false; 938 mNullDepthStencilState = getDepthStencilState(device, desc); 939 } 940 return mNullDepthStencilState; 941} 942 943AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getDepthStencilState( 944 const mtl::ContextDevice &device, 945 const DepthStencilDesc &desc) 946{ 947 auto ite = mDepthStencilStates.find(desc); 948 if (ite == mDepthStencilStates.end()) 949 { 950 auto re = mDepthStencilStates.insert( 951 std::make_pair(desc, device.newDepthStencilStateWithDescriptor(ToObjC(desc)))); 952 if (!re.second) 953 { 954 return nil; 955 } 956 957 ite = re.first; 958 } 959 960 return ite->second; 961} 962 963AutoObjCPtr<id<MTLSamplerState>> StateCache::getSamplerState(const mtl::ContextDevice &device, 964 const SamplerDesc &desc) 965{ 966 auto ite = mSamplerStates.find(desc); 967 if (ite == mSamplerStates.end()) 968 { 969 auto objCDesc = ToObjC(desc); 970 if (!mFeatures.allowRuntimeSamplerCompareMode.enabled) 971 { 972 // Runtime sampler compare mode is not supported, fallback to never. 973 objCDesc.get().compareFunction = MTLCompareFunctionNever; 974 } 975 auto re = mSamplerStates.insert( 976 std::make_pair(desc, device.newSamplerStateWithDescriptor(objCDesc))); 977 if (!re.second) 978 return nil; 979 980 ite = re.first; 981 } 982 983 return ite->second; 984} 985 986AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(ContextMtl *context) 987{ 988 return getNullSamplerState(context->getMetalDevice()); 989} 990 991AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(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