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