• 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.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/FeaturesMtl.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 MTLStencilDescriptor *ToObjC(const StencilDesc &desc)
42{
43    MTLStencilDescriptor *objCDesc = [[MTLStencilDescriptor alloc] init];
44
45    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stencilFailureOperation);
46    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthFailureOperation);
47    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthStencilPassOperation);
48    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stencilCompareFunction);
49    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, readMask);
50    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, writeMask);
51
52    return [objCDesc ANGLE_MTL_AUTORELEASE];
53}
54
55MTLDepthStencilDescriptor *ToObjC(const DepthStencilDesc &desc)
56{
57    MTLDepthStencilDescriptor *objCDesc = [[MTLDepthStencilDescriptor alloc] init];
58
59    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, backFaceStencil);
60    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, frontFaceStencil);
61    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthCompareFunction);
62    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthWriteEnabled);
63
64    return [objCDesc ANGLE_MTL_AUTORELEASE];
65}
66
67MTLSamplerDescriptor *ToObjC(const SamplerDesc &desc)
68{
69    MTLSamplerDescriptor *objCDesc = [[MTLSamplerDescriptor alloc] init];
70
71    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rAddressMode);
72    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, sAddressMode);
73    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, tAddressMode);
74    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, minFilter);
75    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, magFilter);
76    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, mipFilter);
77    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, maxAnisotropy);
78    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, compareFunction);
79
80    return [objCDesc ANGLE_MTL_AUTORELEASE];
81}
82
83MTLVertexAttributeDescriptor *ToObjC(const VertexAttributeDesc &desc)
84{
85    MTLVertexAttributeDescriptor *objCDesc = [[MTLVertexAttributeDescriptor alloc] init];
86
87    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, format);
88    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, offset);
89    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, bufferIndex);
90
91    ASSERT(desc.bufferIndex >= kVboBindingIndexStart);
92
93    return [objCDesc ANGLE_MTL_AUTORELEASE];
94}
95
96MTLVertexBufferLayoutDescriptor *ToObjC(const VertexBufferLayoutDesc &desc)
97{
98    MTLVertexBufferLayoutDescriptor *objCDesc = [[MTLVertexBufferLayoutDescriptor alloc] init];
99
100    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stepFunction);
101    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stepRate);
102    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stride);
103
104    return [objCDesc ANGLE_MTL_AUTORELEASE];
105}
106
107MTLVertexDescriptor *ToObjC(const VertexDesc &desc)
108{
109    MTLVertexDescriptor *objCDesc = [[MTLVertexDescriptor alloc] init];
110    [objCDesc reset];
111
112    for (uint8_t i = 0; i < desc.numAttribs; ++i)
113    {
114        [objCDesc.attributes setObject:ToObjC(desc.attributes[i]) atIndexedSubscript:i];
115    }
116
117    for (uint8_t i = 0; i < desc.numBufferLayouts; ++i)
118    {
119        // Ignore if stepFunction is kVertexStepFunctionInvalid.
120        // If we don't set this slot, it will apparently be disabled by metal runtime.
121        if (desc.layouts[i].stepFunction != kVertexStepFunctionInvalid)
122        {
123            [objCDesc.layouts setObject:ToObjC(desc.layouts[i]) atIndexedSubscript:i];
124        }
125    }
126
127    return [objCDesc ANGLE_MTL_AUTORELEASE];
128}
129
130MTLRenderPipelineColorAttachmentDescriptor *ToObjC(const RenderPipelineColorAttachmentDesc &desc)
131{
132    MTLRenderPipelineColorAttachmentDescriptor *objCDesc =
133        [[MTLRenderPipelineColorAttachmentDescriptor alloc] init];
134
135    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, pixelFormat);
136    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, writeMask);
137    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaBlendOperation);
138    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rgbBlendOperation);
139    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, destinationAlphaBlendFactor);
140    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, destinationRGBBlendFactor);
141    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, sourceAlphaBlendFactor);
142    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, sourceRGBBlendFactor);
143    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, blendingEnabled);
144
145    return [objCDesc ANGLE_MTL_AUTORELEASE];
146}
147
148MTLRenderPipelineDescriptor *ToObjC(id<MTLFunction> vertexShader,
149                                    id<MTLFunction> fragmentShader,
150                                    const RenderPipelineDesc &desc)
151{
152    MTLRenderPipelineDescriptor *objCDesc = [[MTLRenderPipelineDescriptor alloc] init];
153    [objCDesc reset];
154
155    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, vertexDescriptor);
156
157    for (uint8_t i = 0; i < desc.outputDescriptor.numColorAttachments; ++i)
158    {
159        [objCDesc.colorAttachments setObject:ToObjC(desc.outputDescriptor.colorAttachments[i])
160                          atIndexedSubscript:i];
161    }
162    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, depthAttachmentPixelFormat);
163    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, stencilAttachmentPixelFormat);
164    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, sampleCount);
165
166#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE
167    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, inputPrimitiveTopology);
168#endif
169    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaToCoverageEnabled);
170
171    // rasterizationEnabled will be true for both EmulatedDiscard & Enabled.
172    objCDesc.rasterizationEnabled = desc.rasterizationEnabled();
173
174    objCDesc.vertexFunction   = vertexShader;
175    objCDesc.fragmentFunction = objCDesc.rasterizationEnabled ? fragmentShader : nil;
176
177    return [objCDesc ANGLE_MTL_AUTORELEASE];
178}
179
180id<MTLTexture> ToObjC(const TextureRef &texture)
181{
182    auto textureRef = texture;
183    return textureRef ? textureRef->get() : nil;
184}
185
186void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
187                                        MTLRenderPassAttachmentDescriptor *dst)
188{
189    const TextureRef &implicitMsTexture = src.implicitMSTexture;
190
191    if (implicitMsTexture)
192    {
193        dst.texture        = ToObjC(implicitMsTexture);
194        dst.level          = 0;
195        dst.slice          = 0;
196        dst.depthPlane     = 0;
197        dst.resolveTexture = ToObjC(src.texture);
198        dst.resolveLevel   = src.level.get();
199        if (dst.resolveTexture.textureType == MTLTextureType3D)
200        {
201            dst.resolveDepthPlane = src.sliceOrDepth;
202            dst.resolveSlice      = 0;
203        }
204        else
205        {
206            dst.resolveSlice      = src.sliceOrDepth;
207            dst.resolveDepthPlane = 0;
208        }
209    }
210    else
211    {
212        dst.texture = ToObjC(src.texture);
213        dst.level   = src.level.get();
214        if (dst.texture.textureType == MTLTextureType3D)
215        {
216            dst.depthPlane = src.sliceOrDepth;
217            dst.slice      = 0;
218        }
219        else
220        {
221            dst.slice      = src.sliceOrDepth;
222            dst.depthPlane = 0;
223        }
224        dst.resolveTexture    = nil;
225        dst.resolveLevel      = 0;
226        dst.resolveSlice      = 0;
227        dst.resolveDepthPlane = 0;
228    }
229
230    ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction);
231    ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction);
232    ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions);
233}
234
235void ToObjC(const RenderPassColorAttachmentDesc &desc,
236            MTLRenderPassColorAttachmentDescriptor *objCDesc)
237{
238    BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
239
240    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearColor);
241}
242
243void ToObjC(const RenderPassDepthAttachmentDesc &desc,
244            MTLRenderPassDepthAttachmentDescriptor *objCDesc)
245{
246    BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
247
248    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearDepth);
249}
250
251void ToObjC(const RenderPassStencilAttachmentDesc &desc,
252            MTLRenderPassStencilAttachmentDescriptor *objCDesc)
253{
254    BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
255
256    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil);
257}
258
259}  // namespace
260
261// StencilDesc implementation
262bool StencilDesc::operator==(const StencilDesc &rhs) const
263{
264    return ANGLE_PROP_EQ(*this, rhs, stencilFailureOperation) &&
265           ANGLE_PROP_EQ(*this, rhs, depthFailureOperation) &&
266           ANGLE_PROP_EQ(*this, rhs, depthStencilPassOperation) &&
267
268           ANGLE_PROP_EQ(*this, rhs, stencilCompareFunction) &&
269
270           ANGLE_PROP_EQ(*this, rhs, readMask) && ANGLE_PROP_EQ(*this, rhs, writeMask);
271}
272
273void StencilDesc::reset()
274{
275    stencilFailureOperation = depthFailureOperation = depthStencilPassOperation =
276        MTLStencilOperationKeep;
277
278    stencilCompareFunction = MTLCompareFunctionAlways;
279    readMask = writeMask = std::numeric_limits<uint32_t>::max() & mtl::kStencilMaskAll;
280}
281
282// DepthStencilDesc implementation
283DepthStencilDesc::DepthStencilDesc()
284{
285    memset(this, 0, sizeof(*this));
286}
287DepthStencilDesc::DepthStencilDesc(const DepthStencilDesc &src)
288{
289    memcpy(this, &src, sizeof(*this));
290}
291DepthStencilDesc::DepthStencilDesc(DepthStencilDesc &&src)
292{
293    memcpy(this, &src, sizeof(*this));
294}
295
296DepthStencilDesc &DepthStencilDesc::operator=(const DepthStencilDesc &src)
297{
298    memcpy(this, &src, sizeof(*this));
299    return *this;
300}
301
302bool DepthStencilDesc::operator==(const DepthStencilDesc &rhs) const
303{
304    return ANGLE_PROP_EQ(*this, rhs, backFaceStencil) &&
305           ANGLE_PROP_EQ(*this, rhs, frontFaceStencil) &&
306
307           ANGLE_PROP_EQ(*this, rhs, depthCompareFunction) &&
308
309           ANGLE_PROP_EQ(*this, rhs, depthWriteEnabled);
310}
311
312void DepthStencilDesc::reset()
313{
314    frontFaceStencil.reset();
315    backFaceStencil.reset();
316
317    depthCompareFunction = MTLCompareFunctionAlways;
318    depthWriteEnabled    = true;
319}
320
321void DepthStencilDesc::updateDepthTestEnabled(const gl::DepthStencilState &dsState)
322{
323    if (!dsState.depthTest)
324    {
325        depthCompareFunction = MTLCompareFunctionAlways;
326        depthWriteEnabled    = false;
327    }
328    else
329    {
330        updateDepthCompareFunc(dsState);
331        updateDepthWriteEnabled(dsState);
332    }
333}
334
335void DepthStencilDesc::updateDepthWriteEnabled(const gl::DepthStencilState &dsState)
336{
337    depthWriteEnabled = dsState.depthTest && dsState.depthMask;
338}
339
340void DepthStencilDesc::updateDepthCompareFunc(const gl::DepthStencilState &dsState)
341{
342    if (!dsState.depthTest)
343    {
344        return;
345    }
346    depthCompareFunction = GetCompareFunc(dsState.depthFunc);
347}
348
349void DepthStencilDesc::updateStencilTestEnabled(const gl::DepthStencilState &dsState)
350{
351    if (!dsState.stencilTest)
352    {
353        frontFaceStencil.stencilCompareFunction    = MTLCompareFunctionAlways;
354        frontFaceStencil.depthFailureOperation     = MTLStencilOperationKeep;
355        frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
356        frontFaceStencil.writeMask                 = 0;
357
358        backFaceStencil.stencilCompareFunction    = MTLCompareFunctionAlways;
359        backFaceStencil.depthFailureOperation     = MTLStencilOperationKeep;
360        backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
361        backFaceStencil.writeMask                 = 0;
362    }
363    else
364    {
365        updateStencilFrontFuncs(dsState);
366        updateStencilFrontOps(dsState);
367        updateStencilFrontWriteMask(dsState);
368        updateStencilBackFuncs(dsState);
369        updateStencilBackOps(dsState);
370        updateStencilBackWriteMask(dsState);
371    }
372}
373
374void DepthStencilDesc::updateStencilFrontOps(const gl::DepthStencilState &dsState)
375{
376    if (!dsState.stencilTest)
377    {
378        return;
379    }
380    frontFaceStencil.stencilFailureOperation   = GetStencilOp(dsState.stencilFail);
381    frontFaceStencil.depthFailureOperation     = GetStencilOp(dsState.stencilPassDepthFail);
382    frontFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilPassDepthPass);
383}
384
385void DepthStencilDesc::updateStencilBackOps(const gl::DepthStencilState &dsState)
386{
387    if (!dsState.stencilTest)
388    {
389        return;
390    }
391    backFaceStencil.stencilFailureOperation   = GetStencilOp(dsState.stencilBackFail);
392    backFaceStencil.depthFailureOperation     = GetStencilOp(dsState.stencilBackPassDepthFail);
393    backFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilBackPassDepthPass);
394}
395
396void DepthStencilDesc::updateStencilFrontFuncs(const gl::DepthStencilState &dsState)
397{
398    if (!dsState.stencilTest)
399    {
400        return;
401    }
402    frontFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilFunc);
403    frontFaceStencil.readMask               = dsState.stencilMask & mtl::kStencilMaskAll;
404}
405
406void DepthStencilDesc::updateStencilBackFuncs(const gl::DepthStencilState &dsState)
407{
408    if (!dsState.stencilTest)
409    {
410        return;
411    }
412    backFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilBackFunc);
413    backFaceStencil.readMask               = dsState.stencilBackMask & mtl::kStencilMaskAll;
414}
415
416void DepthStencilDesc::updateStencilFrontWriteMask(const gl::DepthStencilState &dsState)
417{
418    if (!dsState.stencilTest)
419    {
420        return;
421    }
422    frontFaceStencil.writeMask = dsState.stencilWritemask & mtl::kStencilMaskAll;
423}
424
425void DepthStencilDesc::updateStencilBackWriteMask(const gl::DepthStencilState &dsState)
426{
427    if (!dsState.stencilTest)
428    {
429        return;
430    }
431    backFaceStencil.writeMask = dsState.stencilBackWritemask & mtl::kStencilMaskAll;
432}
433
434size_t DepthStencilDesc::hash() const
435{
436    return angle::ComputeGenericHash(*this);
437}
438
439// SamplerDesc implementation
440SamplerDesc::SamplerDesc()
441{
442    memset(this, 0, sizeof(*this));
443}
444SamplerDesc::SamplerDesc(const SamplerDesc &src)
445{
446    memcpy(this, &src, sizeof(*this));
447}
448SamplerDesc::SamplerDesc(SamplerDesc &&src)
449{
450    memcpy(this, &src, sizeof(*this));
451}
452
453SamplerDesc::SamplerDesc(const gl::SamplerState &glState) : SamplerDesc()
454{
455    rAddressMode = GetSamplerAddressMode(glState.getWrapR());
456    sAddressMode = GetSamplerAddressMode(glState.getWrapS());
457    tAddressMode = GetSamplerAddressMode(glState.getWrapT());
458
459    minFilter = GetFilter(glState.getMinFilter());
460    magFilter = GetFilter(glState.getMagFilter());
461    mipFilter = GetMipmapFilter(glState.getMinFilter());
462
463    maxAnisotropy = static_cast<uint32_t>(glState.getMaxAnisotropy());
464
465    compareFunction = GetCompareFunc(glState.getCompareFunc());
466}
467
468SamplerDesc &SamplerDesc::operator=(const SamplerDesc &src)
469{
470    memcpy(this, &src, sizeof(*this));
471    return *this;
472}
473
474void SamplerDesc::reset()
475{
476    rAddressMode = MTLSamplerAddressModeClampToEdge;
477    sAddressMode = MTLSamplerAddressModeClampToEdge;
478    tAddressMode = MTLSamplerAddressModeClampToEdge;
479
480    minFilter = MTLSamplerMinMagFilterNearest;
481    magFilter = MTLSamplerMinMagFilterNearest;
482    mipFilter = MTLSamplerMipFilterNearest;
483
484    maxAnisotropy = 1;
485
486    compareFunction = MTLCompareFunctionNever;
487}
488
489bool SamplerDesc::operator==(const SamplerDesc &rhs) const
490{
491    return ANGLE_PROP_EQ(*this, rhs, rAddressMode) && ANGLE_PROP_EQ(*this, rhs, sAddressMode) &&
492           ANGLE_PROP_EQ(*this, rhs, tAddressMode) &&
493
494           ANGLE_PROP_EQ(*this, rhs, minFilter) && ANGLE_PROP_EQ(*this, rhs, magFilter) &&
495           ANGLE_PROP_EQ(*this, rhs, mipFilter) &&
496
497           ANGLE_PROP_EQ(*this, rhs, maxAnisotropy) &&
498
499           ANGLE_PROP_EQ(*this, rhs, compareFunction);
500}
501
502size_t SamplerDesc::hash() const
503{
504    return angle::ComputeGenericHash(*this);
505}
506
507// BlendDesc implementation
508bool BlendDesc::operator==(const BlendDesc &rhs) const
509{
510    return ANGLE_PROP_EQ(*this, rhs, writeMask) &&
511
512           ANGLE_PROP_EQ(*this, rhs, alphaBlendOperation) &&
513           ANGLE_PROP_EQ(*this, rhs, rgbBlendOperation) &&
514
515           ANGLE_PROP_EQ(*this, rhs, destinationAlphaBlendFactor) &&
516           ANGLE_PROP_EQ(*this, rhs, destinationRGBBlendFactor) &&
517           ANGLE_PROP_EQ(*this, rhs, sourceAlphaBlendFactor) &&
518           ANGLE_PROP_EQ(*this, rhs, sourceRGBBlendFactor) &&
519
520           ANGLE_PROP_EQ(*this, rhs, blendingEnabled);
521}
522
523void BlendDesc::reset()
524{
525    reset(MTLColorWriteMaskAll);
526}
527
528void BlendDesc::reset(MTLColorWriteMask _writeMask)
529{
530    writeMask = _writeMask;
531
532    blendingEnabled     = false;
533    alphaBlendOperation = rgbBlendOperation = MTLBlendOperationAdd;
534
535    destinationAlphaBlendFactor = destinationRGBBlendFactor = MTLBlendFactorZero;
536    sourceAlphaBlendFactor = sourceRGBBlendFactor = MTLBlendFactorOne;
537}
538
539void BlendDesc::updateWriteMask(const uint8_t angleMask)
540{
541    ASSERT(angleMask == (angleMask & 0xF));
542
543// ANGLE's packed color mask is abgr (matches Vulkan & D3D11), while Metal expects rgba.
544#if defined(__aarch64__)
545    // ARM64 can reverse bits in a single instruction
546    writeMask = __builtin_bitreverse8(angleMask) >> 4;
547#else
548    /* On other architectures, Clang generates a polyfill that uses more
549       instructions than the following expression optimized for a 4-bit value.
550
551       (abgr * 0x41) & 0x14A:
552        .......abgr +
553        .abgr...... &
554        00101001010 =
555        ..b.r..a.g.
556
557       (b.r..a.g.) * 0x111:
558                b.r..a.g. +
559            b.r..a.g..... +
560        b.r..a.g......... =
561        b.r.bargbarg.a.g.
562              ^^^^
563    */
564    writeMask = ((((angleMask * 0x41) & 0x14A) * 0x111) >> 7) & 0xF;
565#endif
566}
567
568// RenderPipelineColorAttachmentDesc implementation
569bool RenderPipelineColorAttachmentDesc::operator==(
570    const RenderPipelineColorAttachmentDesc &rhs) const
571{
572    if (!BlendDesc::operator==(rhs))
573    {
574        return false;
575    }
576    return ANGLE_PROP_EQ(*this, rhs, pixelFormat);
577}
578
579void RenderPipelineColorAttachmentDesc::reset()
580{
581    reset(MTLPixelFormatInvalid);
582}
583
584void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format)
585{
586    reset(format, MTLColorWriteMaskAll);
587}
588
589void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, MTLColorWriteMask _writeMask)
590{
591    this->pixelFormat = format;
592
593    BlendDesc::reset(_writeMask);
594}
595
596void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, const BlendDesc &blendDesc)
597{
598    this->pixelFormat = format;
599
600    BlendDesc::operator=(blendDesc);
601}
602
603void RenderPipelineColorAttachmentDesc::update(const BlendDesc &blendDesc)
604{
605    BlendDesc::operator=(blendDesc);
606}
607
608// RenderPipelineOutputDesc implementation
609bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) const
610{
611    if (numColorAttachments != rhs.numColorAttachments)
612    {
613        return false;
614    }
615
616    for (uint8_t i = 0; i < numColorAttachments; ++i)
617    {
618        if (colorAttachments[i] != rhs.colorAttachments[i])
619        {
620            return false;
621        }
622    }
623
624    return ANGLE_PROP_EQ(*this, rhs, depthAttachmentPixelFormat) &&
625           ANGLE_PROP_EQ(*this, rhs, stencilAttachmentPixelFormat);
626}
627
628void RenderPipelineOutputDesc::updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers)
629{
630    for (uint32_t colorIndex = 0; colorIndex < this->numColorAttachments; ++colorIndex)
631    {
632        if (!enabledBuffers.test(colorIndex))
633        {
634            this->colorAttachments[colorIndex].writeMask = MTLColorWriteMaskNone;
635        }
636    }
637}
638
639// RenderPipelineDesc implementation
640RenderPipelineDesc::RenderPipelineDesc()
641{
642    memset(this, 0, sizeof(*this));
643    outputDescriptor.sampleCount = 1;
644    rasterizationType            = RenderPipelineRasterization::Enabled;
645}
646
647RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src)
648{
649    memcpy(this, &src, sizeof(*this));
650}
651
652RenderPipelineDesc::RenderPipelineDesc(RenderPipelineDesc &&src)
653{
654    memcpy(this, &src, sizeof(*this));
655}
656
657RenderPipelineDesc &RenderPipelineDesc::operator=(const RenderPipelineDesc &src)
658{
659    memcpy(this, &src, sizeof(*this));
660    return *this;
661}
662
663bool RenderPipelineDesc::operator==(const RenderPipelineDesc &rhs) const
664{
665    // NOTE(hqle): Use a faster way to compare, i.e take into account
666    // the number of active vertex attributes & render targets.
667    // If that way is used, hash() method must be changed also.
668    return memcmp(this, &rhs, sizeof(*this)) == 0;
669}
670
671size_t RenderPipelineDesc::hash() const
672{
673    return angle::ComputeGenericHash(*this);
674}
675
676bool RenderPipelineDesc::rasterizationEnabled() const
677{
678    return rasterizationType != RenderPipelineRasterization::Disabled;
679}
680
681// RenderPassDesc implementation
682RenderPassAttachmentDesc::RenderPassAttachmentDesc()
683{
684    reset();
685}
686
687void RenderPassAttachmentDesc::reset()
688{
689    texture.reset();
690    implicitMSTexture.reset();
691    level              = mtl::kZeroNativeMipLevel;
692    sliceOrDepth       = 0;
693    blendable          = false;
694    loadAction         = MTLLoadActionLoad;
695    storeAction        = MTLStoreActionStore;
696    storeActionOptions = MTLStoreActionOptionNone;
697}
698
699bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions(
700    const RenderPassAttachmentDesc &other) const
701{
702    return texture == other.texture && implicitMSTexture == other.implicitMSTexture &&
703           level == other.level && sliceOrDepth == other.sliceOrDepth &&
704           blendable == other.blendable;
705}
706
707bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const
708{
709    if (!equalIgnoreLoadStoreOptions(other))
710    {
711        return false;
712    }
713
714    return loadAction == other.loadAction && storeAction == other.storeAction &&
715           storeActionOptions == other.storeActionOptions;
716}
717
718void RenderPassDesc::populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const
719{
720    WriteMaskArray writeMaskArray;
721    writeMaskArray.fill(MTLColorWriteMaskAll);
722    populateRenderPipelineOutputDesc(writeMaskArray, outDesc);
723}
724
725void RenderPassDesc::populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray,
726                                                      RenderPipelineOutputDesc *outDesc) const
727{
728    // Default blend state with replaced color write masks.
729    BlendDescArray blendDescArray;
730    for (size_t i = 0; i < blendDescArray.size(); i++)
731    {
732        blendDescArray[i].reset(writeMaskArray[i]);
733    }
734    populateRenderPipelineOutputDesc(blendDescArray, outDesc);
735}
736
737void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray,
738                                                      RenderPipelineOutputDesc *outDesc) const
739{
740    RenderPipelineOutputDesc &outputDescriptor = *outDesc;
741    outputDescriptor.numColorAttachments       = this->numColorAttachments;
742    outputDescriptor.sampleCount               = this->sampleCount;
743    for (uint32_t i = 0; i < this->numColorAttachments; ++i)
744    {
745        auto &renderPassColorAttachment = this->colorAttachments[i];
746        auto texture                    = renderPassColorAttachment.texture;
747
748        if (texture)
749        {
750            if (renderPassColorAttachment.blendable)
751            {
752                // Copy parameters from blend state
753                outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(),
754                                                           blendDescArray[i]);
755            }
756            else
757            {
758                // Disable blending if the attachment's render target doesn't support blending.
759                // Force default blending state to reduce the number of unique states.
760                outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(),
761                                                           blendDescArray[i].writeMask);
762            }
763
764            // Combine the masks. This is useful when the texture is not supposed to have alpha
765            // channel such as GL_RGB8, however, Metal doesn't natively support 24 bit RGB, so
766            // we need to use RGBA texture, and then disable alpha write to this texture
767            outputDescriptor.colorAttachments[i].writeMask &= texture->getColorWritableMask();
768        }
769        else
770        {
771
772            outputDescriptor.colorAttachments[i].blendingEnabled = false;
773            outputDescriptor.colorAttachments[i].pixelFormat     = MTLPixelFormatInvalid;
774        }
775    }
776
777    // Reset the unused output slots to ensure consistent hash value
778    for (uint32_t i = this->numColorAttachments; i < kMaxRenderTargets; ++i)
779    {
780        outputDescriptor.colorAttachments[i].reset();
781    }
782
783    auto depthTexture = this->depthAttachment.texture;
784    outputDescriptor.depthAttachmentPixelFormat =
785        depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid;
786
787    auto stencilTexture = this->stencilAttachment.texture;
788    outputDescriptor.stencilAttachmentPixelFormat =
789        stencilTexture ? stencilTexture->pixelFormat() : MTLPixelFormatInvalid;
790}
791
792bool RenderPassDesc::equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const
793{
794    if (numColorAttachments != other.numColorAttachments)
795    {
796        return false;
797    }
798
799    for (uint32_t i = 0; i < numColorAttachments; ++i)
800    {
801        auto &renderPassColorAttachment = colorAttachments[i];
802        auto &otherRPAttachment         = other.colorAttachments[i];
803        if (!renderPassColorAttachment.equalIgnoreLoadStoreOptions(otherRPAttachment))
804        {
805            return false;
806        }
807    }
808
809    return depthAttachment.equalIgnoreLoadStoreOptions(other.depthAttachment) &&
810           stencilAttachment.equalIgnoreLoadStoreOptions(other.stencilAttachment);
811}
812
813bool RenderPassDesc::operator==(const RenderPassDesc &other) const
814{
815    if (numColorAttachments != other.numColorAttachments)
816    {
817        return false;
818    }
819
820    for (uint32_t i = 0; i < numColorAttachments; ++i)
821    {
822        auto &renderPassColorAttachment = colorAttachments[i];
823        auto &otherRPAttachment         = other.colorAttachments[i];
824        if (renderPassColorAttachment != (otherRPAttachment))
825        {
826            return false;
827        }
828    }
829
830    return depthAttachment == other.depthAttachment && stencilAttachment == other.stencilAttachment;
831}
832
833// Convert to Metal object
834void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const
835{
836    ANGLE_MTL_OBJC_SCOPE
837    {
838        for (uint32_t i = 0; i < numColorAttachments; ++i)
839        {
840            ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]);
841        }
842        for (uint32_t i = numColorAttachments; i < kMaxRenderTargets; ++i)
843        {
844            // Inactive render target
845            objCDesc.colorAttachments[i].texture     = nil;
846            objCDesc.colorAttachments[i].level       = 0;
847            objCDesc.colorAttachments[i].slice       = 0;
848            objCDesc.colorAttachments[i].depthPlane  = 0;
849            objCDesc.colorAttachments[i].loadAction  = MTLLoadActionDontCare;
850            objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare;
851        }
852
853        ToObjC(depthAttachment, objCDesc.depthAttachment);
854        ToObjC(stencilAttachment, objCDesc.stencilAttachment);
855    }
856}
857
858// RenderPipelineCache implementation
859RenderPipelineCache::RenderPipelineCache() : RenderPipelineCache(nullptr) {}
860
861RenderPipelineCache::RenderPipelineCache(
862    RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory)
863    : mSpecializedShaderFactory(specializedShaderFactory)
864{}
865
866RenderPipelineCache::~RenderPipelineCache() {}
867
868void RenderPipelineCache::setVertexShader(ContextMtl *context, id<MTLFunction> shader)
869{
870    mVertexShader.retainAssign(shader);
871
872    if (!shader)
873    {
874        clearPipelineStates();
875        return;
876    }
877
878    recreatePipelineStates(context);
879}
880
881void RenderPipelineCache::setFragmentShader(ContextMtl *context, id<MTLFunction> shader)
882{
883    mFragmentShader.retainAssign(shader);
884
885    if (!shader)
886    {
887        clearPipelineStates();
888        return;
889    }
890
891    recreatePipelineStates(context);
892}
893
894bool RenderPipelineCache::hasDefaultAttribs(const RenderPipelineDesc &rpdesc) const
895{
896    const VertexDesc &desc = rpdesc.vertexDescriptor;
897    for (uint8_t i = 0; i < desc.numAttribs; ++i)
898    {
899        if (desc.attributes[i].bufferIndex == kDefaultAttribsBindingIndex)
900        {
901            return true;
902        }
903    }
904
905    return false;
906}
907
908AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::getRenderPipelineState(
909    ContextMtl *context,
910    const RenderPipelineDesc &desc)
911{
912    auto insertDefaultAttribLayout = hasDefaultAttribs(desc);
913    int tableIdx                   = insertDefaultAttribLayout ? 1 : 0;
914    auto &table                    = mRenderPipelineStates[tableIdx];
915    auto ite                       = table.find(desc);
916    if (ite == table.end())
917    {
918        return insertRenderPipelineState(context, desc, insertDefaultAttribLayout);
919    }
920
921    return ite->second;
922}
923
924AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelineState(
925    ContextMtl *context,
926    const RenderPipelineDesc &desc,
927    bool insertDefaultAttribLayout)
928{
929    AutoObjCPtr<id<MTLRenderPipelineState>> newState =
930        createRenderPipelineState(context, desc, insertDefaultAttribLayout);
931    if (!newState)
932    {
933        return nil;
934    }
935
936    int tableIdx = insertDefaultAttribLayout ? 1 : 0;
937    auto re      = mRenderPipelineStates[tableIdx].insert(std::make_pair(desc, newState));
938    if (!re.second)
939    {
940        return nil;
941    }
942
943    return re.first->second;
944}
945
946AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelineState(
947    ContextMtl *context,
948    const RenderPipelineDesc &originalDesc,
949    bool insertDefaultAttribLayout)
950{
951    ANGLE_MTL_OBJC_SCOPE
952    {
953        // Disable coverage if the render pipeline's sample count is only 1.
954        RenderPipelineDesc desc = originalDesc;
955        if (desc.outputDescriptor.sampleCount == 1)
956        {
957            // Disable sample coverage if the output is not multisample
958            desc.emulateCoverageMask    = false;
959            desc.alphaToCoverageEnabled = false;
960        }
961
962        // Choose shader variant
963        id<MTLFunction> vertShader = nil;
964        id<MTLFunction> fragShader = nil;
965        if (mSpecializedShaderFactory &&
966            mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Vertex, desc))
967        {
968            if (IsError(mSpecializedShaderFactory->getSpecializedShader(
969                    context, gl::ShaderType::Vertex, desc, &vertShader)))
970            {
971                return nil;
972            }
973        }
974        else
975        {
976            // Non-specialized version
977            vertShader = mVertexShader;
978        }
979
980        if (mSpecializedShaderFactory &&
981            mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Fragment, desc))
982        {
983            if (IsError(mSpecializedShaderFactory->getSpecializedShader(
984                    context, gl::ShaderType::Fragment, desc, &fragShader)))
985            {
986                return nil;
987            }
988        }
989        else
990        {
991            // Non-specialized version
992            fragShader = mFragmentShader;
993        }
994
995        if (!vertShader)
996        {
997            // Render pipeline without vertex shader is invalid.
998            context->handleError(GL_INVALID_OPERATION, __FILE__, ANGLE_FUNCTION, __LINE__);
999            return nil;
1000        }
1001
1002        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
1003
1004        // Convert to Objective-C desc:
1005        AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc = ToObjC(vertShader, fragShader, desc);
1006
1007        // Validate Render Pipeline State:
1008        if (DeviceHasMaximumRenderTargetSize(metalDevice))
1009        {
1010            // TODO: Is the use of NSUInteger in 32 bit systems ok without any overflow checking?
1011            NSUInteger maxSize = GetMaxRenderTargetSizeForDeviceInBytes(metalDevice);
1012            NSUInteger renderTargetSize =
1013                ComputeTotalSizeUsedForMTLRenderPipelineDescriptor(objCDesc, context, metalDevice);
1014            if (renderTargetSize > maxSize)
1015            {
1016                NSString *errorString = [NSString
1017                    stringWithFormat:@"This set of render targets requires %lu bytes of "
1018                                     @"pixel storage. This device supports %lu bytes.",
1019                                     (unsigned long)renderTargetSize, (unsigned long)maxSize];
1020                NSError *err          = [NSError errorWithDomain:@"MTLValidationError"
1021                                                   code:-1
1022                                               userInfo:@{NSLocalizedDescriptionKey : errorString}];
1023                context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__);
1024                return nil;
1025            }
1026        }
1027
1028        // Special attribute slot for default attribute
1029        if (insertDefaultAttribLayout)
1030        {
1031            MTLVertexBufferLayoutDescriptor *defaultAttribLayoutObjCDesc =
1032                [[MTLVertexBufferLayoutDescriptor alloc] init];
1033            defaultAttribLayoutObjCDesc.stepFunction = MTLVertexStepFunctionConstant;
1034            defaultAttribLayoutObjCDesc.stepRate     = 0;
1035            defaultAttribLayoutObjCDesc.stride       = kDefaultAttributeSize * kMaxVertexAttribs;
1036
1037            [objCDesc.get().vertexDescriptor.layouts
1038                         setObject:[defaultAttribLayoutObjCDesc ANGLE_MTL_AUTORELEASE]
1039                atIndexedSubscript:kDefaultAttribsBindingIndex];
1040        }
1041        // Create pipeline state
1042        NSError *err  = nil;
1043        auto newState = metalDevice.newRenderPipelineStateWithDescriptor(objCDesc, &err);
1044        if (err)
1045        {
1046            context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__);
1047            return nil;
1048        }
1049
1050        return newState;
1051    }
1052}
1053
1054void RenderPipelineCache::recreatePipelineStates(ContextMtl *context)
1055{
1056    for (int hasDefaultAttrib = 0; hasDefaultAttrib <= 1; ++hasDefaultAttrib)
1057    {
1058        for (auto &ite : mRenderPipelineStates[hasDefaultAttrib])
1059        {
1060            if (ite.second == nil)
1061            {
1062                continue;
1063            }
1064
1065            ite.second = createRenderPipelineState(context, ite.first, hasDefaultAttrib);
1066        }
1067    }
1068}
1069
1070void RenderPipelineCache::clear()
1071{
1072    mVertexShader   = nil;
1073    mFragmentShader = nil;
1074    clearPipelineStates();
1075}
1076
1077void RenderPipelineCache::clearPipelineStates()
1078{
1079    mRenderPipelineStates[0].clear();
1080    mRenderPipelineStates[1].clear();
1081}
1082
1083// ProvokingVertexPipelineDesc
1084ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc()
1085{
1086    memset(this, 0, sizeof(*this));
1087}
1088ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc(
1089    const ProvokingVertexComputePipelineDesc &src)
1090{
1091    memcpy(this, &src, sizeof(*this));
1092}
1093ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc(
1094    const ProvokingVertexComputePipelineDesc &&src)
1095{
1096    memcpy(this, &src, sizeof(*this));
1097}
1098ProvokingVertexComputePipelineDesc &ProvokingVertexComputePipelineDesc::operator=(
1099    const ProvokingVertexComputePipelineDesc &src)
1100{
1101    memcpy(this, &src, sizeof(*this));
1102    return *this;
1103}
1104bool ProvokingVertexComputePipelineDesc::operator==(
1105    const ProvokingVertexComputePipelineDesc &rhs) const
1106{
1107    return memcmp(this, &rhs, sizeof(*this)) == 0;
1108}
1109bool ProvokingVertexComputePipelineDesc::operator!=(
1110    const ProvokingVertexComputePipelineDesc &rhs) const
1111{
1112    return !(*this == rhs);
1113}
1114size_t ProvokingVertexComputePipelineDesc::hash() const
1115{
1116    return angle::ComputeGenericHash(*this);
1117}
1118
1119ProvokingVertexComputePipelineCache::ProvokingVertexComputePipelineCache() : mComputeShader(nullptr)
1120{}
1121
1122ProvokingVertexComputePipelineCache::ProvokingVertexComputePipelineCache(
1123    ProvokingVertexCacheSpecializeShaderFactory *specializedShaderFactory)
1124    : mComputeShader(nullptr), mSpecializedShaderFactory(specializedShaderFactory)
1125{}
1126
1127void ProvokingVertexComputePipelineCache::setComputeShader(ContextMtl *context,
1128                                                           id<MTLFunction> shader)
1129{
1130    mComputeShader.retainAssign(shader);
1131    if (!shader)
1132    {
1133        clearPipelineStates();
1134        return;
1135    }
1136
1137    recreatePipelineStates(context);
1138}
1139
1140void ProvokingVertexComputePipelineCache::clearPipelineStates()
1141{
1142    mComputePipelineStates.clear();
1143}
1144
1145void ProvokingVertexComputePipelineCache::clear()
1146{
1147    clearPipelineStates();
1148}
1149
1150AutoObjCPtr<id<MTLComputePipelineState>>
1151ProvokingVertexComputePipelineCache::getComputePipelineState(
1152    ContextMtl *context,
1153    const ProvokingVertexComputePipelineDesc &desc)
1154{
1155    auto &table = mComputePipelineStates;
1156    auto ite    = table.find(desc);
1157    if (ite == table.end())
1158    {
1159        return insertComputePipelineState(context, desc);
1160    }
1161
1162    return ite->second;
1163}
1164
1165AutoObjCPtr<id<MTLComputePipelineState>>
1166ProvokingVertexComputePipelineCache::insertComputePipelineState(
1167    ContextMtl *context,
1168    const ProvokingVertexComputePipelineDesc &desc)
1169{
1170    AutoObjCPtr<id<MTLComputePipelineState>> newState = createComputePipelineState(context, desc);
1171
1172    auto re = mComputePipelineStates.insert(std::make_pair(desc, newState));
1173    if (!re.second)
1174    {
1175        return nil;
1176    }
1177
1178    return re.first->second;
1179}
1180
1181void ProvokingVertexComputePipelineCache::recreatePipelineStates(ContextMtl *context)
1182{
1183
1184    for (auto &ite : mComputePipelineStates)
1185    {
1186        if (ite.second == nil)
1187        {
1188            continue;
1189        }
1190
1191        ite.second = createComputePipelineState(context, ite.first);
1192    }
1193}
1194
1195AutoObjCPtr<id<MTLComputePipelineState>>
1196ProvokingVertexComputePipelineCache::createComputePipelineState(
1197    ContextMtl *context,
1198    const ProvokingVertexComputePipelineDesc &originalDesc)
1199{
1200    ANGLE_MTL_OBJC_SCOPE
1201    {
1202        // Disable coverage if the render pipeline's sample count is only 1.
1203        ProvokingVertexComputePipelineDesc desc = originalDesc;
1204
1205        id<MTLFunction> computeFunction = nil;
1206        // Special case for transform feedback shader, we've already created it! See
1207        // getTransformFeedbackRenderPipeline
1208        if (mSpecializedShaderFactory &&
1209            mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Compute, desc))
1210        {
1211            if (IsError(mSpecializedShaderFactory->getSpecializedShader(
1212                    context, gl::ShaderType::Compute, desc, &computeFunction)))
1213            {
1214                return nil;
1215            }
1216        }
1217        else
1218        {
1219            // Non-specialized version
1220            computeFunction = mComputeShader;
1221        }
1222
1223        if (!computeFunction)
1224        {
1225            // Render pipeline without vertex shader is invalid.
1226            context->handleError(GL_INVALID_OPERATION, __FILE__, ANGLE_FUNCTION, __LINE__);
1227            return nil;
1228        }
1229
1230        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
1231
1232        // Convert to Objective-C desc:
1233        NSError *err  = nil;
1234        auto newState = metalDevice.newComputePipelineStateWithFunction(computeFunction, &err);
1235        if (err)
1236        {
1237            context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__);
1238            return nil;
1239        }
1240
1241        return newState;
1242    }
1243}
1244
1245ProvokingVertexComputePipelineCache::~ProvokingVertexComputePipelineCache() {}
1246
1247// StateCache implementation
1248StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {}
1249
1250StateCache::~StateCache() {}
1251
1252AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getNullDepthStencilState(
1253    const mtl::ContextDevice &device)
1254{
1255    if (!mNullDepthStencilState)
1256    {
1257        DepthStencilDesc desc;
1258        desc.reset();
1259        ASSERT(desc.frontFaceStencil.stencilCompareFunction == MTLCompareFunctionAlways);
1260        desc.depthWriteEnabled = false;
1261        mNullDepthStencilState = getDepthStencilState(device, desc);
1262    }
1263    return mNullDepthStencilState;
1264}
1265
1266AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getDepthStencilState(
1267    const mtl::ContextDevice &device,
1268    const DepthStencilDesc &desc)
1269{
1270    ANGLE_MTL_OBJC_SCOPE
1271    {
1272        auto ite = mDepthStencilStates.find(desc);
1273        if (ite == mDepthStencilStates.end())
1274        {
1275            auto re = mDepthStencilStates.insert(
1276                std::make_pair(desc, device.newDepthStencilStateWithDescriptor(ToObjC(desc))));
1277            if (!re.second)
1278            {
1279                return nil;
1280            }
1281
1282            ite = re.first;
1283        }
1284
1285        return ite->second;
1286    }
1287}
1288
1289AutoObjCPtr<id<MTLSamplerState>> StateCache::getSamplerState(const mtl::ContextDevice &device,
1290                                                             const SamplerDesc &desc)
1291{
1292    ANGLE_MTL_OBJC_SCOPE
1293    {
1294        auto ite = mSamplerStates.find(desc);
1295        if (ite == mSamplerStates.end())
1296        {
1297            AutoObjCObj<MTLSamplerDescriptor> objCDesc = ToObjC(desc);
1298            if (!mFeatures.allowRuntimeSamplerCompareMode.enabled)
1299            {
1300                // Runtime sampler compare mode is not supported, fallback to never.
1301                objCDesc.get().compareFunction = MTLCompareFunctionNever;
1302            }
1303            auto re = mSamplerStates.insert(
1304                std::make_pair(desc, device.newSamplerStateWithDescriptor(objCDesc)));
1305            if (!re.second)
1306                return nil;
1307
1308            ite = re.first;
1309        }
1310
1311        return ite->second;
1312    }
1313}
1314
1315AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(ContextMtl *context)
1316{
1317    return getNullSamplerState(context->getMetalDevice());
1318}
1319
1320AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(const mtl::ContextDevice &device)
1321{
1322    SamplerDesc desc;
1323    desc.reset();
1324
1325    return getSamplerState(device, desc);
1326}
1327
1328void StateCache::clear()
1329{
1330    mNullDepthStencilState = nil;
1331    mDepthStencilStates.clear();
1332    mSamplerStates.clear();
1333}
1334
1335}  // namespace mtl
1336}  // namespace rx
1337