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