• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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