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.h: 7 // Defines the class interface for StateCache, RenderPipelineCache and various 8 // C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors. 9 // 10 11 #ifndef LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ 12 #define LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ 13 14 #import <Metal/Metal.h> 15 16 #include <unordered_map> 17 18 #include "libANGLE/State.h" 19 #include "libANGLE/angletypes.h" 20 #include "libANGLE/renderer/metal/mtl_common.h" 21 #include "libANGLE/renderer/metal/mtl_resources.h" 22 23 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs); 24 25 namespace angle 26 { 27 struct FeaturesMtl; 28 } 29 30 namespace rx 31 { 32 class ContextMtl; 33 34 namespace mtl 35 { 36 struct alignas(1) StencilDesc 37 { 38 bool operator==(const StencilDesc &rhs) const; 39 40 // Set default values 41 void reset(); 42 43 // Use uint8_t instead of MTLStencilOperation to compact space 44 uint8_t stencilFailureOperation : 3; 45 uint8_t depthFailureOperation : 3; 46 uint8_t depthStencilPassOperation : 3; 47 48 // Use uint8_t instead of MTLCompareFunction to compact space 49 uint8_t stencilCompareFunction : 3; 50 51 uint8_t readMask : 8; 52 uint8_t writeMask : 8; 53 }; 54 55 struct alignas(4) DepthStencilDesc 56 { 57 DepthStencilDesc(); 58 DepthStencilDesc(const DepthStencilDesc &src); 59 DepthStencilDesc(DepthStencilDesc &&src); 60 61 DepthStencilDesc &operator=(const DepthStencilDesc &src); 62 63 bool operator==(const DepthStencilDesc &rhs) const; 64 65 // Set default values. 66 // Default is depth/stencil test disabled. Depth/stencil write enabled. 67 void reset(); 68 69 size_t hash() const; 70 71 void updateDepthTestEnabled(const gl::DepthStencilState &dsState); 72 void updateDepthWriteEnabled(const gl::DepthStencilState &dsState); 73 void updateDepthCompareFunc(const gl::DepthStencilState &dsState); 74 void updateStencilTestEnabled(const gl::DepthStencilState &dsState); 75 void updateStencilFrontOps(const gl::DepthStencilState &dsState); 76 void updateStencilBackOps(const gl::DepthStencilState &dsState); 77 void updateStencilFrontFuncs(const gl::DepthStencilState &dsState); 78 void updateStencilBackFuncs(const gl::DepthStencilState &dsState); 79 void updateStencilFrontWriteMask(const gl::DepthStencilState &dsState); 80 void updateStencilBackWriteMask(const gl::DepthStencilState &dsState); 81 82 StencilDesc backFaceStencil; 83 StencilDesc frontFaceStencil; 84 85 // Use uint8_t instead of MTLCompareFunction to compact space 86 uint8_t depthCompareFunction : 3; 87 bool depthWriteEnabled : 1; 88 }; 89 90 struct alignas(4) SamplerDesc 91 { 92 SamplerDesc(); 93 SamplerDesc(const SamplerDesc &src); 94 SamplerDesc(SamplerDesc &&src); 95 96 explicit SamplerDesc(const gl::SamplerState &glState); 97 98 SamplerDesc &operator=(const SamplerDesc &src); 99 100 // Set default values. All filters are nearest, and addresModes are clamp to edge. 101 void reset(); 102 103 bool operator==(const SamplerDesc &rhs) const; 104 105 size_t hash() const; 106 107 // Use uint8_t instead of MTLSamplerAddressMode to compact space 108 uint8_t rAddressMode : 3; 109 uint8_t sAddressMode : 3; 110 uint8_t tAddressMode : 3; 111 112 // Use uint8_t instead of MTLSamplerMinMagFilter to compact space 113 uint8_t minFilter : 1; 114 uint8_t magFilter : 1; 115 uint8_t mipFilter : 2; 116 117 uint8_t maxAnisotropy : 5; 118 119 // Use uint8_t instead of MTLCompareFunction to compact space 120 uint8_t compareFunction : 3; 121 }; 122 123 struct VertexAttributeDesc 124 { 125 inline bool operator==(const VertexAttributeDesc &rhs) const 126 { 127 return format == rhs.format && offset == rhs.offset && bufferIndex == rhs.bufferIndex; 128 } 129 inline bool operator!=(const VertexAttributeDesc &rhs) const { return !(*this == rhs); } 130 131 // Use uint8_t instead of MTLVertexFormat to compact space 132 uint8_t format : 6; 133 // Offset is only used for default attributes buffer. So 8 bits are enough. 134 uint8_t offset : 8; 135 uint8_t bufferIndex : 5; 136 }; 137 138 struct VertexBufferLayoutDesc 139 { 140 inline bool operator==(const VertexBufferLayoutDesc &rhs) const 141 { 142 return stepFunction == rhs.stepFunction && stepRate == rhs.stepRate && stride == rhs.stride; 143 } 144 inline bool operator!=(const VertexBufferLayoutDesc &rhs) const { return !(*this == rhs); } 145 146 uint32_t stepRate; 147 uint32_t stride; 148 149 // Use uint8_t instead of MTLVertexStepFunction to compact space 150 uint8_t stepFunction; 151 }; 152 153 struct VertexDesc 154 { 155 VertexAttributeDesc attributes[kMaxVertexAttribs]; 156 VertexBufferLayoutDesc layouts[kMaxVertexAttribs]; 157 158 uint8_t numAttribs; 159 uint8_t numBufferLayouts; 160 }; 161 162 struct BlendDesc 163 { 164 bool operator==(const BlendDesc &rhs) const; 165 BlendDesc &operator=(const BlendDesc &src) = default; 166 167 // Set default values 168 void reset(); 169 void reset(MTLColorWriteMask writeMask); 170 171 void updateWriteMask(const uint8_t angleMask); 172 173 // Use uint8_t instead of MTLColorWriteMask to compact space 174 uint8_t writeMask : 4; 175 176 // Use uint8_t instead of MTLBlendOperation to compact space 177 uint8_t alphaBlendOperation : 3; 178 uint8_t rgbBlendOperation : 3; 179 180 // Use uint8_t instead of MTLBlendFactor to compact space 181 // NOTE(hqle): enum MTLBlendFactorSource1Color and above are unused. 182 uint8_t destinationAlphaBlendFactor : 4; 183 uint8_t destinationRGBBlendFactor : 4; 184 uint8_t sourceAlphaBlendFactor : 4; 185 uint8_t sourceRGBBlendFactor : 4; 186 187 bool blendingEnabled : 1; 188 }; 189 190 using BlendDescArray = std::array<BlendDesc, kMaxRenderTargets>; 191 using WriteMaskArray = std::array<uint8_t, kMaxRenderTargets>; 192 193 struct alignas(2) RenderPipelineColorAttachmentDesc : public BlendDesc 194 { 195 bool operator==(const RenderPipelineColorAttachmentDesc &rhs) const; 196 inline bool operator!=(const RenderPipelineColorAttachmentDesc &rhs) const 197 { 198 return !(*this == rhs); 199 } 200 201 // Set default values 202 void reset(); 203 void reset(MTLPixelFormat format); 204 void reset(MTLPixelFormat format, MTLColorWriteMask writeMask); 205 void reset(MTLPixelFormat format, const BlendDesc &blendDesc); 206 207 void update(const BlendDesc &blendDesc); 208 209 // Use uint16_t instead of MTLPixelFormat to compact space 210 uint16_t pixelFormat : 16; 211 }; 212 213 struct RenderPipelineOutputDesc 214 { 215 bool operator==(const RenderPipelineOutputDesc &rhs) const; 216 217 void updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers); 218 219 RenderPipelineColorAttachmentDesc colorAttachments[kMaxRenderTargets]; 220 221 // Use uint16_t instead of MTLPixelFormat to compact space 222 uint16_t depthAttachmentPixelFormat : 16; 223 uint16_t stencilAttachmentPixelFormat : 16; 224 225 static_assert(kMaxRenderTargets <= 4, "kMaxRenderTargets must be <= 4"); 226 uint8_t numColorAttachments : 3; 227 uint8_t sampleCount : 5; 228 }; 229 230 // Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here: 231 #if !(TARGET_OS_OSX || TARGET_OS_MACCATALYST) && \ 232 (!defined(__IPHONE_12_0) || ANGLE_IOS_DEPLOY_TARGET < __IPHONE_12_0) 233 # define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 0 234 using PrimitiveTopologyClass = uint32_t; 235 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle = 0; 236 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = 0; 237 #else 238 # define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 1 239 using PrimitiveTopologyClass = MTLPrimitiveTopologyClass; 240 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle = 241 MTLPrimitiveTopologyClassTriangle; 242 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = MTLPrimitiveTopologyClassPoint; 243 #endif 244 245 enum class RenderPipelineRasterization : uint32_t 246 { 247 // This flag is used for vertex shader not writing any stage output (e.g gl_Position). 248 // This will disable fragment shader stage. This is useful for transform feedback ouput vertex 249 // shader. 250 Disabled, 251 252 // Fragment shader is enabled. 253 Enabled, 254 255 // This flag is for rasterization discard emulation when vertex shader still writes to stage 256 // output. Disabled flag cannot be used in this case since Metal doesn't allow that. The 257 // emulation would insert a code snippet to move gl_Position out of clip space's visible area to 258 // simulate the discard. 259 EmulatedDiscard, 260 261 EnumCount, 262 }; 263 264 template <typename T> 265 using RenderPipelineRasterStateMap = angle::PackedEnumMap<RenderPipelineRasterization, T>; 266 267 struct alignas(4) RenderPipelineDesc 268 { 269 RenderPipelineDesc(); 270 RenderPipelineDesc(const RenderPipelineDesc &src); 271 RenderPipelineDesc(RenderPipelineDesc &&src); 272 273 RenderPipelineDesc &operator=(const RenderPipelineDesc &src); 274 275 bool operator==(const RenderPipelineDesc &rhs) const; 276 size_t hash() const; 277 bool rasterizationEnabled() const; 278 279 VertexDesc vertexDescriptor; 280 281 RenderPipelineOutputDesc outputDescriptor; 282 283 // Use uint8_t instead of PrimitiveTopologyClass to compact space. 284 uint8_t inputPrimitiveTopology : 2; 285 286 bool alphaToCoverageEnabled : 1; 287 288 // These flags are for emulation and do not correspond to any flags in 289 // MTLRenderPipelineDescriptor descriptor. These flags should be used by 290 // RenderPipelineCacheSpecializeShaderFactory. 291 RenderPipelineRasterization rasterizationType : 2; 292 bool emulateCoverageMask : 1; 293 }; 294 295 struct alignas(4) ProvokingVertexComputePipelineDesc 296 { 297 ProvokingVertexComputePipelineDesc(); 298 ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &src); 299 ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &&src); 300 301 ProvokingVertexComputePipelineDesc &operator=(const ProvokingVertexComputePipelineDesc &src); 302 303 bool operator==(const ProvokingVertexComputePipelineDesc &rhs) const; 304 bool operator!=(const ProvokingVertexComputePipelineDesc &rhs) const; 305 size_t hash() const; 306 307 gl::PrimitiveMode primitiveMode; 308 uint8_t elementType; 309 bool primitiveRestartEnabled; 310 }; 311 312 struct RenderPassAttachmentDesc 313 { 314 RenderPassAttachmentDesc(); 315 // Set default values 316 void reset(); 317 318 bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const; 319 bool operator==(const RenderPassAttachmentDesc &other) const; 320 hasImplicitMSTextureRenderPassAttachmentDesc321 ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); } 322 323 TextureRef texture; 324 // Implicit multisample texture that will be rendered into and discarded at the end of 325 // a render pass. Its result will be resolved into normal texture above. 326 TextureRef implicitMSTexture; 327 MipmapNativeLevel level; 328 uint32_t sliceOrDepth; 329 330 // This attachment is blendable or not. 331 bool blendable; 332 MTLLoadAction loadAction; 333 MTLStoreAction storeAction; 334 MTLStoreActionOptions storeActionOptions; 335 }; 336 337 struct RenderPassColorAttachmentDesc : public RenderPassAttachmentDesc 338 { 339 inline bool operator==(const RenderPassColorAttachmentDesc &other) const 340 { 341 return RenderPassAttachmentDesc::operator==(other) && clearColor == other.clearColor; 342 } 343 inline bool operator!=(const RenderPassColorAttachmentDesc &other) const 344 { 345 return !(*this == other); 346 } 347 MTLClearColor clearColor = {0, 0, 0, 0}; 348 }; 349 350 struct RenderPassDepthAttachmentDesc : public RenderPassAttachmentDesc 351 { 352 inline bool operator==(const RenderPassDepthAttachmentDesc &other) const 353 { 354 return RenderPassAttachmentDesc::operator==(other) && clearDepth == other.clearDepth; 355 } 356 inline bool operator!=(const RenderPassDepthAttachmentDesc &other) const 357 { 358 return !(*this == other); 359 } 360 361 double clearDepth = 0; 362 }; 363 364 struct RenderPassStencilAttachmentDesc : public RenderPassAttachmentDesc 365 { 366 inline bool operator==(const RenderPassStencilAttachmentDesc &other) const 367 { 368 return RenderPassAttachmentDesc::operator==(other) && clearStencil == other.clearStencil; 369 } 370 inline bool operator!=(const RenderPassStencilAttachmentDesc &other) const 371 { 372 return !(*this == other); 373 } 374 uint32_t clearStencil = 0; 375 }; 376 377 // 378 // This is C++ equivalent of Objective-C MTLRenderPassDescriptor. 379 // We could use MTLRenderPassDescriptor directly, however, using C++ struct has benefits of fast 380 // copy, stack allocation, inlined comparing function, etc. 381 // 382 struct RenderPassDesc 383 { 384 RenderPassColorAttachmentDesc colorAttachments[kMaxRenderTargets]; 385 RenderPassDepthAttachmentDesc depthAttachment; 386 RenderPassStencilAttachmentDesc stencilAttachment; 387 388 void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const; 389 390 // This will populate the RenderPipelineOutputDesc with default blend state and 391 // MTLColorWriteMaskAll 392 void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const; 393 // This will populate the RenderPipelineOutputDesc with default blend state and the specified 394 // MTLColorWriteMask 395 void populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray, 396 RenderPipelineOutputDesc *outDesc) const; 397 // This will populate the RenderPipelineOutputDesc with the specified blend state 398 void populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray, 399 RenderPipelineOutputDesc *outDesc) const; 400 401 bool equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const; 402 bool operator==(const RenderPassDesc &other) const; 403 inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); } 404 405 uint32_t numColorAttachments = 0; 406 uint32_t sampleCount = 1; 407 }; 408 409 } // namespace mtl 410 } // namespace rx 411 412 namespace std 413 { 414 415 template <> 416 struct hash<rx::mtl::DepthStencilDesc> 417 { 418 size_t operator()(const rx::mtl::DepthStencilDesc &key) const { return key.hash(); } 419 }; 420 421 template <> 422 struct hash<rx::mtl::SamplerDesc> 423 { 424 size_t operator()(const rx::mtl::SamplerDesc &key) const { return key.hash(); } 425 }; 426 427 template <> 428 struct hash<rx::mtl::RenderPipelineDesc> 429 { 430 size_t operator()(const rx::mtl::RenderPipelineDesc &key) const { return key.hash(); } 431 }; 432 433 template <> 434 struct hash<rx::mtl::ProvokingVertexComputePipelineDesc> 435 { 436 size_t operator()(const rx::mtl::ProvokingVertexComputePipelineDesc &key) const 437 { 438 return key.hash(); 439 } 440 }; 441 } // namespace std 442 443 namespace rx 444 { 445 namespace mtl 446 { 447 448 // Abstract factory to create specialized vertex & fragment shaders based on RenderPipelineDesc. 449 class RenderPipelineCacheSpecializeShaderFactory 450 { 451 public: 452 virtual ~RenderPipelineCacheSpecializeShaderFactory() = default; 453 // Get specialized shader for the render pipeline cache. 454 virtual angle::Result getSpecializedShader(Context *context, 455 gl::ShaderType shaderType, 456 const RenderPipelineDesc &renderPipelineDesc, 457 id<MTLFunction> *shaderOut) = 0; 458 // Check whether specialized shaders is required for the specified RenderPipelineDesc. 459 // If not, the render pipeline cache will use the supplied non-specialized shaders. 460 virtual bool hasSpecializedShader(gl::ShaderType shaderType, 461 const RenderPipelineDesc &renderPipelineDesc) = 0; 462 }; 463 464 // Abstract factory to create specialized provoking vertex compute shaders based off of 465 // compute shader pipeline descs 466 467 class ProvokingVertexCacheSpecializeShaderFactory 468 { 469 public: 470 virtual ~ProvokingVertexCacheSpecializeShaderFactory() = default; 471 // Get specialized shader for the render pipeline cache. 472 virtual angle::Result getSpecializedShader( 473 Context *context, 474 gl::ShaderType shaderType, 475 const ProvokingVertexComputePipelineDesc &renderPipelineDesc, 476 id<MTLFunction> *shaderOut) = 0; 477 // Check whether specialized shaders is required for the specified RenderPipelineDesc. 478 // If not, the render pipeline cache will use the supplied non-specialized shaders. 479 virtual bool hasSpecializedShader( 480 gl::ShaderType shaderType, 481 const ProvokingVertexComputePipelineDesc &renderPipelineDesc) = 0; 482 }; 483 484 // Render pipeline state cache per shader program. 485 class RenderPipelineCache final : angle::NonCopyable 486 { 487 public: 488 RenderPipelineCache(); 489 RenderPipelineCache(RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory); 490 ~RenderPipelineCache(); 491 492 // Set non-specialized vertex/fragment shader to be used by render pipeline cache to create 493 // render pipeline state. If the internal 494 // RenderPipelineCacheSpecializeShaderFactory.hasSpecializedShader() returns false for a 495 // particular RenderPipelineDesc, the render pipeline cache will use the non-specialized 496 // shaders. 497 void setVertexShader(Context *context, id<MTLFunction> shader); 498 void setFragmentShader(Context *context, id<MTLFunction> shader); 499 500 // Get non-specialized shaders supplied via set*Shader(). 501 id<MTLFunction> getVertexShader() { return mVertexShader; } 502 id<MTLFunction> getFragmentShader() { return mFragmentShader; } 503 504 AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context, 505 const RenderPipelineDesc &desc); 506 507 void clear(); 508 509 protected: 510 // Non-specialized vertex shader 511 AutoObjCPtr<id<MTLFunction>> mVertexShader; 512 // Non-specialized fragment shader 513 AutoObjCPtr<id<MTLFunction>> mFragmentShader; 514 515 private: 516 void clearPipelineStates(); 517 void recreatePipelineStates(Context *context); 518 AutoObjCPtr<id<MTLRenderPipelineState>> insertRenderPipelineState( 519 Context *context, 520 const RenderPipelineDesc &desc, 521 bool insertDefaultAttribLayout); 522 AutoObjCPtr<id<MTLRenderPipelineState>> createRenderPipelineState( 523 Context *context, 524 const RenderPipelineDesc &desc, 525 bool insertDefaultAttribLayout); 526 527 bool hasDefaultAttribs(const RenderPipelineDesc &desc) const; 528 529 // One table with default attrib and one table without. 530 angle::HashMap<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>> 531 mRenderPipelineStates[2]; 532 RenderPipelineCacheSpecializeShaderFactory *mSpecializedShaderFactory; 533 }; 534 535 // render pipeline state cache per shader program 536 class ProvokingVertexComputePipelineCache final : angle::NonCopyable 537 { 538 public: 539 ProvokingVertexComputePipelineCache(); 540 ProvokingVertexComputePipelineCache( 541 ProvokingVertexCacheSpecializeShaderFactory *specializedShaderFactory); 542 ~ProvokingVertexComputePipelineCache(); 543 544 // Set non-specialized vertex/fragment shader to be used by render pipeline cache to create 545 // render pipeline state. If the internal 546 // RenderPipelineCacheSpecializeShaderFactory.hasSpecializedShader() returns false for a 547 // particular RenderPipelineDesc, the render pipeline cache will use the non-specialized 548 // shaders. 549 void setComputeShader(Context *context, id<MTLFunction> shader); 550 id<MTLFunction> getComputeShader() { return mComputeShader; } 551 552 AutoObjCPtr<id<MTLComputePipelineState>> getComputePipelineState( 553 ContextMtl *context, 554 const ProvokingVertexComputePipelineDesc &desc); 555 556 void clear(); 557 558 protected: 559 // Non-specialized compute shader 560 AutoObjCPtr<id<MTLFunction>> mComputeShader; 561 562 private: 563 void clearPipelineStates(); 564 void recreatePipelineStates(Context *context); 565 AutoObjCPtr<id<MTLComputePipelineState>> insertComputePipelineState( 566 Context *context, 567 const ProvokingVertexComputePipelineDesc &desc); 568 569 AutoObjCPtr<id<MTLComputePipelineState>> createComputePipelineState( 570 Context *context, 571 const ProvokingVertexComputePipelineDesc &desc); 572 573 bool hasDefaultAttribs(const RenderPipelineDesc &desc) const; 574 575 // One table with default attrib and one table without. 576 std::unordered_map<ProvokingVertexComputePipelineDesc, AutoObjCPtr<id<MTLComputePipelineState>>> 577 mComputePipelineStates; 578 ProvokingVertexCacheSpecializeShaderFactory *mSpecializedShaderFactory; 579 }; 580 581 class StateCache final : angle::NonCopyable 582 { 583 public: 584 StateCache(const angle::FeaturesMtl &features); 585 ~StateCache(); 586 587 // Null depth stencil state has depth/stecil read & write disabled. 588 inline AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(Context *context) 589 { 590 return getNullDepthStencilState(context->getMetalDevice()); 591 } 592 AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(id<MTLDevice> device); 593 AutoObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(id<MTLDevice> device, 594 const DepthStencilDesc &desc); 595 AutoObjCPtr<id<MTLSamplerState>> getSamplerState(id<MTLDevice> device, const SamplerDesc &desc); 596 // Null sampler state uses default SamplerDesc 597 AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(Context *context); 598 AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(id<MTLDevice> device); 599 void clear(); 600 601 private: 602 const angle::FeaturesMtl &mFeatures; 603 604 AutoObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil; 605 angle::HashMap<DepthStencilDesc, AutoObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates; 606 angle::HashMap<SamplerDesc, AutoObjCPtr<id<MTLSamplerState>>> mSamplerStates; 607 }; 608 609 } // namespace mtl 610 } // namespace rx 611 612 static inline bool operator==(const rx::mtl::VertexDesc &lhs, const rx::mtl::VertexDesc &rhs) 613 { 614 if (lhs.numAttribs != rhs.numAttribs || lhs.numBufferLayouts != rhs.numBufferLayouts) 615 { 616 return false; 617 } 618 for (uint8_t i = 0; i < lhs.numAttribs; ++i) 619 { 620 if (lhs.attributes[i] != rhs.attributes[i]) 621 { 622 return false; 623 } 624 } 625 for (uint8_t i = 0; i < lhs.numBufferLayouts; ++i) 626 { 627 if (lhs.layouts[i] != rhs.layouts[i]) 628 { 629 return false; 630 } 631 } 632 return true; 633 } 634 635 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs) 636 { 637 return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue && 638 lhs.alpha == rhs.alpha; 639 } 640 641 #endif /* LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ */ 642