• 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/autogen/FeaturesMtl_autogen.h"
21
22#define ANGLE_OBJC_CP_PROPERTY(DST, SRC, PROPERTY) \
23    (DST).PROPERTY = static_cast<__typeof__((DST).PROPERTY)>(ToObjC((SRC).PROPERTY))
24
25#define ANGLE_PROP_EQ(LHS, RHS, PROP) ((LHS).PROP == (RHS).PROP)
26
27namespace rx
28{
29namespace mtl
30{
31
32namespace
33{
34
35template <class T>
36inline T ToObjC(const T p)
37{
38    return p;
39}
40
41inline angle::ObjCPtr<MTLStencilDescriptor> ToObjC(const StencilDesc &desc)
42{
43    auto objCDesc = angle::adoptObjCPtr([[MTLStencilDescriptor alloc] init]);
44    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stencilFailureOperation);
45    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthFailureOperation);
46    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthStencilPassOperation);
47    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stencilCompareFunction);
48    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, readMask);
49    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, writeMask);
50    return objCDesc;
51}
52
53inline angle::ObjCPtr<MTLDepthStencilDescriptor> ToObjC(const DepthStencilDesc &desc)
54{
55    auto objCDesc = angle::adoptObjCPtr([[MTLDepthStencilDescriptor alloc] init]);
56    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, backFaceStencil);
57    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, frontFaceStencil);
58    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthCompareFunction);
59    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, depthWriteEnabled);
60    return objCDesc;
61}
62
63inline angle::ObjCPtr<MTLSamplerDescriptor> ToObjC(const SamplerDesc &desc)
64{
65    auto objCDesc = angle::adoptObjCPtr([[MTLSamplerDescriptor alloc] init]);
66    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, rAddressMode);
67    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, sAddressMode);
68    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, tAddressMode);
69    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, minFilter);
70    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, magFilter);
71    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, mipFilter);
72    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, maxAnisotropy);
73    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, compareFunction);
74    return objCDesc;
75}
76
77inline angle::ObjCPtr<MTLVertexAttributeDescriptor> ToObjC(const VertexAttributeDesc &desc)
78{
79    auto objCDesc = angle::adoptObjCPtr([[MTLVertexAttributeDescriptor alloc] init]);
80    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, format);
81    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, offset);
82    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, bufferIndex);
83    ASSERT(desc.bufferIndex >= kVboBindingIndexStart);
84    return objCDesc;
85}
86
87inline angle::ObjCPtr<MTLVertexBufferLayoutDescriptor> ToObjC(const VertexBufferLayoutDesc &desc)
88{
89    auto objCDesc = angle::adoptObjCPtr([[MTLVertexBufferLayoutDescriptor alloc] init]);
90    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stepFunction);
91    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stepRate);
92    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, stride);
93    return objCDesc;
94}
95
96inline angle::ObjCPtr<MTLVertexDescriptor> ToObjC(const VertexDesc &desc)
97{
98    auto objCDesc = angle::adoptObjCPtr([[MTLVertexDescriptor alloc] init]);
99    [objCDesc reset];
100
101    for (uint8_t i = 0; i < desc.numAttribs; ++i)
102    {
103        [objCDesc.get().attributes setObject:ToObjC(desc.attributes[i]) atIndexedSubscript:i];
104    }
105
106    for (uint8_t i = 0; i < desc.numBufferLayouts; ++i)
107    {
108        // Ignore if stepFunction is kVertexStepFunctionInvalid.
109        // If we don't set this slot, it will apparently be disabled by metal runtime.
110        if (desc.layouts[i].stepFunction != kVertexStepFunctionInvalid)
111        {
112            [objCDesc.get().layouts setObject:ToObjC(desc.layouts[i]) atIndexedSubscript:i];
113        }
114    }
115
116    return objCDesc;
117}
118
119inline angle::ObjCPtr<MTLRenderPipelineColorAttachmentDescriptor> ToObjC(
120    const RenderPipelineColorAttachmentDesc &desc)
121{
122    auto objCDesc = angle::adoptObjCPtr([[MTLRenderPipelineColorAttachmentDescriptor alloc] init]);
123    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, pixelFormat);
124    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, writeMask);
125    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, alphaBlendOperation);
126    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, rgbBlendOperation);
127    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, destinationAlphaBlendFactor);
128    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, destinationRGBBlendFactor);
129    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, sourceAlphaBlendFactor);
130    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, sourceRGBBlendFactor);
131    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), desc, blendingEnabled);
132    return objCDesc;
133}
134
135id<MTLTexture> ToObjC(const TextureRef &texture)
136{
137    auto textureRef = texture;
138    return textureRef ? textureRef->get() : nil;
139}
140
141void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
142                                        MTLRenderPassAttachmentDescriptor *dst)
143{
144    const TextureRef &implicitMsTexture = src.implicitMSTexture;
145
146    if (implicitMsTexture)
147    {
148        dst.texture        = ToObjC(implicitMsTexture);
149        dst.level          = 0;
150        dst.slice          = 0;
151        dst.depthPlane     = 0;
152        dst.resolveTexture = ToObjC(src.texture);
153        dst.resolveLevel   = src.level.get();
154        if (dst.resolveTexture.textureType == MTLTextureType3D)
155        {
156            dst.resolveDepthPlane = src.sliceOrDepth;
157            dst.resolveSlice      = 0;
158        }
159        else
160        {
161            dst.resolveSlice      = src.sliceOrDepth;
162            dst.resolveDepthPlane = 0;
163        }
164    }
165    else
166    {
167        dst.texture = ToObjC(src.texture);
168        dst.level   = src.level.get();
169        if (dst.texture.textureType == MTLTextureType3D)
170        {
171            dst.depthPlane = src.sliceOrDepth;
172            dst.slice      = 0;
173        }
174        else
175        {
176            dst.slice      = src.sliceOrDepth;
177            dst.depthPlane = 0;
178        }
179        dst.resolveTexture    = nil;
180        dst.resolveLevel      = 0;
181        dst.resolveSlice      = 0;
182        dst.resolveDepthPlane = 0;
183    }
184
185    ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction);
186    ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction);
187    ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions);
188}
189
190void ToObjC(const RenderPassColorAttachmentDesc &desc,
191            MTLRenderPassColorAttachmentDescriptor *objCDesc)
192{
193    BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
194
195    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearColor);
196}
197
198void ToObjC(const RenderPassDepthAttachmentDesc &desc,
199            MTLRenderPassDepthAttachmentDescriptor *objCDesc)
200{
201    BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
202
203    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearDepth);
204}
205
206void ToObjC(const RenderPassStencilAttachmentDesc &desc,
207            MTLRenderPassStencilAttachmentDescriptor *objCDesc)
208{
209    BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
210
211    ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil);
212}
213
214MTLColorWriteMask AdjustColorWriteMaskForSharedExponent(MTLColorWriteMask mask)
215{
216    // For RGB9_E5 color buffers, ANGLE frontend validation ignores alpha writemask value.
217    // Metal validation is more strict and allows only all-enabled or all-disabled.
218    ASSERT((mask == MTLColorWriteMaskAll) ||
219           (mask == (MTLColorWriteMaskAll ^ MTLColorWriteMaskAlpha)) ||
220           (mask == MTLColorWriteMaskAlpha) || (mask == MTLColorWriteMaskNone));
221    return (mask & MTLColorWriteMaskBlue) ? MTLColorWriteMaskAll : MTLColorWriteMaskNone;
222}
223
224}  // namespace
225
226// StencilDesc implementation
227bool StencilDesc::operator==(const StencilDesc &rhs) const
228{
229    return ANGLE_PROP_EQ(*this, rhs, stencilFailureOperation) &&
230           ANGLE_PROP_EQ(*this, rhs, depthFailureOperation) &&
231           ANGLE_PROP_EQ(*this, rhs, depthStencilPassOperation) &&
232
233           ANGLE_PROP_EQ(*this, rhs, stencilCompareFunction) &&
234
235           ANGLE_PROP_EQ(*this, rhs, readMask) && ANGLE_PROP_EQ(*this, rhs, writeMask);
236}
237
238void StencilDesc::reset()
239{
240    stencilFailureOperation = depthFailureOperation = depthStencilPassOperation =
241        MTLStencilOperationKeep;
242
243    stencilCompareFunction = MTLCompareFunctionAlways;
244    readMask = writeMask = std::numeric_limits<uint32_t>::max() & mtl::kStencilMaskAll;
245}
246
247// DepthStencilDesc implementation
248DepthStencilDesc::DepthStencilDesc()
249{
250    memset(this, 0, sizeof(*this));
251}
252DepthStencilDesc::DepthStencilDesc(const DepthStencilDesc &src)
253{
254    memcpy(this, &src, sizeof(*this));
255}
256DepthStencilDesc::DepthStencilDesc(DepthStencilDesc &&src)
257{
258    memcpy(this, &src, sizeof(*this));
259}
260
261DepthStencilDesc &DepthStencilDesc::operator=(const DepthStencilDesc &src)
262{
263    memcpy(this, &src, sizeof(*this));
264    return *this;
265}
266
267bool DepthStencilDesc::operator==(const DepthStencilDesc &rhs) const
268{
269    return ANGLE_PROP_EQ(*this, rhs, backFaceStencil) &&
270           ANGLE_PROP_EQ(*this, rhs, frontFaceStencil) &&
271
272           ANGLE_PROP_EQ(*this, rhs, depthCompareFunction) &&
273
274           ANGLE_PROP_EQ(*this, rhs, depthWriteEnabled);
275}
276
277void DepthStencilDesc::reset()
278{
279    frontFaceStencil.reset();
280    backFaceStencil.reset();
281
282    depthCompareFunction = MTLCompareFunctionAlways;
283    depthWriteEnabled    = true;
284}
285
286void DepthStencilDesc::updateDepthTestEnabled(const gl::DepthStencilState &dsState)
287{
288    if (!dsState.depthTest)
289    {
290        depthCompareFunction = MTLCompareFunctionAlways;
291        depthWriteEnabled    = false;
292    }
293    else
294    {
295        updateDepthCompareFunc(dsState);
296        updateDepthWriteEnabled(dsState);
297    }
298}
299
300void DepthStencilDesc::updateDepthWriteEnabled(const gl::DepthStencilState &dsState)
301{
302    depthWriteEnabled = dsState.depthTest && dsState.depthMask;
303}
304
305void DepthStencilDesc::updateDepthCompareFunc(const gl::DepthStencilState &dsState)
306{
307    if (!dsState.depthTest)
308    {
309        return;
310    }
311    depthCompareFunction = GetCompareFunc(dsState.depthFunc);
312}
313
314void DepthStencilDesc::updateStencilTestEnabled(const gl::DepthStencilState &dsState)
315{
316    if (!dsState.stencilTest)
317    {
318        frontFaceStencil.stencilCompareFunction    = MTLCompareFunctionAlways;
319        frontFaceStencil.depthFailureOperation     = MTLStencilOperationKeep;
320        frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
321        frontFaceStencil.writeMask                 = 0;
322
323        backFaceStencil.stencilCompareFunction    = MTLCompareFunctionAlways;
324        backFaceStencil.depthFailureOperation     = MTLStencilOperationKeep;
325        backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
326        backFaceStencil.writeMask                 = 0;
327    }
328    else
329    {
330        updateStencilFrontFuncs(dsState);
331        updateStencilFrontOps(dsState);
332        updateStencilFrontWriteMask(dsState);
333        updateStencilBackFuncs(dsState);
334        updateStencilBackOps(dsState);
335        updateStencilBackWriteMask(dsState);
336    }
337}
338
339void DepthStencilDesc::updateStencilFrontOps(const gl::DepthStencilState &dsState)
340{
341    if (!dsState.stencilTest)
342    {
343        return;
344    }
345    frontFaceStencil.stencilFailureOperation   = GetStencilOp(dsState.stencilFail);
346    frontFaceStencil.depthFailureOperation     = GetStencilOp(dsState.stencilPassDepthFail);
347    frontFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilPassDepthPass);
348}
349
350void DepthStencilDesc::updateStencilBackOps(const gl::DepthStencilState &dsState)
351{
352    if (!dsState.stencilTest)
353    {
354        return;
355    }
356    backFaceStencil.stencilFailureOperation   = GetStencilOp(dsState.stencilBackFail);
357    backFaceStencil.depthFailureOperation     = GetStencilOp(dsState.stencilBackPassDepthFail);
358    backFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilBackPassDepthPass);
359}
360
361void DepthStencilDesc::updateStencilFrontFuncs(const gl::DepthStencilState &dsState)
362{
363    if (!dsState.stencilTest)
364    {
365        return;
366    }
367    frontFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilFunc);
368    frontFaceStencil.readMask               = dsState.stencilMask & mtl::kStencilMaskAll;
369}
370
371void DepthStencilDesc::updateStencilBackFuncs(const gl::DepthStencilState &dsState)
372{
373    if (!dsState.stencilTest)
374    {
375        return;
376    }
377    backFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilBackFunc);
378    backFaceStencil.readMask               = dsState.stencilBackMask & mtl::kStencilMaskAll;
379}
380
381void DepthStencilDesc::updateStencilFrontWriteMask(const gl::DepthStencilState &dsState)
382{
383    if (!dsState.stencilTest)
384    {
385        return;
386    }
387    frontFaceStencil.writeMask = dsState.stencilWritemask & mtl::kStencilMaskAll;
388}
389
390void DepthStencilDesc::updateStencilBackWriteMask(const gl::DepthStencilState &dsState)
391{
392    if (!dsState.stencilTest)
393    {
394        return;
395    }
396    backFaceStencil.writeMask = dsState.stencilBackWritemask & mtl::kStencilMaskAll;
397}
398
399size_t DepthStencilDesc::hash() const
400{
401    return angle::ComputeGenericHash(*this);
402}
403
404// SamplerDesc implementation
405SamplerDesc::SamplerDesc()
406{
407    memset(this, 0, sizeof(*this));
408}
409SamplerDesc::SamplerDesc(const SamplerDesc &src)
410{
411    memcpy(this, &src, sizeof(*this));
412}
413SamplerDesc::SamplerDesc(SamplerDesc &&src)
414{
415    memcpy(this, &src, sizeof(*this));
416}
417
418SamplerDesc::SamplerDesc(const gl::SamplerState &glState) : SamplerDesc()
419{
420    rAddressMode = GetSamplerAddressMode(glState.getWrapR());
421    sAddressMode = GetSamplerAddressMode(glState.getWrapS());
422    tAddressMode = GetSamplerAddressMode(glState.getWrapT());
423
424    minFilter = GetFilter(glState.getMinFilter());
425    magFilter = GetFilter(glState.getMagFilter());
426    mipFilter = GetMipmapFilter(glState.getMinFilter());
427
428    maxAnisotropy = static_cast<uint32_t>(glState.getMaxAnisotropy());
429
430    compareFunction = GetCompareFunc(glState.getCompareFunc());
431}
432
433SamplerDesc &SamplerDesc::operator=(const SamplerDesc &src)
434{
435    memcpy(this, &src, sizeof(*this));
436    return *this;
437}
438
439void SamplerDesc::reset()
440{
441    rAddressMode = MTLSamplerAddressModeClampToEdge;
442    sAddressMode = MTLSamplerAddressModeClampToEdge;
443    tAddressMode = MTLSamplerAddressModeClampToEdge;
444
445    minFilter = MTLSamplerMinMagFilterNearest;
446    magFilter = MTLSamplerMinMagFilterNearest;
447    mipFilter = MTLSamplerMipFilterNearest;
448
449    maxAnisotropy = 1;
450
451    compareFunction = MTLCompareFunctionNever;
452}
453
454bool SamplerDesc::operator==(const SamplerDesc &rhs) const
455{
456    return ANGLE_PROP_EQ(*this, rhs, rAddressMode) && ANGLE_PROP_EQ(*this, rhs, sAddressMode) &&
457           ANGLE_PROP_EQ(*this, rhs, tAddressMode) &&
458
459           ANGLE_PROP_EQ(*this, rhs, minFilter) && ANGLE_PROP_EQ(*this, rhs, magFilter) &&
460           ANGLE_PROP_EQ(*this, rhs, mipFilter) &&
461
462           ANGLE_PROP_EQ(*this, rhs, maxAnisotropy) &&
463
464           ANGLE_PROP_EQ(*this, rhs, compareFunction);
465}
466
467size_t SamplerDesc::hash() const
468{
469    return angle::ComputeGenericHash(*this);
470}
471
472// BlendDesc implementation
473bool BlendDesc::operator==(const BlendDesc &rhs) const
474{
475    return ANGLE_PROP_EQ(*this, rhs, writeMask) &&
476
477           ANGLE_PROP_EQ(*this, rhs, alphaBlendOperation) &&
478           ANGLE_PROP_EQ(*this, rhs, rgbBlendOperation) &&
479
480           ANGLE_PROP_EQ(*this, rhs, destinationAlphaBlendFactor) &&
481           ANGLE_PROP_EQ(*this, rhs, destinationRGBBlendFactor) &&
482           ANGLE_PROP_EQ(*this, rhs, sourceAlphaBlendFactor) &&
483           ANGLE_PROP_EQ(*this, rhs, sourceRGBBlendFactor) &&
484
485           ANGLE_PROP_EQ(*this, rhs, blendingEnabled);
486}
487
488void BlendDesc::reset()
489{
490    reset(MTLColorWriteMaskAll);
491}
492
493void BlendDesc::reset(MTLColorWriteMask _writeMask)
494{
495    writeMask = _writeMask;
496
497    blendingEnabled     = false;
498    alphaBlendOperation = rgbBlendOperation = MTLBlendOperationAdd;
499
500    destinationAlphaBlendFactor = destinationRGBBlendFactor = MTLBlendFactorZero;
501    sourceAlphaBlendFactor = sourceRGBBlendFactor = MTLBlendFactorOne;
502}
503
504void BlendDesc::updateWriteMask(const uint8_t angleMask)
505{
506    ASSERT(angleMask == (angleMask & 0xF));
507
508// ANGLE's packed color mask is abgr (matches Vulkan & D3D11), while Metal expects rgba.
509#if defined(__aarch64__)
510    // ARM64 can reverse bits in a single instruction
511    writeMask = __builtin_bitreverse8(angleMask) >> 4;
512#else
513    /* On other architectures, Clang generates a polyfill that uses more
514       instructions than the following expression optimized for a 4-bit value.
515
516       (abgr * 0x41) & 0x14A:
517        .......abgr +
518        .abgr...... &
519        00101001010 =
520        ..b.r..a.g.
521
522       (b.r..a.g.) * 0x111:
523                b.r..a.g. +
524            b.r..a.g..... +
525        b.r..a.g......... =
526        b.r.bargbarg.a.g.
527              ^^^^
528    */
529    writeMask = ((((angleMask * 0x41) & 0x14A) * 0x111) >> 7) & 0xF;
530#endif
531}
532
533// RenderPipelineColorAttachmentDesc implementation
534bool RenderPipelineColorAttachmentDesc::operator==(
535    const RenderPipelineColorAttachmentDesc &rhs) const
536{
537    if (!BlendDesc::operator==(rhs))
538    {
539        return false;
540    }
541    return ANGLE_PROP_EQ(*this, rhs, pixelFormat);
542}
543
544void RenderPipelineColorAttachmentDesc::reset()
545{
546    reset(MTLPixelFormatInvalid);
547}
548
549void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format)
550{
551    reset(format, MTLColorWriteMaskAll);
552}
553
554void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, MTLColorWriteMask _writeMask)
555{
556    this->pixelFormat = format;
557
558    if (format == MTLPixelFormatRGB9E5Float)
559    {
560        _writeMask = AdjustColorWriteMaskForSharedExponent(_writeMask);
561    }
562
563    BlendDesc::reset(_writeMask);
564}
565
566void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, const BlendDesc &blendDesc)
567{
568    this->pixelFormat = format;
569
570    BlendDesc::operator=(blendDesc);
571
572    if (format == MTLPixelFormatRGB9E5Float)
573    {
574        writeMask = AdjustColorWriteMaskForSharedExponent(writeMask);
575    }
576}
577
578// RenderPipelineOutputDesc implementation
579bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) const
580{
581    if (numColorAttachments != rhs.numColorAttachments)
582    {
583        return false;
584    }
585
586    for (uint8_t i = 0; i < numColorAttachments; ++i)
587    {
588        if (colorAttachments[i] != rhs.colorAttachments[i])
589        {
590            return false;
591        }
592    }
593
594    return ANGLE_PROP_EQ(*this, rhs, depthAttachmentPixelFormat) &&
595           ANGLE_PROP_EQ(*this, rhs, stencilAttachmentPixelFormat);
596}
597
598void RenderPipelineOutputDesc::updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers)
599{
600    for (uint32_t colorIndex = 0; colorIndex < this->numColorAttachments; ++colorIndex)
601    {
602        if (!enabledBuffers.test(colorIndex))
603        {
604            this->colorAttachments[colorIndex].writeMask = MTLColorWriteMaskNone;
605        }
606    }
607}
608
609// RenderPipelineDesc implementation
610RenderPipelineDesc::RenderPipelineDesc()
611{
612    memset(this, 0, sizeof(*this));
613    outputDescriptor.rasterSampleCount = 1;
614    rasterizationType                  = RenderPipelineRasterization::Enabled;
615}
616
617RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src)
618{
619    memcpy(this, &src, sizeof(*this));
620}
621
622RenderPipelineDesc::RenderPipelineDesc(RenderPipelineDesc &&src)
623{
624    memcpy(this, &src, sizeof(*this));
625}
626
627RenderPipelineDesc &RenderPipelineDesc::operator=(const RenderPipelineDesc &src)
628{
629    memcpy(this, &src, sizeof(*this));
630    return *this;
631}
632
633bool RenderPipelineDesc::operator==(const RenderPipelineDesc &rhs) const
634{
635    // NOTE(hqle): Use a faster way to compare, i.e take into account
636    // the number of active vertex attributes & render targets.
637    // If that way is used, hash() method must be changed also.
638    return memcmp(this, &rhs, sizeof(*this)) == 0;
639}
640
641size_t RenderPipelineDesc::hash() const
642{
643    return angle::ComputeGenericHash(*this);
644}
645
646bool RenderPipelineDesc::rasterizationEnabled() const
647{
648    return rasterizationType != RenderPipelineRasterization::Disabled;
649}
650
651angle::ObjCPtr<MTLRenderPipelineDescriptor> RenderPipelineDesc::createMetalDesc(
652    id<MTLFunction> vertexShader,
653    id<MTLFunction> fragmentShader) const
654{
655    auto objCDesc = angle::adoptObjCPtr([[MTLRenderPipelineDescriptor alloc] init]);
656    [objCDesc reset];
657
658    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, vertexDescriptor);
659
660    for (uint8_t i = 0; i < outputDescriptor.numColorAttachments; ++i)
661    {
662        [objCDesc.get().colorAttachments setObject:ToObjC(outputDescriptor.colorAttachments[i])
663                                atIndexedSubscript:i];
664    }
665    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, depthAttachmentPixelFormat);
666    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, stencilAttachmentPixelFormat);
667    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), outputDescriptor, rasterSampleCount);
668
669    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, inputPrimitiveTopology);
670    ANGLE_OBJC_CP_PROPERTY(objCDesc.get(), *this, alphaToCoverageEnabled);
671
672    // rasterizationEnabled will be true for both EmulatedDiscard & Enabled.
673    objCDesc.get().rasterizationEnabled = rasterizationEnabled();
674
675    objCDesc.get().vertexFunction   = vertexShader;
676    objCDesc.get().fragmentFunction = objCDesc.get().rasterizationEnabled ? fragmentShader : nil;
677
678    return objCDesc;
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.rasterSampleCount         = this->rasterSampleCount;
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                blendDescArray[i].writeMask != MTLColorWriteMaskNone)
752            {
753                // Copy parameters from blend state
754                outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(),
755                                                           blendDescArray[i]);
756            }
757            else
758            {
759                // Disable blending if the attachment's render target doesn't support blending
760                // or if all its color channels are masked out. The latter is needed because:
761                //
762                // * When blending is enabled and *Source1* blend factors are used, Metal
763                //   requires a fragment shader to bind both primary and secondary outputs
764                //
765                // * ANGLE frontend validation allows draw calls on draw buffers without
766                //   bound fragment outputs if all their color channels are masked out
767                //
768                // * When all color channels are masked out, blending has no effect anyway
769                //
770                // Besides disabling blending, use default values for factors and
771                // operations to reduce the number of unique pipeline states.
772                outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(),
773                                                           blendDescArray[i].writeMask);
774            }
775
776            // Combine the masks. This is useful when the texture is not supposed to have alpha
777            // channel such as GL_RGB8, however, Metal doesn't natively support 24 bit RGB, so
778            // we need to use RGBA texture, and then disable alpha write to this texture
779            outputDescriptor.colorAttachments[i].writeMask &= texture->getColorWritableMask();
780        }
781        else
782        {
783
784            outputDescriptor.colorAttachments[i].blendingEnabled = false;
785            outputDescriptor.colorAttachments[i].pixelFormat     = MTLPixelFormatInvalid;
786        }
787    }
788
789    // Reset the unused output slots to ensure consistent hash value
790    for (uint32_t i = this->numColorAttachments; i < outputDescriptor.colorAttachments.size(); ++i)
791    {
792        outputDescriptor.colorAttachments[i].reset();
793    }
794
795    auto depthTexture = this->depthAttachment.texture;
796    outputDescriptor.depthAttachmentPixelFormat =
797        depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid;
798
799    auto stencilTexture = this->stencilAttachment.texture;
800    outputDescriptor.stencilAttachmentPixelFormat =
801        stencilTexture ? stencilTexture->pixelFormat() : MTLPixelFormatInvalid;
802}
803
804bool RenderPassDesc::equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const
805{
806    if (numColorAttachments != other.numColorAttachments)
807    {
808        return false;
809    }
810
811    for (uint32_t i = 0; i < numColorAttachments; ++i)
812    {
813        auto &renderPassColorAttachment = colorAttachments[i];
814        auto &otherRPAttachment         = other.colorAttachments[i];
815        if (!renderPassColorAttachment.equalIgnoreLoadStoreOptions(otherRPAttachment))
816        {
817            return false;
818        }
819    }
820
821    if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight)
822    {
823        return false;
824    }
825
826    return depthAttachment.equalIgnoreLoadStoreOptions(other.depthAttachment) &&
827           stencilAttachment.equalIgnoreLoadStoreOptions(other.stencilAttachment);
828}
829
830bool RenderPassDesc::operator==(const RenderPassDesc &other) const
831{
832    if (numColorAttachments != other.numColorAttachments)
833    {
834        return false;
835    }
836
837    for (uint32_t i = 0; i < numColorAttachments; ++i)
838    {
839        auto &renderPassColorAttachment = colorAttachments[i];
840        auto &otherRPAttachment         = other.colorAttachments[i];
841        if (renderPassColorAttachment != (otherRPAttachment))
842        {
843            return false;
844        }
845    }
846
847    if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight)
848    {
849        return false;
850    }
851
852    return depthAttachment == other.depthAttachment && stencilAttachment == other.stencilAttachment;
853}
854
855// Convert to Metal object
856void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc,
857                                        uint32_t deviceMaxRenderTargets) const
858{
859    ASSERT(deviceMaxRenderTargets <= kMaxRenderTargets);
860
861    for (uint32_t i = 0; i < numColorAttachments; ++i)
862    {
863        ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]);
864    }
865    for (uint32_t i = numColorAttachments; i < deviceMaxRenderTargets; ++i)
866    {
867        // Inactive render target
868        objCDesc.colorAttachments[i].texture     = nil;
869        objCDesc.colorAttachments[i].level       = 0;
870        objCDesc.colorAttachments[i].slice       = 0;
871        objCDesc.colorAttachments[i].depthPlane  = 0;
872        objCDesc.colorAttachments[i].loadAction  = MTLLoadActionDontCare;
873        objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare;
874    }
875
876    ToObjC(depthAttachment, objCDesc.depthAttachment);
877    ToObjC(stencilAttachment, objCDesc.stencilAttachment);
878
879    if ((defaultWidth | defaultHeight) != 0)
880    {
881        objCDesc.renderTargetWidth        = defaultWidth;
882        objCDesc.renderTargetHeight       = defaultHeight;
883        objCDesc.defaultRasterSampleCount = 1;
884    }
885}
886
887// ProvokingVertexPipelineDesc
888ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc()
889{
890    memset(this, 0, sizeof(*this));
891}
892ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc(
893    const ProvokingVertexComputePipelineDesc &src)
894{
895    memcpy(this, &src, sizeof(*this));
896}
897ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc(
898    ProvokingVertexComputePipelineDesc &&src)
899{
900    memcpy(this, &src, sizeof(*this));
901}
902ProvokingVertexComputePipelineDesc &ProvokingVertexComputePipelineDesc::operator=(
903    const ProvokingVertexComputePipelineDesc &src)
904{
905    memcpy(this, &src, sizeof(*this));
906    return *this;
907}
908bool ProvokingVertexComputePipelineDesc::operator==(
909    const ProvokingVertexComputePipelineDesc &rhs) const
910{
911    return memcmp(this, &rhs, sizeof(*this)) == 0;
912}
913bool ProvokingVertexComputePipelineDesc::operator!=(
914    const ProvokingVertexComputePipelineDesc &rhs) const
915{
916    return !(*this == rhs);
917}
918size_t ProvokingVertexComputePipelineDesc::hash() const
919{
920    return angle::ComputeGenericHash(*this);
921}
922
923// StateCache implementation
924StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {}
925
926StateCache::~StateCache() {}
927
928angle::ObjCPtr<id<MTLDepthStencilState>> StateCache::getNullDepthStencilState(
929    const mtl::ContextDevice &device)
930{
931    if (!mNullDepthStencilState)
932    {
933        DepthStencilDesc desc;
934        desc.reset();
935        ASSERT(desc.frontFaceStencil.stencilCompareFunction == MTLCompareFunctionAlways);
936        desc.depthWriteEnabled = false;
937        mNullDepthStencilState = getDepthStencilState(device, desc);
938    }
939    return mNullDepthStencilState;
940}
941
942angle::ObjCPtr<id<MTLDepthStencilState>> StateCache::getDepthStencilState(
943    const mtl::ContextDevice &device,
944    const DepthStencilDesc &desc)
945{
946    auto ite = mDepthStencilStates.find(desc);
947    if (ite == mDepthStencilStates.end())
948    {
949        auto re = mDepthStencilStates.insert(
950            std::make_pair(desc, device.newDepthStencilStateWithDescriptor(ToObjC(desc))));
951        if (!re.second)
952        {
953            return nil;
954        }
955
956        ite = re.first;
957    }
958
959    return ite->second;
960}
961
962angle::ObjCPtr<id<MTLSamplerState>> StateCache::getSamplerState(const mtl::ContextDevice &device,
963                                                                const SamplerDesc &desc)
964{
965    auto ite = mSamplerStates.find(desc);
966    if (ite == mSamplerStates.end())
967    {
968        auto objCDesc = ToObjC(desc);
969        if (!mFeatures.allowRuntimeSamplerCompareMode.enabled)
970        {
971            // Runtime sampler compare mode is not supported, fallback to never.
972            objCDesc.get().compareFunction = MTLCompareFunctionNever;
973        }
974        auto re = mSamplerStates.insert(
975            std::make_pair(desc, device.newSamplerStateWithDescriptor(objCDesc)));
976        if (!re.second)
977            return nil;
978
979        ite = re.first;
980    }
981
982    return ite->second;
983}
984
985angle::ObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(ContextMtl *context)
986{
987    return getNullSamplerState(context->getMetalDevice());
988}
989
990angle::ObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(
991    const mtl::ContextDevice &device)
992{
993    SamplerDesc desc;
994    desc.reset();
995
996    return getSamplerState(device, desc);
997}
998
999void StateCache::clear()
1000{
1001    mNullDepthStencilState = nil;
1002    mDepthStencilStates.clear();
1003    mSamplerStates.clear();
1004}
1005
1006}  // namespace mtl
1007}  // namespace rx
1008