• 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_render_utils.mm:
7//    Implements the class methods for RenderUtils.
8//
9
10#include "libANGLE/renderer/metal/mtl_render_utils.h"
11
12#include <utility>
13
14#include "common/debug.h"
15#include "libANGLE/ErrorStrings.h"
16#include "libANGLE/renderer/metal/BufferMtl.h"
17#include "libANGLE/renderer/metal/ContextMtl.h"
18#include "libANGLE/renderer/metal/DisplayMtl.h"
19#include "libANGLE/renderer/metal/ProgramMtl.h"
20#include "libANGLE/renderer/metal/QueryMtl.h"
21#include "libANGLE/renderer/metal/mtl_common.h"
22#include "libANGLE/renderer/metal/mtl_utils.h"
23
24namespace rx
25{
26namespace mtl
27{
28namespace
29{
30
31#define NUM_COLOR_OUTPUTS_CONSTANT_NAME @"kNumColorOutputs"
32#define SOURCE_BUFFER_ALIGNED_CONSTANT_NAME @"kSourceBufferAligned"
33#define SOURCE_IDX_IS_U8_CONSTANT_NAME @"kSourceIndexIsU8"
34#define SOURCE_IDX_IS_U16_CONSTANT_NAME @"kSourceIndexIsU16"
35#define SOURCE_IDX_IS_U32_CONSTANT_NAME @"kSourceIndexIsU32"
36#define PREMULTIPLY_ALPHA_CONSTANT_NAME @"kPremultiplyAlpha"
37#define UNMULTIPLY_ALPHA_CONSTANT_NAME @"kUnmultiplyAlpha"
38#define TRANSFORM_LINEAR_TO_SRGB_CONSTANT_NAME @"kTransformLinearToSrgb"
39#define SOURCE_TEXTURE_TYPE_CONSTANT_NAME @"kSourceTextureType"
40#define SOURCE_TEXTURE2_TYPE_CONSTANT_NAME @"kSourceTexture2Type"
41#define COPY_FORMAT_TYPE_CONSTANT_NAME @"kCopyFormatType"
42#define PIXEL_COPY_TEXTURE_TYPE_CONSTANT_NAME @"kCopyTextureType"
43#define VISIBILITY_RESULT_KEEP_OLD_VAL_CONSTANT_NAME @"kCombineWithExistingResult"
44
45// See libANGLE/renderer/metal/shaders/clear.metal
46struct ClearParamsUniform
47{
48    float clearColor[4];
49    float clearDepth;
50    float padding[3];
51};
52
53// See libANGLE/renderer/metal/shaders/blit.metal
54struct BlitParamsUniform
55{
56    // 0: lower left, 1: upper right
57    float srcTexCoords[2][2];
58    int srcLevel         = 0;
59    int srcLayer         = 0;
60    uint8_t dstLuminance = 0;  // dest texture is luminace
61    uint8_t padding[7];
62};
63
64struct BlitStencilToBufferParamsUniform
65{
66    float srcStartTexCoords[2];
67    float srcTexCoordSteps[2];
68    uint32_t srcLevel;
69    uint32_t srcLayer;
70
71    uint32_t dstSize[2];
72    uint32_t dstBufferRowPitch;
73    uint8_t resolveMS;
74
75    uint8_t padding[11];
76};
77
78// See libANGLE/renderer/metal/shaders/genIndices.metal
79struct TriFanOrLineLoopArrayParams
80{
81    uint firstVertex;
82    uint vertexCount;
83    uint padding[2];
84};
85
86struct IndexConversionUniform
87{
88    uint32_t srcOffset;
89    uint32_t indexCount;
90    uint8_t primitiveRestartEnabled;
91    uint8_t padding[7];
92};
93
94// See libANGLE/renderer/metal/shaders/visibility.metal
95struct CombineVisibilityResultUniform
96{
97    uint32_t startOffset;
98    uint32_t numOffsets;
99    uint32_t padding[2];
100};
101
102// See libANGLE/renderer/metal/shaders/gen_mipmap.metal
103struct Generate3DMipmapUniform
104{
105    uint32_t srcLevel;
106    uint32_t numMipmapsToGenerate;
107    uint8_t sRGB;
108    uint8_t padding[7];
109};
110
111// See libANGLE/renderer/metal/shaders/copy_buffer.metal
112struct CopyPixelFromBufferUniforms
113{
114    uint32_t copySize[3];
115    uint32_t padding1;
116    uint32_t textureOffset[3];
117    uint32_t padding2;
118    uint32_t bufferStartOffset;
119    uint32_t pixelSize;
120    uint32_t bufferRowPitch;
121    uint32_t bufferDepthPitch;
122};
123struct WritePixelToBufferUniforms
124{
125    uint32_t copySize[2];
126    uint32_t textureOffset[2];
127
128    uint32_t bufferStartOffset;
129    uint32_t pixelSize;
130    uint32_t bufferRowPitch;
131
132    uint32_t textureLevel;
133    uint32_t textureLayer;
134    uint8_t reverseTextureRowOrder;
135
136    uint8_t padding[11];
137};
138
139struct CopyVertexUniforms
140{
141    uint32_t srcBufferStartOffset;
142    uint32_t srcStride;
143    uint32_t srcComponentBytes;
144    uint32_t srcComponents;
145    uint32_t srcDefaultAlphaData;
146
147    uint32_t dstBufferStartOffset;
148    uint32_t dstStride;
149    uint32_t dstComponents;
150
151    uint32_t vertexCount;
152
153    uint32_t padding[3];
154};
155
156// Class to automatically disable occlusion query upon entering block and re-able it upon
157// exiting block.
158struct ScopedDisableOcclusionQuery
159{
160    ScopedDisableOcclusionQuery(ContextMtl *contextMtl,
161                                RenderCommandEncoder *encoder,
162                                angle::Result *resultOut)
163        : mContextMtl(contextMtl), mEncoder(encoder), mResultOut(resultOut)
164    {
165#ifndef NDEBUG
166        if (contextMtl->hasActiveOcclusionQuery())
167        {
168            encoder->pushDebugGroup(@"Disabled OcclusionQuery");
169        }
170#endif
171        // temporarily disable occlusion query
172        contextMtl->disableActiveOcclusionQueryInRenderPass();
173    }
174    ~ScopedDisableOcclusionQuery()
175    {
176        *mResultOut = mContextMtl->restartActiveOcclusionQueryInRenderPass();
177#ifndef NDEBUG
178        if (mContextMtl->hasActiveOcclusionQuery())
179        {
180            mEncoder->popDebugGroup();
181        }
182#else
183        ANGLE_UNUSED_VARIABLE(mEncoder);
184#endif
185    }
186
187  private:
188    ContextMtl *mContextMtl;
189    RenderCommandEncoder *mEncoder;
190
191    angle::Result *mResultOut;
192};
193
194void GetBlitTexCoords(const NormalizedCoords &normalizedCoords,
195                      bool srcYFlipped,
196                      bool unpackFlipX,
197                      bool unpackFlipY,
198                      float *u0,
199                      float *v0,
200                      float *u1,
201                      float *v1)
202{
203    *u0 = normalizedCoords.v[0];
204    *v0 = normalizedCoords.v[1];
205    *u1 = normalizedCoords.v[2];
206    *v1 = normalizedCoords.v[3];
207
208    if (srcYFlipped)
209    {
210        *v0 = 1.0 - *v0;
211        *v1 = 1.0 - *v1;
212    }
213
214    if (unpackFlipX)
215    {
216        std::swap(*u0, *u1);
217    }
218
219    if (unpackFlipY)
220    {
221        std::swap(*v0, *v1);
222    }
223}
224
225template <typename T>
226angle::Result GenTriFanFromClientElements(ContextMtl *contextMtl,
227                                          GLsizei count,
228                                          bool primitiveRestartEnabled,
229                                          const T *indices,
230                                          const BufferRef &dstBuffer,
231                                          uint32_t dstOffset,
232                                          uint32_t *indicesGenerated)
233{
234    ASSERT(count > 2);
235    ASSERT(indicesGenerated != nullptr);
236    constexpr T kSrcPrimitiveRestartIndex = std::numeric_limits<T>::max();
237    GLsizei dstTriangle                   = 0;
238    uint32_t *dstPtr = reinterpret_cast<uint32_t *>(dstBuffer->map(contextMtl) + dstOffset);
239    T triFirstIdx;
240    memcpy(&triFirstIdx, indices, sizeof(triFirstIdx));
241
242    if (primitiveRestartEnabled)
243    {
244        GLsizei triFirstIdxLoc = 0;
245        while (triFirstIdx == kSrcPrimitiveRestartIndex && triFirstIdxLoc + 2 < count)
246        {
247            ++triFirstIdxLoc;
248            memcpy(&triFirstIdx, indices + triFirstIdxLoc, sizeof(triFirstIdx));
249        }
250
251        T srcPrevIdx = 0;
252        if (triFirstIdxLoc + 1 < count)
253        {
254            memcpy(&srcPrevIdx, indices + triFirstIdxLoc + 1, sizeof(srcPrevIdx));
255        }
256
257        for (GLsizei i = triFirstIdxLoc + 2; i < count; ++i)
258        {
259            uint32_t triIndices[3];
260            T srcIdx;
261            memcpy(&srcIdx, indices + i, sizeof(srcIdx));
262            bool completeTriangle = true;
263            if (srcPrevIdx == kSrcPrimitiveRestartIndex || srcIdx == kSrcPrimitiveRestartIndex)
264            {
265                // Incomplete triangle. Move to next triangle and set triFirstIndex
266                triFirstIdx      = srcIdx;
267                triFirstIdxLoc   = i;
268                completeTriangle = false;
269            }
270            else if (i < triFirstIdxLoc + 2)
271            {
272                // Incomplete triangle, move to next triangle
273                completeTriangle = false;
274            }
275            else
276            {
277                triIndices[0] = triFirstIdx;
278                triIndices[1] = srcPrevIdx;
279                triIndices[2] = srcIdx;
280            }
281            if (completeTriangle)
282            {
283                memcpy(dstPtr + 3 * dstTriangle, triIndices, sizeof(triIndices));
284                ++dstTriangle;
285            }
286            srcPrevIdx = srcIdx;
287        }
288    }
289    else
290    {
291        T srcPrevIdx;
292        memcpy(&srcPrevIdx, indices + 1, sizeof(srcPrevIdx));
293
294        for (GLsizei i = 2; i < count; ++i)
295        {
296            T srcIdx;
297            memcpy(&srcIdx, indices + i, sizeof(srcIdx));
298
299            uint32_t triIndices[3];
300            triIndices[0] = triFirstIdx;
301            triIndices[1] = srcPrevIdx;
302            triIndices[2] = srcIdx;
303            srcPrevIdx    = srcIdx;
304
305            memcpy(dstPtr + 3 * dstTriangle, triIndices, sizeof(triIndices));
306            ++dstTriangle;
307        }
308    }
309    *indicesGenerated = dstTriangle * 3;
310    dstBuffer->unmapAndFlushSubset(contextMtl, dstOffset, *(indicesGenerated) * sizeof(uint32_t));
311
312    return angle::Result::Continue;
313}
314
315// Converts line loop vertices to line strip vertices.
316// Returns number of vertices written.
317template <typename In, typename Out>
318size_t CopyLineLoopIndices(GLsizei indexCount,
319                           const uint8_t *indices,
320                           bool usePrimitiveRestart,
321                           uint8_t *outIndices)
322{
323    if (usePrimitiveRestart)
324    {
325        return CopyLineLoopIndicesWithRestart<In, Out>(indexCount, indices, outIndices);
326    }
327    if (indexCount <= 0)
328    {
329        return 0;
330    }
331    In firstValue;
332    memcpy(&firstValue, indices, sizeof(In));
333    for (GLsizei i = 0; i < indexCount; ++i)
334    {
335        In value;
336        memcpy(&value, indices, sizeof(In));
337        indices += sizeof(In);
338        Out outValue = value;
339        memcpy(outIndices, &outValue, sizeof(Out));
340        outIndices += sizeof(Out);
341    }
342    Out outFirstValue = firstValue;
343    memcpy(outIndices, &outFirstValue, sizeof(Out));
344    return indexCount + 1;
345}
346
347template <typename T>
348void GetFirstLastIndicesFromClientElements(GLsizei count,
349                                           const T *indices,
350                                           uint32_t *firstOut,
351                                           uint32_t *lastOut)
352{
353    *firstOut = 0;
354    *lastOut  = 0;
355    memcpy(firstOut, indices, sizeof(indices[0]));
356    memcpy(lastOut, indices + count - 1, sizeof(indices[0]));
357}
358
359int GetShaderTextureType(const TextureRef &texture)
360{
361    if (!texture)
362    {
363        return -1;
364    }
365    switch (texture->textureType())
366    {
367        case MTLTextureType2D:
368            return mtl_shader::kTextureType2D;
369        case MTLTextureType2DArray:
370            return mtl_shader::kTextureType2DArray;
371        case MTLTextureType2DMultisample:
372            return mtl_shader::kTextureType2DMultisample;
373        case MTLTextureTypeCube:
374            return mtl_shader::kTextureTypeCube;
375        case MTLTextureType3D:
376            return mtl_shader::kTextureType3D;
377        default:
378            UNREACHABLE();
379    }
380
381    return 0;
382}
383
384int GetPixelTypeIndex(const angle::Format &angleFormat)
385{
386    if (angleFormat.isSint())
387    {
388        return static_cast<int>(PixelType::Int);
389    }
390    else if (angleFormat.isUint())
391    {
392        return static_cast<int>(PixelType::UInt);
393    }
394    else
395    {
396        return static_cast<int>(PixelType::Float);
397    }
398}
399
400angle::Result EnsureShaderInitialized(ContextMtl *context,
401                                      NSString *functionName,
402                                      angle::ObjCPtr<id<MTLFunction>> *shaderOut)
403{
404    angle::ObjCPtr<id<MTLFunction>> &shader = *shaderOut;
405    if (shader)
406    {
407        return angle::Result::Continue;
408    }
409
410    ANGLE_MTL_OBJC_SCOPE
411    {
412        auto shaderLib = context->getDisplay()->getDefaultShadersLib();
413        shader         = angle::adoptObjCPtr([shaderLib newFunctionWithName:functionName]);
414        ANGLE_CHECK(context, shader, gl::err::kInternalError, GL_INVALID_OPERATION);
415        return angle::Result::Continue;
416    }
417}
418
419angle::Result EnsureSpecializedShaderInitialized(ContextMtl *context,
420                                                 NSString *functionName,
421                                                 MTLFunctionConstantValues *funcConstants,
422                                                 angle::ObjCPtr<id<MTLFunction>> *shaderOut)
423{
424    if (!funcConstants)
425    {
426        // Non specialized constants provided, use default creation function.
427        return EnsureShaderInitialized(context, functionName, shaderOut);
428    }
429
430    angle::ObjCPtr<id<MTLFunction>> &shader = *shaderOut;
431    if (shader)
432    {
433        return angle::Result::Continue;
434    }
435
436    ANGLE_MTL_OBJC_SCOPE
437    {
438        auto shaderLib = context->getDisplay()->getDefaultShadersLib();
439        NSError *err   = nil;
440        shader         = angle::adoptObjCPtr([shaderLib newFunctionWithName:functionName
441                                                     constantValues:funcConstants
442                                                              error:&err]);
443        ANGLE_MTL_CHECK(context, shader, err);
444        return angle::Result::Continue;
445    }
446}
447
448// Get pipeline descriptor for render pipeline that contains vertex shader acting as compute shader.
449ANGLE_INLINE
450RenderPipelineDesc GetComputingVertexShaderOnlyRenderPipelineDesc(RenderCommandEncoder *cmdEncoder)
451{
452    RenderPipelineDesc pipelineDesc;
453    const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
454
455    renderPassDesc.populateRenderPipelineOutputDesc(&pipelineDesc.outputDescriptor);
456    pipelineDesc.rasterizationType      = RenderPipelineRasterization::Disabled;
457    pipelineDesc.inputPrimitiveTopology = MTLPrimitiveTopologyClassPoint;
458
459    return pipelineDesc;
460}
461
462// Dispatch compute using 3D grid
463void DispatchCompute(ContextMtl *contextMtl,
464                     ComputeCommandEncoder *encoder,
465                     bool allowNonUniform,
466                     const MTLSize &numThreads,
467                     const MTLSize &threadsPerThreadgroup)
468{
469    if (allowNonUniform && contextMtl->getDisplay()->getFeatures().hasNonUniformDispatch.enabled)
470    {
471        encoder->dispatchNonUniform(numThreads, threadsPerThreadgroup);
472    }
473    else
474    {
475        MTLSize groups = MTLSizeMake(
476            (numThreads.width + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
477            (numThreads.height + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height,
478            (numThreads.depth + threadsPerThreadgroup.depth - 1) / threadsPerThreadgroup.depth);
479        encoder->dispatch(groups, threadsPerThreadgroup);
480    }
481}
482
483// Dispatch compute using 1D grid
484void DispatchCompute(ContextMtl *contextMtl,
485                     ComputeCommandEncoder *cmdEncoder,
486                     id<MTLComputePipelineState> pipelineState,
487                     size_t numThreads)
488{
489    NSUInteger w = std::min<NSUInteger>(pipelineState.threadExecutionWidth, numThreads);
490    MTLSize threadsPerThreadgroup = MTLSizeMake(w, 1, 1);
491
492    if (contextMtl->getDisplay()->getFeatures().hasNonUniformDispatch.enabled)
493    {
494        MTLSize threads = MTLSizeMake(numThreads, 1, 1);
495        cmdEncoder->dispatchNonUniform(threads, threadsPerThreadgroup);
496    }
497    else
498    {
499        MTLSize groups = MTLSizeMake((numThreads + w - 1) / w, 1, 1);
500        cmdEncoder->dispatch(groups, threadsPerThreadgroup);
501    }
502}
503
504void SetupFullscreenQuadDrawCommonStates(RenderCommandEncoder *cmdEncoder)
505{
506    cmdEncoder->setCullMode(MTLCullModeNone);
507    cmdEncoder->setTriangleFillMode(MTLTriangleFillModeFill);
508    cmdEncoder->setDepthBias(0, 0, 0);
509}
510
511void SetupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder,
512                                  const BlitParams &params,
513                                  bool isColorBlit)
514{
515    // To ensure consistent texture coordinate interpolation on Apple silicon, a two-triangle quad
516    // with the common edge going from upper-left to lower-right must be used. Any other primitive,
517    // e.g., a clipped triangle, would produce various texture sampling artifacts on that hardware.
518
519    BlitParamsUniform uniformParams;
520    uniformParams.srcLevel = params.srcLevel.get();
521    uniformParams.srcLayer = params.srcLayer;
522    if (isColorBlit)
523    {
524        const ColorBlitParams *colorParams = static_cast<const ColorBlitParams *>(&params);
525        uniformParams.dstLuminance         = colorParams->dstLuminance ? 1 : 0;
526    }
527
528    float u0, v0, u1, v1;
529    GetBlitTexCoords(params.srcNormalizedCoords, params.srcYFlipped, params.unpackFlipX,
530                     params.unpackFlipY, &u0, &v0, &u1, &v1);
531
532    if (params.dstFlipX)
533    {
534        std::swap(u0, u1);
535    }
536
537    // If viewport is not flipped, we have to flip Y in normalized device coordinates
538    // since NDC has Y in the opposite direction of viewport coodrinates.
539    // To keep the common edge properly oriented, swap the texture coordinates instead.
540    if (!params.dstFlipY)
541    {
542        std::swap(v0, v1);
543    }
544
545    // lower left
546    uniformParams.srcTexCoords[0][0] = u0;
547    uniformParams.srcTexCoords[0][1] = v0;
548    // upper right
549    uniformParams.srcTexCoords[1][0] = u1;
550    uniformParams.srcTexCoords[1][1] = v1;
551
552    cmdEncoder->setVertexData(uniformParams, 0);
553    cmdEncoder->setFragmentData(uniformParams, 0);
554}
555
556void SetupCommonBlitWithDrawStates(const gl::Context *context,
557                                   RenderCommandEncoder *cmdEncoder,
558                                   const BlitParams &params,
559                                   bool isColorBlit)
560{
561    // Setup states
562    SetupFullscreenQuadDrawCommonStates(cmdEncoder);
563
564    // Viewport
565    MTLViewport viewportMtl =
566        GetViewport(params.dstRect, params.dstTextureSize.height, params.dstFlipY);
567    MTLScissorRect scissorRectMtl =
568        GetScissorRect(params.dstScissorRect, params.dstTextureSize.height, params.dstFlipY);
569    cmdEncoder->setViewport(viewportMtl);
570    cmdEncoder->setScissorRect(scissorRectMtl);
571
572    if (params.src)
573    {
574        cmdEncoder->setFragmentTexture(params.src, 0);
575    }
576
577    // Uniform
578    SetupBlitWithDrawUniformData(cmdEncoder, params, isColorBlit);
579}
580
581// Overloaded functions to be used with both compute and render command encoder.
582ANGLE_INLINE void SetComputeOrVertexBuffer(RenderCommandEncoder *encoder,
583                                           const BufferRef &buffer,
584                                           uint32_t offset,
585                                           uint32_t index)
586{
587    encoder->setBuffer(gl::ShaderType::Vertex, buffer, offset, index);
588}
589ANGLE_INLINE void SetComputeOrVertexBufferForWrite(RenderCommandEncoder *encoder,
590                                                   const BufferRef &buffer,
591                                                   uint32_t offset,
592                                                   uint32_t index)
593{
594    encoder->setBufferForWrite(gl::ShaderType::Vertex, buffer, offset, index);
595}
596ANGLE_INLINE void SetComputeOrVertexBuffer(ComputeCommandEncoder *encoder,
597                                           const BufferRef &buffer,
598                                           uint32_t offset,
599                                           uint32_t index)
600{
601    encoder->setBuffer(buffer, offset, index);
602}
603ANGLE_INLINE void SetComputeOrVertexBufferForWrite(ComputeCommandEncoder *encoder,
604                                                   const BufferRef &buffer,
605                                                   uint32_t offset,
606                                                   uint32_t index)
607{
608    encoder->setBufferForWrite(buffer, offset, index);
609}
610
611template <typename T>
612ANGLE_INLINE void SetComputeOrVertexData(RenderCommandEncoder *encoder,
613                                         const T &data,
614                                         uint32_t index)
615{
616    encoder->setData(gl::ShaderType::Vertex, data, index);
617}
618template <typename T>
619ANGLE_INLINE void SetComputeOrVertexData(ComputeCommandEncoder *encoder,
620                                         const T &data,
621                                         uint32_t index)
622{
623    encoder->setData(data, index);
624}
625
626ANGLE_INLINE void SetPipelineState(RenderCommandEncoder *encoder,
627                                   id<MTLRenderPipelineState> pipeline)
628{
629    encoder->setRenderPipelineState(pipeline);
630}
631ANGLE_INLINE void SetPipelineState(ComputeCommandEncoder *encoder,
632                                   id<MTLComputePipelineState> pipeline)
633{
634    encoder->setComputePipelineState(pipeline);
635}
636
637}  // namespace
638
639NormalizedCoords::NormalizedCoords() : v{0.0f, 0.0f, 1.0f, 1.0f} {}
640
641NormalizedCoords::NormalizedCoords(float x,
642                                   float y,
643                                   float width,
644                                   float height,
645                                   const gl::Rectangle &rect)
646    : v{
647          x / rect.width,
648          y / rect.height,
649          (x + width) / rect.width,
650          (y + height) / rect.height,
651      }
652{}
653
654NormalizedCoords::NormalizedCoords(const gl::Rectangle &rect, const gl::Extents &extents)
655    : v{
656          static_cast<float>(rect.x0()) / extents.width,
657          static_cast<float>(rect.y0()) / extents.height,
658          static_cast<float>(rect.x1()) / extents.width,
659          static_cast<float>(rect.y1()) / extents.height,
660      }
661{}
662
663// StencilBlitViaBufferParams implementation
664StencilBlitViaBufferParams::StencilBlitViaBufferParams() {}
665
666StencilBlitViaBufferParams::StencilBlitViaBufferParams(const DepthStencilBlitParams &srcParams)
667{
668    dstTextureSize      = srcParams.dstTextureSize;
669    dstRect             = srcParams.dstRect;
670    dstScissorRect      = srcParams.dstScissorRect;
671    dstFlipY            = srcParams.dstFlipY;
672    dstFlipX            = srcParams.dstFlipX;
673    srcNormalizedCoords = srcParams.srcNormalizedCoords;
674    srcYFlipped         = srcParams.srcYFlipped;
675    unpackFlipX         = srcParams.unpackFlipX;
676    unpackFlipY         = srcParams.unpackFlipY;
677
678    srcStencil = srcParams.srcStencil;
679}
680
681// RenderUtils implementation
682RenderUtils::RenderUtils()
683    : mClearUtils{ClearUtils("clearIntFS"), ClearUtils("clearUIntFS"), ClearUtils("clearFloatFS")},
684      mColorBlitUtils{ColorBlitUtils("blitIntFS"), ColorBlitUtils("blitUIntFS"),
685                      ColorBlitUtils("blitFloatFS")},
686      mCopyTextureFloatToUIntUtils("copyTextureFloatToUIntFS"),
687      mCopyPixelsUtils{
688          CopyPixelsUtils("readFromBufferToIntTexture", "writeFromIntTextureToBuffer"),
689          CopyPixelsUtils("readFromBufferToUIntTexture", "writeFromUIntTextureToBuffer"),
690          CopyPixelsUtils("readFromBufferToFloatTexture", "writeFromFloatTextureToBuffer")}
691{}
692
693// Clear current framebuffer
694angle::Result RenderUtils::clearWithDraw(const gl::Context *context,
695                                         RenderCommandEncoder *cmdEncoder,
696                                         const ClearRectParams &params)
697{
698    int index = 0;
699    if (params.clearColor.valid())
700    {
701        index = static_cast<int>(params.clearColor.value().getType());
702    }
703    else if (params.colorFormat)
704    {
705        index = GetPixelTypeIndex(params.colorFormat->actualAngleFormat());
706    }
707    return mClearUtils[index].clearWithDraw(context, cmdEncoder, params);
708}
709
710// Blit texture data to current framebuffer
711angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
712                                             RenderCommandEncoder *cmdEncoder,
713                                             const angle::Format &srcAngleFormat,
714                                             const ColorBlitParams &params)
715{
716    int index = GetPixelTypeIndex(srcAngleFormat);
717    return mColorBlitUtils[index].blitColorWithDraw(context, cmdEncoder, params);
718}
719
720angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
721                                             RenderCommandEncoder *cmdEncoder,
722                                             const angle::Format &srcAngleFormat,
723                                             const TextureRef &srcTexture)
724{
725    if (!srcTexture)
726    {
727        return angle::Result::Continue;
728    }
729    ColorBlitParams params;
730    params.enabledBuffers.set(0);
731    params.src            = srcTexture;
732    params.dstTextureSize = gl::Extents(static_cast<int>(srcTexture->widthAt0()),
733                                        static_cast<int>(srcTexture->heightAt0()),
734                                        static_cast<int>(srcTexture->depthAt0()));
735    params.dstRect        = params.dstScissorRect =
736        gl::Rectangle(0, 0, params.dstTextureSize.width, params.dstTextureSize.height);
737    params.srcNormalizedCoords = NormalizedCoords();
738
739    return blitColorWithDraw(context, cmdEncoder, srcAngleFormat, params);
740}
741
742angle::Result RenderUtils::copyTextureWithDraw(const gl::Context *context,
743                                               RenderCommandEncoder *cmdEncoder,
744                                               const angle::Format &srcAngleFormat,
745                                               const angle::Format &dstAngleFormat,
746                                               const ColorBlitParams &params)
747{
748    if (!srcAngleFormat.isInt() && dstAngleFormat.isUint())
749    {
750        return mCopyTextureFloatToUIntUtils.blitColorWithDraw(context, cmdEncoder, params);
751    }
752    ASSERT(srcAngleFormat.isSint() == dstAngleFormat.isSint() &&
753           srcAngleFormat.isUint() == dstAngleFormat.isUint());
754    return blitColorWithDraw(context, cmdEncoder, srcAngleFormat, params);
755}
756
757angle::Result RenderUtils::blitDepthStencilWithDraw(const gl::Context *context,
758                                                    RenderCommandEncoder *cmdEncoder,
759                                                    const DepthStencilBlitParams &params)
760{
761    return mDepthStencilBlitUtils.blitDepthStencilWithDraw(context, cmdEncoder, params);
762}
763
764angle::Result RenderUtils::blitStencilViaCopyBuffer(const gl::Context *context,
765                                                    const StencilBlitViaBufferParams &params)
766{
767    return mDepthStencilBlitUtils.blitStencilViaCopyBuffer(context, params);
768}
769
770angle::Result RenderUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
771                                                 const IndexConversionParams &params)
772{
773    return mIndexUtils.convertIndexBufferGPU(contextMtl, params);
774}
775angle::Result RenderUtils::generateTriFanBufferFromArrays(
776    ContextMtl *contextMtl,
777    const TriFanOrLineLoopFromArrayParams &params)
778{
779    return mIndexUtils.generateTriFanBufferFromArrays(contextMtl, params);
780}
781angle::Result RenderUtils::generateTriFanBufferFromElementsArray(
782    ContextMtl *contextMtl,
783    const IndexGenerationParams &params,
784    uint32_t *indicesGenerated)
785{
786    return mIndexUtils.generateTriFanBufferFromElementsArray(contextMtl, params, indicesGenerated);
787}
788
789angle::Result RenderUtils::generateLineLoopBufferFromArrays(
790    ContextMtl *contextMtl,
791    const TriFanOrLineLoopFromArrayParams &params)
792{
793    return mIndexUtils.generateLineLoopBufferFromArrays(contextMtl, params);
794}
795angle::Result RenderUtils::generateLineLoopLastSegment(ContextMtl *contextMtl,
796                                                       uint32_t firstVertex,
797                                                       uint32_t lastVertex,
798                                                       const BufferRef &dstBuffer,
799                                                       uint32_t dstOffset)
800{
801    return mIndexUtils.generateLineLoopLastSegment(contextMtl, firstVertex, lastVertex, dstBuffer,
802                                                   dstOffset);
803}
804angle::Result RenderUtils::generateLineLoopBufferFromElementsArray(
805    ContextMtl *contextMtl,
806    const IndexGenerationParams &params,
807    uint32_t *indicesGenerated)
808{
809    return mIndexUtils.generateLineLoopBufferFromElementsArray(contextMtl, params,
810                                                               indicesGenerated);
811}
812angle::Result RenderUtils::generateLineLoopLastSegmentFromElementsArray(
813    ContextMtl *contextMtl,
814    const IndexGenerationParams &params)
815{
816    return mIndexUtils.generateLineLoopLastSegmentFromElementsArray(contextMtl, params);
817}
818
819void RenderUtils::combineVisibilityResult(
820    ContextMtl *contextMtl,
821    bool keepOldValue,
822    const VisibilityBufferOffsetsMtl &renderPassResultBufOffsets,
823    const BufferRef &renderPassResultBuf,
824    const BufferRef &finalResultBuf)
825{
826    // TODO(geofflang): Propagate this error. It spreads to adding angle::Result return values in
827    // most of the metal backend's files.
828    (void)mVisibilityResultUtils.combineVisibilityResult(
829        contextMtl, keepOldValue, renderPassResultBufOffsets, renderPassResultBuf, finalResultBuf);
830}
831
832// Compute based mipmap generation
833angle::Result RenderUtils::generateMipmapCS(ContextMtl *contextMtl,
834                                            const TextureRef &srcTexture,
835                                            bool sRGBMipmap,
836                                            NativeTexLevelArray *mipmapOutputViews)
837{
838    return mMipmapUtils.generateMipmapCS(contextMtl, srcTexture, sRGBMipmap, mipmapOutputViews);
839}
840
841angle::Result RenderUtils::unpackPixelsFromBufferToTexture(ContextMtl *contextMtl,
842                                                           const angle::Format &srcAngleFormat,
843                                                           const CopyPixelsFromBufferParams &params)
844{
845    int index = GetPixelTypeIndex(srcAngleFormat);
846    return mCopyPixelsUtils[index].unpackPixelsFromBufferToTexture(contextMtl, srcAngleFormat,
847                                                                   params);
848}
849angle::Result RenderUtils::packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
850                                                         const angle::Format &dstAngleFormat,
851                                                         const CopyPixelsToBufferParams &params)
852{
853    int index = GetPixelTypeIndex(dstAngleFormat);
854    return mCopyPixelsUtils[index].packPixelsFromTextureToBuffer(contextMtl, dstAngleFormat,
855                                                                 params);
856}
857
858angle::Result RenderUtils::convertVertexFormatToFloatCS(ContextMtl *contextMtl,
859                                                        const angle::Format &srcAngleFormat,
860                                                        const VertexFormatConvertParams &params)
861{
862    return mVertexFormatUtils.convertVertexFormatToFloatCS(contextMtl, srcAngleFormat, params);
863}
864
865angle::Result RenderUtils::convertVertexFormatToFloatVS(const gl::Context *context,
866                                                        RenderCommandEncoder *encoder,
867                                                        const angle::Format &srcAngleFormat,
868                                                        const VertexFormatConvertParams &params)
869{
870    return mVertexFormatUtils.convertVertexFormatToFloatVS(context, encoder, srcAngleFormat,
871                                                           params);
872}
873
874// Expand number of components per vertex's attribute
875angle::Result RenderUtils::expandVertexFormatComponentsCS(ContextMtl *contextMtl,
876                                                          const angle::Format &srcAngleFormat,
877                                                          const VertexFormatConvertParams &params)
878{
879    return mVertexFormatUtils.expandVertexFormatComponentsCS(contextMtl, srcAngleFormat, params);
880}
881
882angle::Result RenderUtils::expandVertexFormatComponentsVS(const gl::Context *context,
883                                                          RenderCommandEncoder *encoder,
884                                                          const angle::Format &srcAngleFormat,
885                                                          const VertexFormatConvertParams &params)
886{
887    return mVertexFormatUtils.expandVertexFormatComponentsVS(context, encoder, srcAngleFormat,
888                                                             params);
889}
890
891angle::Result RenderUtils::linearizeBlocks(ContextMtl *contextMtl,
892                                           const BlockLinearizationParams &params)
893{
894    return mBlockLinearizationUtils.linearizeBlocks(contextMtl, params);
895}
896
897angle::Result RenderUtils::saturateDepth(ContextMtl *contextMtl,
898                                         const DepthSaturationParams &params)
899{
900    return mDepthSaturationUtils.saturateDepth(contextMtl, params);
901}
902
903// ClearUtils implementation
904ClearUtils::ClearUtils(const std::string &fragmentShaderName)
905    : mFragmentShaderName(fragmentShaderName)
906{}
907
908angle::Result ClearUtils::ensureShadersInitialized(ContextMtl *ctx, uint32_t numOutputs)
909{
910    ANGLE_MTL_OBJC_SCOPE
911    {
912        if (!mVertexShader)
913        {
914            id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
915            mVertexShader = angle::adoptObjCPtr([shaderLib newFunctionWithName:@"clearVS"]);
916            ANGLE_CHECK(ctx, mVertexShader, gl::err::kInternalError, GL_INVALID_OPERATION);
917        }
918
919        if (!mFragmentShaders[numOutputs])
920        {
921            NSError *err             = nil;
922            id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
923            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
924                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
925
926            // Create clear shader for each number of color outputs.
927            // So clear k color outputs will use mFragmentShaders[k] for example:
928            [funcConstants setConstantValue:&numOutputs
929                                       type:MTLDataTypeUInt
930                                   withName:NUM_COLOR_OUTPUTS_CONSTANT_NAME];
931
932            mFragmentShaders[numOutputs] = angle::adoptObjCPtr([shaderLib
933                newFunctionWithName:[NSString stringWithUTF8String:mFragmentShaderName.c_str()]
934                     constantValues:funcConstants
935                              error:&err]);
936            ANGLE_MTL_CHECK(ctx, mFragmentShaders[numOutputs], err);
937        }
938        return angle::Result::Continue;
939    }
940}
941
942id<MTLDepthStencilState> ClearUtils::getClearDepthStencilState(const gl::Context *context,
943                                                               const ClearRectParams &params)
944{
945    ContextMtl *contextMtl = GetImpl(context);
946
947    if (!params.clearDepth.valid() && !params.clearStencil.valid())
948    {
949        // Doesn't clear depth nor stencil
950        return contextMtl->getDisplay()->getStateCache().getNullDepthStencilState(
951            contextMtl->getMetalDevice());
952    }
953
954    DepthStencilDesc desc;
955    desc.reset();
956
957    if (params.clearDepth.valid())
958    {
959        // Clear depth state
960        desc.depthWriteEnabled = true;
961    }
962    else
963    {
964        desc.depthWriteEnabled = false;
965    }
966
967    if (params.clearStencil.valid())
968    {
969        // Clear stencil state
970        desc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationReplace;
971        desc.frontFaceStencil.writeMask                 = contextMtl->getStencilMask();
972        desc.backFaceStencil.depthStencilPassOperation  = MTLStencilOperationReplace;
973        desc.backFaceStencil.writeMask                  = contextMtl->getStencilMask();
974    }
975
976    return contextMtl->getDisplay()->getStateCache().getDepthStencilState(
977        contextMtl->getMetalDevice(), desc);
978}
979
980angle::Result ClearUtils::getClearRenderPipelineState(
981    const gl::Context *context,
982    RenderCommandEncoder *cmdEncoder,
983    const ClearRectParams &params,
984    angle::ObjCPtr<id<MTLRenderPipelineState>> *outPipelineState)
985{
986    ContextMtl *contextMtl = GetImpl(context);
987    // The color mask to be applied to every color attachment:
988    WriteMaskArray clearWriteMaskArray = params.clearWriteMaskArray;
989    if (!params.clearColor.valid())
990    {
991        clearWriteMaskArray.fill(MTLColorWriteMaskNone);
992    }
993    else
994    {
995        // Adjust masks for disabled outputs before creating a pipeline.
996        gl::DrawBufferMask disabledBuffers(params.enabledBuffers);
997        for (size_t index : disabledBuffers.flip())
998        {
999            clearWriteMaskArray[index] = MTLColorWriteMaskNone;
1000        }
1001    }
1002
1003    RenderPipelineDesc pipelineDesc;
1004    const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
1005
1006    renderPassDesc.populateRenderPipelineOutputDesc(clearWriteMaskArray,
1007                                                    &pipelineDesc.outputDescriptor);
1008
1009    pipelineDesc.inputPrimitiveTopology = MTLPrimitiveTopologyClassTriangle;
1010
1011    ANGLE_TRY(ensureShadersInitialized(contextMtl, renderPassDesc.numColorAttachments));
1012
1013    return contextMtl->getPipelineCache().getRenderPipeline(
1014        contextMtl, mVertexShader, mFragmentShaders[renderPassDesc.numColorAttachments],
1015        pipelineDesc, outPipelineState);
1016}
1017
1018angle::Result ClearUtils::setupClearWithDraw(const gl::Context *context,
1019                                             RenderCommandEncoder *cmdEncoder,
1020                                             const ClearRectParams &params)
1021{
1022    // Generate render pipeline state
1023    angle::ObjCPtr<id<MTLRenderPipelineState>> renderPipelineState;
1024    ANGLE_TRY(getClearRenderPipelineState(context, cmdEncoder, params, &renderPipelineState));
1025
1026    // Setup states
1027    SetupFullscreenQuadDrawCommonStates(cmdEncoder);
1028    cmdEncoder->setRenderPipelineState(renderPipelineState);
1029
1030    id<MTLDepthStencilState> dsState = getClearDepthStencilState(context, params);
1031    cmdEncoder->setDepthStencilState(dsState).setStencilRefVal(params.clearStencil.value());
1032
1033    // Viewports
1034    MTLViewport viewport;
1035    MTLScissorRect scissorRect;
1036
1037    viewport = GetViewport(params.clearArea, params.dstTextureSize.height, params.flipY);
1038
1039    scissorRect = GetScissorRect(params.clearArea, params.dstTextureSize.height, params.flipY);
1040
1041    cmdEncoder->setViewport(viewport);
1042    cmdEncoder->setScissorRect(scissorRect);
1043
1044    // uniform
1045    ClearParamsUniform uniformParams;
1046    const ClearColorValue &clearValue = params.clearColor.value();
1047    // ClearColorValue is an int, uint, float union so it's safe to use only floats.
1048    // The Shader will do the bit cast based on appropriate format type.
1049    // See shaders/clear.metal (3 variants ClearFloatFS, ClearIntFS and ClearUIntFS each does the
1050    // appropriate bit cast)
1051    ASSERT(sizeof(uniformParams.clearColor) == clearValue.getValueBytes().size());
1052    std::memcpy(uniformParams.clearColor, clearValue.getValueBytes().data(),
1053                clearValue.getValueBytes().size());
1054    uniformParams.clearDepth = params.clearDepth.value();
1055
1056    cmdEncoder->setVertexData(uniformParams, 0);
1057    cmdEncoder->setFragmentData(uniformParams, 0);
1058    return angle::Result::Continue;
1059}
1060
1061angle::Result ClearUtils::clearWithDraw(const gl::Context *context,
1062                                        RenderCommandEncoder *cmdEncoder,
1063                                        const ClearRectParams &params)
1064{
1065    ClearRectParams overridedParams = params;
1066    // Make sure we don't clear attachment that doesn't exist
1067    const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
1068    if (renderPassDesc.numColorAttachments == 0)
1069    {
1070        overridedParams.clearColor.reset();
1071    }
1072    if (!renderPassDesc.depthAttachment.texture)
1073    {
1074        overridedParams.clearDepth.reset();
1075    }
1076    if (!renderPassDesc.stencilAttachment.texture)
1077    {
1078        overridedParams.clearStencil.reset();
1079    }
1080
1081    if (!overridedParams.clearColor.valid() && !overridedParams.clearDepth.valid() &&
1082        !overridedParams.clearStencil.valid())
1083    {
1084        return angle::Result::Continue;
1085    }
1086    ContextMtl *contextMtl = GetImpl(context);
1087    ANGLE_TRY(setupClearWithDraw(context, cmdEncoder, overridedParams));
1088
1089    angle::Result result;
1090    {
1091        // Need to disable occlusion query, otherwise clearing will affect the occlusion counting
1092        ScopedDisableOcclusionQuery disableOcclusionQuery(contextMtl, cmdEncoder, &result);
1093        // Draw the screen aligned triangle
1094        cmdEncoder->draw(MTLPrimitiveTypeTriangle, 0, 3);
1095    }
1096
1097    // Invalidate current context's state
1098    contextMtl->invalidateState(context);
1099
1100    return result;
1101}
1102
1103// ColorBlitUtils implementation
1104ColorBlitUtils::ColorBlitUtils(const std::string &fragmentShaderName)
1105    : mFragmentShaderName(fragmentShaderName)
1106{}
1107
1108angle::Result ColorBlitUtils::ensureShadersInitialized(
1109    ContextMtl *ctx,
1110    const ShaderKey &key,
1111    angle::ObjCPtr<id<MTLFunction>> *fragmentShaderOut)
1112{
1113    ANGLE_MTL_OBJC_SCOPE
1114    {
1115        if (!mVertexShader)
1116        {
1117            id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
1118            mVertexShader = angle::adoptObjCPtr([shaderLib newFunctionWithName:@"blitVS"]);
1119            ANGLE_CHECK(ctx, mVertexShader, gl::err::kInternalError, GL_INVALID_OPERATION);
1120        }
1121
1122        if (!(*fragmentShaderOut))
1123        {
1124            NSError *err             = nil;
1125            id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
1126            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
1127                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
1128
1129            bool unmultiplyAlpha       = key.unmultiplyAlpha;
1130            bool premultiplyAlpha      = key.premultiplyAlpha;
1131            bool transformLinearToSrgb = key.transformLinearToSrgb;
1132            // Set alpha multiply flags
1133            [funcConstants setConstantValue:&unmultiplyAlpha
1134                                       type:MTLDataTypeBool
1135                                   withName:UNMULTIPLY_ALPHA_CONSTANT_NAME];
1136            [funcConstants setConstantValue:&premultiplyAlpha
1137                                       type:MTLDataTypeBool
1138                                   withName:PREMULTIPLY_ALPHA_CONSTANT_NAME];
1139            [funcConstants setConstantValue:&transformLinearToSrgb
1140                                       type:MTLDataTypeBool
1141                                   withName:TRANSFORM_LINEAR_TO_SRGB_CONSTANT_NAME];
1142
1143            uint32_t numColorAttachments = key.numColorAttachments;
1144            // We create blit shader pipeline cache for each number of color outputs.
1145            // So blit k color outputs will use mBlitRenderPipelineCache[k-1] for example:
1146            [funcConstants setConstantValue:&numColorAttachments
1147                                       type:MTLDataTypeUInt
1148                                   withName:NUM_COLOR_OUTPUTS_CONSTANT_NAME];
1149
1150            // Set texture type constant
1151            [funcConstants setConstantValue:&key.sourceTextureType
1152                                       type:MTLDataTypeInt
1153                                   withName:SOURCE_TEXTURE_TYPE_CONSTANT_NAME];
1154
1155            *fragmentShaderOut = angle::adoptObjCPtr([shaderLib
1156                newFunctionWithName:[NSString stringWithUTF8String:mFragmentShaderName.c_str()]
1157                     constantValues:funcConstants
1158                              error:&err]);
1159            ANGLE_MTL_CHECK(ctx, *fragmentShaderOut, err);
1160        }
1161        return angle::Result::Continue;
1162    }
1163}
1164
1165angle::Result ColorBlitUtils::getColorBlitRenderPipelineState(
1166    const gl::Context *context,
1167    RenderCommandEncoder *cmdEncoder,
1168    const ColorBlitParams &params,
1169    angle::ObjCPtr<id<MTLRenderPipelineState>> *outPipelineState)
1170{
1171    ContextMtl *contextMtl = GetImpl(context);
1172    RenderPipelineDesc pipelineDesc;
1173    const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
1174
1175    renderPassDesc.populateRenderPipelineOutputDesc(&pipelineDesc.outputDescriptor);
1176
1177    // Disable blit for some outputs that are not enabled
1178    pipelineDesc.outputDescriptor.updateEnabledDrawBuffers(params.enabledBuffers);
1179
1180    pipelineDesc.inputPrimitiveTopology = MTLPrimitiveTopologyClassTriangle;
1181
1182    ShaderKey key(GetShaderTextureType(params.src), renderPassDesc.numColorAttachments,
1183                  params.unpackUnmultiplyAlpha, params.unpackPremultiplyAlpha,
1184                  params.transformLinearToSrgb);
1185
1186    angle::ObjCPtr<id<MTLFunction>> *fragmentShader = &mBlitFragmentShaders[key];
1187    ANGLE_TRY(ensureShadersInitialized(contextMtl, key, fragmentShader));
1188
1189    return contextMtl->getPipelineCache().getRenderPipeline(
1190        contextMtl, mVertexShader, *fragmentShader, pipelineDesc, outPipelineState);
1191}
1192
1193angle::Result ColorBlitUtils::setupColorBlitWithDraw(const gl::Context *context,
1194                                                     RenderCommandEncoder *cmdEncoder,
1195                                                     const ColorBlitParams &params)
1196{
1197    ASSERT(cmdEncoder->renderPassDesc().numColorAttachments >= 1 && params.src);
1198
1199    ContextMtl *contextMtl = mtl::GetImpl(context);
1200
1201    // Generate render pipeline state
1202    angle::ObjCPtr<id<MTLRenderPipelineState>> renderPipelineState;
1203    ANGLE_TRY(getColorBlitRenderPipelineState(context, cmdEncoder, params, &renderPipelineState));
1204
1205    // Setup states
1206    cmdEncoder->setRenderPipelineState(renderPipelineState);
1207    cmdEncoder->setDepthStencilState(
1208        contextMtl->getDisplay()->getStateCache().getNullDepthStencilState(
1209            contextMtl->getMetalDevice()));
1210
1211    SetupCommonBlitWithDrawStates(context, cmdEncoder, params, true);
1212
1213    // Set sampler state
1214    SamplerDesc samplerDesc;
1215    samplerDesc.reset();
1216    samplerDesc.minFilter = samplerDesc.magFilter = GetFilter(params.filter);
1217
1218    cmdEncoder->setFragmentSamplerState(contextMtl->getDisplay()->getStateCache().getSamplerState(
1219                                            contextMtl->getMetalDevice(), samplerDesc),
1220                                        0, FLT_MAX, 0);
1221    return angle::Result::Continue;
1222}
1223
1224angle::Result ColorBlitUtils::blitColorWithDraw(const gl::Context *context,
1225                                                RenderCommandEncoder *cmdEncoder,
1226                                                const ColorBlitParams &params)
1227{
1228    if (!params.src)
1229    {
1230        return angle::Result::Continue;
1231    }
1232    ContextMtl *contextMtl = GetImpl(context);
1233    ANGLE_TRY(setupColorBlitWithDraw(context, cmdEncoder, params));
1234
1235    angle::Result result;
1236    {
1237        // Need to disable occlusion query, otherwise blitting will affect the occlusion counting
1238        ScopedDisableOcclusionQuery disableOcclusionQuery(contextMtl, cmdEncoder, &result);
1239        // Draw the screen aligned quad
1240        cmdEncoder->draw(MTLPrimitiveTypeTriangleStrip, 0, 4);
1241    }
1242
1243    // Invalidate current context's state
1244    contextMtl->invalidateState(context);
1245
1246    return result;
1247}
1248
1249angle::Result DepthStencilBlitUtils::ensureShadersInitialized(
1250    ContextMtl *ctx,
1251    int sourceDepthTextureType,
1252    int sourceStencilTextureType,
1253    angle::ObjCPtr<id<MTLFunction>> *fragmentShaderOut)
1254{
1255
1256    ANGLE_MTL_OBJC_SCOPE
1257    {
1258        if (!mVertexShader)
1259        {
1260            id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
1261            mVertexShader = angle::adoptObjCPtr([shaderLib newFunctionWithName:@"blitVS"]);
1262            ANGLE_CHECK(ctx, mVertexShader, gl::err::kInternalError, GL_INVALID_OPERATION);
1263        }
1264
1265        if (!(*fragmentShaderOut))
1266        {
1267            NSError *err             = nil;
1268            id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
1269            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
1270                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
1271            NSString *shaderName;
1272            if (sourceDepthTextureType != -1 && sourceStencilTextureType != -1)
1273            {
1274                shaderName = @"blitDepthStencilFS";
1275            }
1276            else if (sourceDepthTextureType != -1)
1277            {
1278                shaderName = @"blitDepthFS";
1279            }
1280            else
1281            {
1282                shaderName = @"blitStencilFS";
1283            }
1284
1285            if (sourceDepthTextureType != -1)
1286            {
1287                [funcConstants setConstantValue:&sourceDepthTextureType
1288                                           type:MTLDataTypeInt
1289                                       withName:SOURCE_TEXTURE_TYPE_CONSTANT_NAME];
1290            }
1291            if (sourceStencilTextureType != -1)
1292            {
1293
1294                [funcConstants setConstantValue:&sourceStencilTextureType
1295                                           type:MTLDataTypeInt
1296                                       withName:SOURCE_TEXTURE2_TYPE_CONSTANT_NAME];
1297            }
1298
1299            *fragmentShaderOut = angle::adoptObjCPtr([shaderLib newFunctionWithName:shaderName
1300                                                                     constantValues:funcConstants
1301                                                                              error:&err]);
1302            ANGLE_MTL_CHECK(ctx, *fragmentShaderOut, err);
1303        }
1304
1305        return angle::Result::Continue;
1306    }
1307}
1308
1309angle::Result DepthStencilBlitUtils::getStencilToBufferComputePipelineState(
1310    ContextMtl *contextMtl,
1311    const StencilBlitViaBufferParams &params,
1312    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipelineState)
1313{
1314    int sourceStencilTextureType = GetShaderTextureType(params.srcStencil);
1315    angle::ObjCPtr<id<MTLFunction>> &shader =
1316        mStencilBlitToBufferComputeShaders[sourceStencilTextureType];
1317    if (!shader)
1318    {
1319        ANGLE_MTL_OBJC_SCOPE
1320        {
1321            auto shaderLib = contextMtl->getDisplay()->getDefaultShadersLib();
1322            NSError *err   = nil;
1323            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
1324                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
1325
1326            [funcConstants setConstantValue:&sourceStencilTextureType
1327                                       type:MTLDataTypeInt
1328                                   withName:SOURCE_TEXTURE2_TYPE_CONSTANT_NAME];
1329
1330            shader = angle::adoptObjCPtr([shaderLib newFunctionWithName:@"blitStencilToBufferCS"
1331                                                         constantValues:funcConstants
1332                                                                  error:&err]);
1333            ANGLE_MTL_CHECK(contextMtl, shader, err);
1334        }
1335    }
1336
1337    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, shader,
1338                                                             outComputePipelineState);
1339}
1340
1341angle::Result DepthStencilBlitUtils::getDepthStencilBlitRenderPipelineState(
1342    const gl::Context *context,
1343    RenderCommandEncoder *cmdEncoder,
1344    const DepthStencilBlitParams &params,
1345    angle::ObjCPtr<id<MTLRenderPipelineState>> *outRenderPipelineState)
1346{
1347    ContextMtl *contextMtl = GetImpl(context);
1348    RenderPipelineDesc pipelineDesc;
1349    const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
1350
1351    renderPassDesc.populateRenderPipelineOutputDesc(&pipelineDesc.outputDescriptor);
1352
1353    // Disable all color outputs
1354    pipelineDesc.outputDescriptor.updateEnabledDrawBuffers(gl::DrawBufferMask());
1355
1356    pipelineDesc.inputPrimitiveTopology = MTLPrimitiveTopologyClassTriangle;
1357
1358    angle::ObjCPtr<id<MTLFunction>> *fragmentShader = nullptr;
1359    int depthTextureType                            = GetShaderTextureType(params.src);
1360    int stencilTextureType                          = GetShaderTextureType(params.srcStencil);
1361    if (params.src && params.srcStencil)
1362    {
1363        fragmentShader = &mDepthStencilBlitFragmentShaders[depthTextureType][stencilTextureType];
1364    }
1365    else if (params.src)
1366    {
1367        // Only depth blit
1368        fragmentShader = &mDepthBlitFragmentShaders[depthTextureType];
1369    }
1370    else
1371    {
1372        // Only stencil blit
1373        fragmentShader = &mStencilBlitFragmentShaders[stencilTextureType];
1374    }
1375
1376    ANGLE_TRY(
1377        ensureShadersInitialized(contextMtl, depthTextureType, stencilTextureType, fragmentShader));
1378
1379    return contextMtl->getPipelineCache().getRenderPipeline(
1380        contextMtl, mVertexShader, *fragmentShader, pipelineDesc, outRenderPipelineState);
1381}
1382
1383angle::Result DepthStencilBlitUtils::setupDepthStencilBlitWithDraw(
1384    const gl::Context *context,
1385    RenderCommandEncoder *cmdEncoder,
1386    const DepthStencilBlitParams &params)
1387{
1388    ContextMtl *contextMtl = mtl::GetImpl(context);
1389
1390    ASSERT(params.src || params.srcStencil);
1391
1392    SetupCommonBlitWithDrawStates(context, cmdEncoder, params, false);
1393
1394    // Generate render pipeline state
1395    angle::ObjCPtr<id<MTLRenderPipelineState>> renderPipelineState;
1396    ANGLE_TRY(
1397        getDepthStencilBlitRenderPipelineState(context, cmdEncoder, params, &renderPipelineState));
1398
1399    // Setup states
1400    cmdEncoder->setRenderPipelineState(renderPipelineState);
1401
1402    // Depth stencil state
1403    mtl::DepthStencilDesc dsStateDesc;
1404    dsStateDesc.reset();
1405    dsStateDesc.depthCompareFunction = MTLCompareFunctionAlways;
1406
1407    if (params.src)
1408    {
1409        // Enable depth write
1410        dsStateDesc.depthWriteEnabled = true;
1411    }
1412    else
1413    {
1414        // Disable depth write
1415        dsStateDesc.depthWriteEnabled = false;
1416    }
1417
1418    if (params.srcStencil)
1419    {
1420        cmdEncoder->setFragmentTexture(params.srcStencil, 1);
1421
1422        if (!contextMtl->getDisplay()->getFeatures().hasShaderStencilOutput.enabled)
1423        {
1424            // Hardware must support stencil writing directly in shader.
1425            UNREACHABLE();
1426        }
1427        // Enable stencil write to framebuffer
1428        dsStateDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways;
1429        dsStateDesc.backFaceStencil.stencilCompareFunction  = MTLCompareFunctionAlways;
1430
1431        dsStateDesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationReplace;
1432        dsStateDesc.backFaceStencil.depthStencilPassOperation  = MTLStencilOperationReplace;
1433
1434        dsStateDesc.frontFaceStencil.writeMask = kStencilMaskAll;
1435        dsStateDesc.backFaceStencil.writeMask  = kStencilMaskAll;
1436    }
1437
1438    cmdEncoder->setDepthStencilState(contextMtl->getDisplay()->getStateCache().getDepthStencilState(
1439        contextMtl->getMetalDevice(), dsStateDesc));
1440    return angle::Result::Continue;
1441}
1442
1443angle::Result DepthStencilBlitUtils::blitDepthStencilWithDraw(const gl::Context *context,
1444                                                              RenderCommandEncoder *cmdEncoder,
1445                                                              const DepthStencilBlitParams &params)
1446{
1447    if (!params.src && !params.srcStencil)
1448    {
1449        return angle::Result::Continue;
1450    }
1451    ContextMtl *contextMtl = GetImpl(context);
1452
1453    ANGLE_TRY(setupDepthStencilBlitWithDraw(context, cmdEncoder, params));
1454
1455    angle::Result result;
1456    {
1457        // Need to disable occlusion query, otherwise blitting will affect the occlusion counting
1458        ScopedDisableOcclusionQuery disableOcclusionQuery(contextMtl, cmdEncoder, &result);
1459        // Draw the screen aligned quad
1460        cmdEncoder->draw(MTLPrimitiveTypeTriangleStrip, 0, 4);
1461    }
1462
1463    // Invalidate current context's state
1464    contextMtl->invalidateState(context);
1465
1466    return result;
1467}
1468
1469angle::Result DepthStencilBlitUtils::blitStencilViaCopyBuffer(
1470    const gl::Context *context,
1471    const StencilBlitViaBufferParams &params)
1472{
1473    // Depth texture must be omitted.
1474    ASSERT(!params.src);
1475    if (!params.srcStencil || !params.dstStencil)
1476    {
1477        return angle::Result::Continue;
1478    }
1479    ContextMtl *contextMtl = GetImpl(context);
1480
1481    // Create intermediate buffer.
1482    uint32_t bufferRequiredRowPitch =
1483        static_cast<uint32_t>(params.dstRect.width) * params.dstStencil->samples();
1484    uint32_t bufferRequiredSize =
1485        bufferRequiredRowPitch * static_cast<uint32_t>(params.dstRect.height);
1486    if (!mStencilCopyBuffer || mStencilCopyBuffer->size() < bufferRequiredSize)
1487    {
1488        ANGLE_TRY(Buffer::MakeBuffer(contextMtl, bufferRequiredSize, nullptr, &mStencilCopyBuffer));
1489    }
1490
1491    // Copy stencil data to buffer via compute shader. We cannot use blit command since blit command
1492    // doesn't support multisample resolve and scaling.
1493    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
1494    ASSERT(cmdEncoder);
1495
1496    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
1497    ANGLE_TRY(getStencilToBufferComputePipelineState(contextMtl, params, &pipeline));
1498
1499    cmdEncoder->setComputePipelineState(pipeline);
1500
1501    float u0, v0, u1, v1;
1502    bool unpackFlipX = params.unpackFlipX;
1503    bool unpackFlipY = params.unpackFlipY;
1504    if (params.dstFlipX)
1505    {
1506        unpackFlipX = !unpackFlipX;
1507    }
1508    if (params.dstFlipY)
1509    {
1510        unpackFlipY = !unpackFlipY;
1511    }
1512    GetBlitTexCoords(params.srcNormalizedCoords, params.srcYFlipped, unpackFlipX, unpackFlipY, &u0,
1513                     &v0, &u1, &v1);
1514
1515    BlitStencilToBufferParamsUniform uniform;
1516    uniform.srcTexCoordSteps[0]  = (u1 - u0) / params.dstRect.width;
1517    uniform.srcTexCoordSteps[1]  = (v1 - v0) / params.dstRect.height;
1518    uniform.srcStartTexCoords[0] = u0 + uniform.srcTexCoordSteps[0] * 0.5f;
1519    uniform.srcStartTexCoords[1] = v0 + uniform.srcTexCoordSteps[1] * 0.5f;
1520    uniform.srcLevel             = params.srcLevel.get();
1521    uniform.srcLayer             = params.srcLayer;
1522    uniform.dstSize[0]           = params.dstRect.width;
1523    uniform.dstSize[1]           = params.dstRect.height;
1524    uniform.dstBufferRowPitch    = bufferRequiredRowPitch;
1525    uniform.resolveMS            = params.dstStencil->samples() == 1;
1526
1527    cmdEncoder->setTexture(params.srcStencil, 1);
1528
1529    cmdEncoder->setData(uniform, 0);
1530    cmdEncoder->setBufferForWrite(mStencilCopyBuffer, 0, 1);
1531
1532    NSUInteger w                  = pipeline.get().threadExecutionWidth;
1533    MTLSize threadsPerThreadgroup = MTLSizeMake(w, 1, 1);
1534    DispatchCompute(contextMtl, cmdEncoder, /** allowNonUniform */ true,
1535                    MTLSizeMake(params.dstRect.width, params.dstRect.height, 1),
1536                    threadsPerThreadgroup);
1537
1538    // Copy buffer to real destination texture
1539    ASSERT(params.dstStencil->textureType() != MTLTextureType3D);
1540
1541    mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
1542
1543    // Only copy the scissored area of the buffer.
1544    MTLScissorRect viewportRectMtl =
1545        GetScissorRect(params.dstRect, params.dstTextureSize.height, params.dstFlipY);
1546    MTLScissorRect scissorRectMtl =
1547        GetScissorRect(params.dstScissorRect, params.dstTextureSize.height, params.dstFlipY);
1548
1549    uint32_t dx = static_cast<uint32_t>(scissorRectMtl.x - viewportRectMtl.x);
1550    uint32_t dy = static_cast<uint32_t>(scissorRectMtl.y - viewportRectMtl.y);
1551
1552    uint32_t bufferStartReadableOffset = dx + bufferRequiredRowPitch * dy;
1553    blitEncoder->copyBufferToTexture(
1554        mStencilCopyBuffer, bufferStartReadableOffset, bufferRequiredRowPitch, 0,
1555        MTLSizeMake(scissorRectMtl.width, scissorRectMtl.height, 1), params.dstStencil,
1556        params.dstStencilLayer, params.dstStencilLevel,
1557        MTLOriginMake(scissorRectMtl.x, scissorRectMtl.y, 0),
1558        params.dstPackedDepthStencilFormat ? MTLBlitOptionStencilFromDepthStencil
1559                                           : MTLBlitOptionNone);
1560
1561    return angle::Result::Continue;
1562}
1563
1564angle::Result IndexGeneratorUtils::getIndexConversionPipeline(
1565    ContextMtl *contextMtl,
1566    gl::DrawElementsType srcType,
1567    uint32_t srcOffset,
1568    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
1569{
1570    size_t elementSize                      = gl::GetDrawElementsTypeSize(srcType);
1571    BOOL aligned                            = (srcOffset % elementSize) == 0;
1572    int srcTypeKey                          = static_cast<int>(srcType);
1573    angle::ObjCPtr<id<MTLFunction>> &shader = mIndexConversionShaders[srcTypeKey][aligned ? 1 : 0];
1574
1575    if (!shader)
1576    {
1577        ANGLE_MTL_OBJC_SCOPE
1578        {
1579            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
1580                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
1581
1582            [funcConstants setConstantValue:&aligned
1583                                       type:MTLDataTypeBool
1584                                   withName:SOURCE_BUFFER_ALIGNED_CONSTANT_NAME];
1585
1586            NSString *shaderName = nil;
1587            switch (srcType)
1588            {
1589                case gl::DrawElementsType::UnsignedByte:
1590                    // No need for specialized shader
1591                    funcConstants = nil;
1592                    shaderName    = @"convertIndexU8ToU16";
1593                    break;
1594                case gl::DrawElementsType::UnsignedShort:
1595                    shaderName = @"convertIndexU16";
1596                    break;
1597                case gl::DrawElementsType::UnsignedInt:
1598                    shaderName = @"convertIndexU32";
1599                    break;
1600                default:
1601                    UNREACHABLE();
1602            }
1603
1604            ANGLE_TRY(
1605                EnsureSpecializedShaderInitialized(contextMtl, shaderName, funcConstants, &shader));
1606        }
1607    }
1608
1609    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, shader,
1610                                                             outComputePipeline);
1611}
1612
1613angle::Result IndexGeneratorUtils::getIndicesFromElemArrayGeneratorPipeline(
1614    ContextMtl *contextMtl,
1615    gl::DrawElementsType srcType,
1616    uint32_t srcOffset,
1617    NSString *shaderName,
1618    IndexConversionShaderArray *shaderArray,
1619    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
1620{
1621    size_t elementSize = gl::GetDrawElementsTypeSize(srcType);
1622    BOOL aligned       = (srcOffset % elementSize) == 0;
1623    int srcTypeKey     = static_cast<int>(srcType);
1624
1625    angle::ObjCPtr<id<MTLFunction>> &shader = (*shaderArray)[srcTypeKey][aligned ? 1 : 0];
1626
1627    if (!shader)
1628    {
1629        ANGLE_MTL_OBJC_SCOPE
1630        {
1631            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
1632                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
1633
1634            bool isU8  = false;
1635            bool isU16 = false;
1636            bool isU32 = false;
1637
1638            switch (srcType)
1639            {
1640                case gl::DrawElementsType::UnsignedByte:
1641                    isU8 = true;
1642                    break;
1643                case gl::DrawElementsType::UnsignedShort:
1644                    isU16 = true;
1645                    break;
1646                case gl::DrawElementsType::UnsignedInt:
1647                    isU32 = true;
1648                    break;
1649                default:
1650                    UNREACHABLE();
1651            }
1652
1653            [funcConstants setConstantValue:&aligned
1654                                       type:MTLDataTypeBool
1655                                   withName:SOURCE_BUFFER_ALIGNED_CONSTANT_NAME];
1656            [funcConstants setConstantValue:&isU8
1657                                       type:MTLDataTypeBool
1658                                   withName:SOURCE_IDX_IS_U8_CONSTANT_NAME];
1659            [funcConstants setConstantValue:&isU16
1660                                       type:MTLDataTypeBool
1661                                   withName:SOURCE_IDX_IS_U16_CONSTANT_NAME];
1662            [funcConstants setConstantValue:&isU32
1663                                       type:MTLDataTypeBool
1664                                   withName:SOURCE_IDX_IS_U32_CONSTANT_NAME];
1665
1666            ANGLE_TRY(
1667                EnsureSpecializedShaderInitialized(contextMtl, shaderName, funcConstants, &shader));
1668        }
1669    }
1670
1671    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, shader,
1672                                                             outComputePipeline);
1673}
1674
1675angle::Result IndexGeneratorUtils::getTriFanFromArrayGeneratorPipeline(
1676    ContextMtl *contextMtl,
1677    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
1678{
1679    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"genTriFanIndicesFromArray",
1680                                      &mTriFanFromArraysGeneratorShader));
1681    return contextMtl->getPipelineCache().getComputePipeline(
1682        contextMtl, mTriFanFromArraysGeneratorShader, outComputePipeline);
1683}
1684
1685angle::Result IndexGeneratorUtils::getLineLoopFromArrayGeneratorPipeline(
1686    ContextMtl *contextMtl,
1687    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
1688{
1689    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"genLineLoopIndicesFromArray",
1690                                      &mLineLoopFromArraysGeneratorShader));
1691    return contextMtl->getPipelineCache().getComputePipeline(
1692        contextMtl, mLineLoopFromArraysGeneratorShader, outComputePipeline);
1693}
1694
1695angle::Result IndexGeneratorUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
1696                                                         const IndexConversionParams &params)
1697{
1698    ComputeCommandEncoder *cmdEncoder = contextMtl->getIndexPreprocessingCommandEncoder();
1699    ASSERT(cmdEncoder);
1700
1701    angle::ObjCPtr<id<MTLComputePipelineState>> pipelineState;
1702    ANGLE_TRY(
1703        getIndexConversionPipeline(contextMtl, params.srcType, params.srcOffset, &pipelineState));
1704
1705    ASSERT(pipelineState);
1706
1707    cmdEncoder->setComputePipelineState(pipelineState);
1708
1709    ASSERT((params.dstOffset % kIndexBufferOffsetAlignment) == 0);
1710
1711    IndexConversionUniform uniform;
1712    uniform.srcOffset               = params.srcOffset;
1713    uniform.indexCount              = params.indexCount;
1714    uniform.primitiveRestartEnabled = params.primitiveRestartEnabled;
1715
1716    cmdEncoder->setData(uniform, 0);
1717    cmdEncoder->setBuffer(params.srcBuffer, 0, 1);
1718    cmdEncoder->setBufferForWrite(params.dstBuffer, params.dstOffset, 2);
1719
1720    DispatchCompute(contextMtl, cmdEncoder, pipelineState, params.indexCount);
1721
1722    return angle::Result::Continue;
1723}
1724
1725angle::Result IndexGeneratorUtils::generateTriFanBufferFromArrays(
1726    ContextMtl *contextMtl,
1727    const TriFanOrLineLoopFromArrayParams &params)
1728{
1729    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
1730    ASSERT(cmdEncoder);
1731
1732    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
1733    ANGLE_TRY(getTriFanFromArrayGeneratorPipeline(contextMtl, &pipeline));
1734
1735    ASSERT(params.vertexCount > 2);
1736
1737    cmdEncoder->setComputePipelineState(pipeline);
1738
1739    ASSERT((params.dstOffset % kIndexBufferOffsetAlignment) == 0);
1740
1741    TriFanOrLineLoopArrayParams uniform;
1742
1743    uniform.firstVertex = params.firstVertex;
1744    uniform.vertexCount = params.vertexCount - 2;
1745
1746    cmdEncoder->setData(uniform, 0);
1747    cmdEncoder->setBufferForWrite(params.dstBuffer, params.dstOffset, 2);
1748
1749    DispatchCompute(contextMtl, cmdEncoder, pipeline, uniform.vertexCount);
1750
1751    return angle::Result::Continue;
1752}
1753
1754angle::Result IndexGeneratorUtils::generateTriFanBufferFromElementsArray(
1755    ContextMtl *contextMtl,
1756    const IndexGenerationParams &params,
1757    uint32_t *indicesGenerated)
1758{
1759    const gl::VertexArray *vertexArray = contextMtl->getState().getVertexArray();
1760    const gl::Buffer *elementBuffer    = vertexArray->getElementArrayBuffer();
1761    if (elementBuffer)
1762    {
1763        BufferMtl *elementBufferMtl = GetImpl(elementBuffer);
1764        size_t srcOffset            = reinterpret_cast<size_t>(params.indices);
1765        ANGLE_CHECK(contextMtl, srcOffset <= std::numeric_limits<uint32_t>::max(),
1766                    "Index offset is too large", GL_INVALID_VALUE);
1767        if (params.primitiveRestartEnabled ||
1768            (!contextMtl->getDisplay()->getFeatures().hasCheapRenderPass.enabled &&
1769             contextMtl->getRenderCommandEncoder()))
1770        {
1771            IndexGenerationParams cpuPathParams = params;
1772            cpuPathParams.indices = elementBufferMtl->getBufferDataReadOnly(contextMtl) + srcOffset;
1773            return generateTriFanBufferFromElementsArrayCPU(contextMtl, cpuPathParams,
1774                                                            indicesGenerated);
1775        }
1776        else
1777        {
1778            return generateTriFanBufferFromElementsArrayGPU(
1779                contextMtl, params.srcType, params.indexCount, elementBufferMtl->getCurrentBuffer(),
1780                static_cast<uint32_t>(srcOffset), params.dstBuffer, params.dstOffset);
1781        }
1782    }
1783    else
1784    {
1785        return generateTriFanBufferFromElementsArrayCPU(contextMtl, params, indicesGenerated);
1786    }
1787}
1788
1789angle::Result IndexGeneratorUtils::generateTriFanBufferFromElementsArrayGPU(
1790    ContextMtl *contextMtl,
1791    gl::DrawElementsType srcType,
1792    uint32_t indexCount,
1793    const BufferRef &srcBuffer,
1794    uint32_t srcOffset,
1795    const BufferRef &dstBuffer,
1796    // Must be multiples of kIndexBufferOffsetAlignment
1797    uint32_t dstOffset)
1798{
1799    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
1800    ASSERT(cmdEncoder);
1801
1802    angle::ObjCPtr<id<MTLComputePipelineState>> pipelineState;
1803    ANGLE_TRY(getIndicesFromElemArrayGeneratorPipeline(
1804        contextMtl, srcType, srcOffset, @"genTriFanIndicesFromElements",
1805        &mTriFanFromElemArrayGeneratorShaders, &pipelineState));
1806
1807    ASSERT(pipelineState);
1808
1809    cmdEncoder->setComputePipelineState(pipelineState);
1810
1811    ASSERT((dstOffset % kIndexBufferOffsetAlignment) == 0);
1812    ASSERT(indexCount > 2);
1813
1814    IndexConversionUniform uniform;
1815    uniform.srcOffset  = srcOffset;
1816    uniform.indexCount = indexCount - 2;  // Only start from the 3rd element.
1817
1818    cmdEncoder->setData(uniform, 0);
1819    cmdEncoder->setBuffer(srcBuffer, 0, 1);
1820    cmdEncoder->setBufferForWrite(dstBuffer, dstOffset, 2);
1821
1822    DispatchCompute(contextMtl, cmdEncoder, pipelineState, uniform.indexCount);
1823
1824    return angle::Result::Continue;
1825}
1826
1827angle::Result IndexGeneratorUtils::generateTriFanBufferFromElementsArrayCPU(
1828    ContextMtl *contextMtl,
1829    const IndexGenerationParams &params,
1830    uint32_t *genIndices)
1831{
1832    switch (params.srcType)
1833    {
1834        case gl::DrawElementsType::UnsignedByte:
1835            return GenTriFanFromClientElements(contextMtl, params.indexCount,
1836                                               params.primitiveRestartEnabled,
1837                                               static_cast<const uint8_t *>(params.indices),
1838                                               params.dstBuffer, params.dstOffset, genIndices);
1839        case gl::DrawElementsType::UnsignedShort:
1840            return GenTriFanFromClientElements(contextMtl, params.indexCount,
1841                                               params.primitiveRestartEnabled,
1842                                               static_cast<const uint16_t *>(params.indices),
1843                                               params.dstBuffer, params.dstOffset, genIndices);
1844        case gl::DrawElementsType::UnsignedInt:
1845            return GenTriFanFromClientElements(contextMtl, params.indexCount,
1846                                               params.primitiveRestartEnabled,
1847                                               static_cast<const uint32_t *>(params.indices),
1848                                               params.dstBuffer, params.dstOffset, genIndices);
1849        default:
1850            UNREACHABLE();
1851    }
1852
1853    return angle::Result::Stop;
1854}
1855
1856angle::Result IndexGeneratorUtils::generateLineLoopBufferFromArrays(
1857    ContextMtl *contextMtl,
1858    const TriFanOrLineLoopFromArrayParams &params)
1859{
1860    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
1861    ASSERT(cmdEncoder);
1862
1863    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
1864    ANGLE_TRY(getLineLoopFromArrayGeneratorPipeline(contextMtl, &pipeline));
1865
1866    cmdEncoder->setComputePipelineState(pipeline);
1867
1868    ASSERT((params.dstOffset % kIndexBufferOffsetAlignment) == 0);
1869
1870    TriFanOrLineLoopArrayParams uniform;
1871
1872    uniform.firstVertex = params.firstVertex;
1873    uniform.vertexCount = params.vertexCount;
1874
1875    cmdEncoder->setData(uniform, 0);
1876    cmdEncoder->setBufferForWrite(params.dstBuffer, params.dstOffset, 2);
1877
1878    DispatchCompute(contextMtl, cmdEncoder, pipeline, uniform.vertexCount + 1);
1879
1880    return angle::Result::Continue;
1881}
1882
1883angle::Result IndexGeneratorUtils::generateLineLoopBufferFromElementsArray(
1884    ContextMtl *contextMtl,
1885    const IndexGenerationParams &params,
1886    uint32_t *indicesGenerated)
1887{
1888    const gl::VertexArray *vertexArray = contextMtl->getState().getVertexArray();
1889    const gl::Buffer *elementBuffer    = vertexArray->getElementArrayBuffer();
1890    if (elementBuffer)
1891    {
1892        BufferMtl *elementBufferMtl = GetImpl(elementBuffer);
1893        size_t srcOffset            = reinterpret_cast<size_t>(params.indices);
1894        ANGLE_CHECK(contextMtl, srcOffset <= std::numeric_limits<uint32_t>::max(),
1895                    "Index offset is too large", GL_INVALID_VALUE);
1896        if (params.primitiveRestartEnabled ||
1897            (!contextMtl->getDisplay()->getFeatures().hasCheapRenderPass.enabled &&
1898             contextMtl->getRenderCommandEncoder()))
1899        {
1900            IndexGenerationParams cpuPathParams = params;
1901            cpuPathParams.indices = elementBufferMtl->getBufferDataReadOnly(contextMtl) + srcOffset;
1902            return generateLineLoopBufferFromElementsArrayCPU(contextMtl, cpuPathParams,
1903                                                              indicesGenerated);
1904        }
1905        else
1906        {
1907            *indicesGenerated = params.indexCount + 1;
1908            return generateLineLoopBufferFromElementsArrayGPU(
1909                contextMtl, params.srcType, params.indexCount, elementBufferMtl->getCurrentBuffer(),
1910                static_cast<uint32_t>(srcOffset), params.dstBuffer, params.dstOffset);
1911        }
1912    }
1913    else
1914    {
1915        return generateLineLoopBufferFromElementsArrayCPU(contextMtl, params, indicesGenerated);
1916    }
1917}
1918
1919angle::Result IndexGeneratorUtils::generateLineLoopBufferFromElementsArrayGPU(
1920    ContextMtl *contextMtl,
1921    gl::DrawElementsType srcType,
1922    uint32_t indexCount,
1923    const BufferRef &srcBuffer,
1924    uint32_t srcOffset,
1925    const BufferRef &dstBuffer,
1926    // Must be multiples of kIndexBufferOffsetAlignment
1927    uint32_t dstOffset)
1928{
1929    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
1930    ASSERT(cmdEncoder);
1931
1932    angle::ObjCPtr<id<MTLComputePipelineState>> pipelineState;
1933    ANGLE_TRY(getIndicesFromElemArrayGeneratorPipeline(
1934        contextMtl, srcType, srcOffset, @"genLineLoopIndicesFromElements",
1935        &mLineLoopFromElemArrayGeneratorShaders, &pipelineState));
1936
1937    cmdEncoder->setComputePipelineState(pipelineState);
1938
1939    ASSERT((dstOffset % kIndexBufferOffsetAlignment) == 0);
1940    ASSERT(indexCount >= 2);
1941
1942    IndexConversionUniform uniform;
1943    uniform.srcOffset  = srcOffset;
1944    uniform.indexCount = indexCount;
1945
1946    cmdEncoder->setData(uniform, 0);
1947    cmdEncoder->setBuffer(srcBuffer, 0, 1);
1948    cmdEncoder->setBufferForWrite(dstBuffer, dstOffset, 2);
1949
1950    DispatchCompute(contextMtl, cmdEncoder, pipelineState, uniform.indexCount + 1);
1951
1952    return angle::Result::Continue;
1953}
1954
1955angle::Result IndexGeneratorUtils::generateLineLoopBufferFromElementsArrayCPU(
1956    ContextMtl *contextMtl,
1957    const IndexGenerationParams &params,
1958    uint32_t *indicesGenerated)
1959{
1960    uint8_t *dstIndices = params.dstBuffer->map(contextMtl, params.dstOffset);
1961    if (dstIndices == nullptr)
1962    {
1963        return angle::Result::Stop;
1964    }
1965    const uint8_t *indices = static_cast<const uint8_t *>(params.indices);
1966    size_t dstIndexCount   = 0;
1967    switch (params.srcType)
1968    {
1969        case gl::DrawElementsType::UnsignedByte:
1970            dstIndexCount = CopyLineLoopIndices<uint8_t, uint32_t>(
1971                params.indexCount, indices, params.primitiveRestartEnabled, dstIndices);
1972            break;
1973        case gl::DrawElementsType::UnsignedShort:
1974            dstIndexCount = CopyLineLoopIndices<uint16_t, uint32_t>(
1975                params.indexCount, indices, params.primitiveRestartEnabled, dstIndices);
1976            break;
1977        case gl::DrawElementsType::UnsignedInt:
1978            dstIndexCount = CopyLineLoopIndices<uint32_t, uint32_t>(
1979                params.indexCount, indices, params.primitiveRestartEnabled, dstIndices);
1980            break;
1981        default:
1982            UNREACHABLE();
1983    }
1984    params.dstBuffer->unmapAndFlushSubset(contextMtl, params.dstOffset,
1985                                          dstIndexCount * sizeof(uint32_t));
1986    *indicesGenerated = static_cast<uint32_t>(dstIndexCount);
1987    return angle::Result::Continue;
1988}
1989
1990angle::Result IndexGeneratorUtils::generateLineLoopLastSegment(ContextMtl *contextMtl,
1991                                                               uint32_t firstVertex,
1992                                                               uint32_t lastVertex,
1993                                                               const BufferRef &dstBuffer,
1994                                                               uint32_t dstOffset)
1995{
1996    uint8_t *ptr = dstBuffer->map(contextMtl) + dstOffset;
1997
1998    uint32_t indices[2] = {lastVertex, firstVertex};
1999    memcpy(ptr, indices, sizeof(indices));
2000
2001    dstBuffer->unmapAndFlushSubset(contextMtl, dstOffset, sizeof(indices));
2002
2003    return angle::Result::Continue;
2004}
2005
2006angle::Result IndexGeneratorUtils::generateLineLoopLastSegmentFromElementsArray(
2007    ContextMtl *contextMtl,
2008    const IndexGenerationParams &params)
2009{
2010    ASSERT(!params.primitiveRestartEnabled);
2011    const gl::VertexArray *vertexArray = contextMtl->getState().getVertexArray();
2012    const gl::Buffer *elementBuffer    = vertexArray->getElementArrayBuffer();
2013    if (elementBuffer)
2014    {
2015        size_t srcOffset = reinterpret_cast<size_t>(params.indices);
2016        ANGLE_CHECK(contextMtl, srcOffset <= std::numeric_limits<uint32_t>::max(),
2017                    "Index offset is too large", GL_INVALID_VALUE);
2018
2019        BufferMtl *bufferMtl = GetImpl(elementBuffer);
2020        std::pair<uint32_t, uint32_t> firstLast;
2021        ANGLE_TRY(bufferMtl->getFirstLastIndices(contextMtl, params.srcType,
2022                                                 static_cast<uint32_t>(srcOffset),
2023                                                 params.indexCount, &firstLast));
2024
2025        return generateLineLoopLastSegment(contextMtl, firstLast.first, firstLast.second,
2026                                           params.dstBuffer, params.dstOffset);
2027    }
2028    else
2029    {
2030        return generateLineLoopLastSegmentFromElementsArrayCPU(contextMtl, params);
2031    }
2032}
2033
2034angle::Result IndexGeneratorUtils::generateLineLoopLastSegmentFromElementsArrayCPU(
2035    ContextMtl *contextMtl,
2036    const IndexGenerationParams &params)
2037{
2038    ASSERT(!params.primitiveRestartEnabled);
2039
2040    uint32_t first, last;
2041
2042    switch (params.srcType)
2043    {
2044        case gl::DrawElementsType::UnsignedByte:
2045            GetFirstLastIndicesFromClientElements(
2046                params.indexCount, static_cast<const uint8_t *>(params.indices), &first, &last);
2047            break;
2048        case gl::DrawElementsType::UnsignedShort:
2049            GetFirstLastIndicesFromClientElements(
2050                params.indexCount, static_cast<const uint16_t *>(params.indices), &first, &last);
2051            break;
2052        case gl::DrawElementsType::UnsignedInt:
2053            GetFirstLastIndicesFromClientElements(
2054                params.indexCount, static_cast<const uint32_t *>(params.indices), &first, &last);
2055            break;
2056        default:
2057            UNREACHABLE();
2058            return angle::Result::Stop;
2059    }
2060
2061    return generateLineLoopLastSegment(contextMtl, first, last, params.dstBuffer, params.dstOffset);
2062}
2063
2064angle::Result VisibilityResultUtils::getVisibilityResultCombinePipeline(
2065    ContextMtl *contextMtl,
2066    bool keepOldValue,
2067    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
2068{
2069    // There is no guarantee Objective-C's BOOL is equal to bool, so casting just in case.
2070    BOOL keepOldValueVal = keepOldValue;
2071    angle::ObjCPtr<id<MTLFunction>> &shader =
2072        mVisibilityResultCombineComputeShaders[keepOldValueVal];
2073    if (!shader)
2074    {
2075        ANGLE_MTL_OBJC_SCOPE
2076        {
2077            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
2078                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
2079
2080            [funcConstants setConstantValue:&keepOldValueVal
2081                                       type:MTLDataTypeBool
2082                                   withName:VISIBILITY_RESULT_KEEP_OLD_VAL_CONSTANT_NAME];
2083
2084            ANGLE_TRY(EnsureSpecializedShaderInitialized(contextMtl, @"combineVisibilityResult",
2085                                                         funcConstants, &shader));
2086        }
2087    }
2088
2089    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, shader,
2090                                                             outComputePipeline);
2091}
2092
2093angle::Result VisibilityResultUtils::combineVisibilityResult(
2094    ContextMtl *contextMtl,
2095    bool keepOldValue,
2096    const VisibilityBufferOffsetsMtl &renderPassResultBufOffsets,
2097    const BufferRef &renderPassResultBuf,
2098    const BufferRef &finalResultBuf)
2099{
2100    ASSERT(!renderPassResultBufOffsets.empty());
2101
2102    if (renderPassResultBufOffsets.size() == 1 && !keepOldValue)
2103    {
2104        // Use blit command to copy directly
2105        BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
2106
2107        blitEncoder->copyBuffer(renderPassResultBuf, renderPassResultBufOffsets.front(),
2108                                finalResultBuf, 0, kOcclusionQueryResultSize);
2109        return angle::Result::Continue;
2110    }
2111
2112    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
2113    ASSERT(cmdEncoder);
2114
2115    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2116    ANGLE_TRY(getVisibilityResultCombinePipeline(contextMtl, keepOldValue, &pipeline));
2117    cmdEncoder->setComputePipelineState(pipeline);
2118
2119    CombineVisibilityResultUniform options;
2120    // Offset is viewed as 64 bit unit in compute shader.
2121    options.startOffset = renderPassResultBufOffsets.front() / kOcclusionQueryResultSize;
2122    options.numOffsets  = renderPassResultBufOffsets.size();
2123
2124    cmdEncoder->setData(options, 0);
2125    cmdEncoder->setBuffer(renderPassResultBuf, 0, 1);
2126    cmdEncoder->setBufferForWrite(finalResultBuf, 0, 2);
2127
2128    DispatchCompute(contextMtl, cmdEncoder, pipeline, 1);
2129
2130    return angle::Result::Continue;
2131}
2132
2133angle::Result MipmapUtils::get3DMipGeneratorPipeline(
2134    ContextMtl *contextMtl,
2135    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
2136{
2137    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"generate3DMipmaps", &m3DMipGeneratorShader));
2138    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, m3DMipGeneratorShader,
2139                                                             outComputePipeline);
2140}
2141
2142angle::Result MipmapUtils::get2DMipGeneratorPipeline(
2143    ContextMtl *contextMtl,
2144    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
2145{
2146    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"generate2DMipmaps", &m2DMipGeneratorShader));
2147    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, m2DMipGeneratorShader,
2148                                                             outComputePipeline);
2149}
2150
2151angle::Result MipmapUtils::get2DArrayMipGeneratorPipeline(
2152    ContextMtl *contextMtl,
2153    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
2154{
2155    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"generate2DArrayMipmaps",
2156                                      &m2DArrayMipGeneratorShader));
2157    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, m2DArrayMipGeneratorShader,
2158                                                             outComputePipeline);
2159}
2160
2161angle::Result MipmapUtils::getCubeMipGeneratorPipeline(
2162    ContextMtl *contextMtl,
2163    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
2164{
2165    ANGLE_TRY(
2166        EnsureShaderInitialized(contextMtl, @"generateCubeMipmaps", &mCubeMipGeneratorShader));
2167    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, mCubeMipGeneratorShader,
2168                                                             outComputePipeline);
2169}
2170
2171angle::Result MipmapUtils::generateMipmapCS(ContextMtl *contextMtl,
2172                                            const TextureRef &srcTexture,
2173                                            bool sRGBMipmap,
2174                                            NativeTexLevelArray *mipmapOutputViews)
2175{
2176    // Only support 3D texture for now.
2177    ASSERT(srcTexture->textureType() == MTLTextureType3D);
2178
2179    MTLSize threadGroupSize;
2180    uint32_t slices = 1;
2181    angle::ObjCPtr<id<MTLComputePipelineState>> computePipeline;
2182    switch (srcTexture->textureType())
2183    {
2184        case MTLTextureType2D:
2185            ANGLE_TRY(get2DMipGeneratorPipeline(contextMtl, &computePipeline));
2186            threadGroupSize = MTLSizeMake(kGenerateMipThreadGroupSizePerDim,
2187                                          kGenerateMipThreadGroupSizePerDim, 1);
2188            break;
2189        case MTLTextureType2DArray:
2190            ANGLE_TRY(get2DArrayMipGeneratorPipeline(contextMtl, &computePipeline));
2191            slices          = srcTexture->arrayLength();
2192            threadGroupSize = MTLSizeMake(kGenerateMipThreadGroupSizePerDim,
2193                                          kGenerateMipThreadGroupSizePerDim, 1);
2194            break;
2195        case MTLTextureTypeCube:
2196            ANGLE_TRY(getCubeMipGeneratorPipeline(contextMtl, &computePipeline));
2197            slices          = 6;
2198            threadGroupSize = MTLSizeMake(kGenerateMipThreadGroupSizePerDim,
2199                                          kGenerateMipThreadGroupSizePerDim, 1);
2200            break;
2201        case MTLTextureType3D:
2202            ANGLE_TRY(get3DMipGeneratorPipeline(contextMtl, &computePipeline));
2203            threadGroupSize =
2204                MTLSizeMake(kGenerateMipThreadGroupSizePerDim, kGenerateMipThreadGroupSizePerDim,
2205                            kGenerateMipThreadGroupSizePerDim);
2206            break;
2207        default:
2208            UNREACHABLE();
2209    }
2210
2211    // The compute shader supports up to 4 mipmaps generated per pass.
2212    // See shaders/gen_mipmap.metal
2213    uint32_t maxMipsPerBatch = 4;
2214
2215    if (threadGroupSize.width * threadGroupSize.height * threadGroupSize.depth >
2216            computePipeline.get().maxTotalThreadsPerThreadgroup ||
2217        ANGLE_UNLIKELY(
2218            !contextMtl->getDisplay()->getFeatures().allowGenMultipleMipsPerPass.enabled))
2219    {
2220        // Multiple mipmaps generation is not supported due to hardware's thread group size limits.
2221        // Fallback to generate one mip per pass and reduce thread group size.
2222        if (ANGLE_UNLIKELY(threadGroupSize.width * threadGroupSize.height >
2223                           computePipeline.get().maxTotalThreadsPerThreadgroup))
2224        {
2225            // Even with reduced thread group size, we cannot proceed.
2226            // HACK: use blit command encoder to generate mipmaps if it is not possible
2227            // to use compute shader due to hardware limits.
2228            BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
2229            blitEncoder->generateMipmapsForTexture(srcTexture);
2230            return angle::Result::Continue;
2231        }
2232
2233        threadGroupSize.depth = 1;
2234        maxMipsPerBatch       = 1;
2235    }
2236
2237    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
2238    ASSERT(cmdEncoder);
2239    cmdEncoder->setComputePipelineState(computePipeline);
2240
2241    Generate3DMipmapUniform options;
2242
2243    uint32_t remainMips             = srcTexture->mipmapLevels() - 1;
2244    MipmapNativeLevel batchSrcLevel = kZeroNativeMipLevel;
2245    options.srcLevel                = batchSrcLevel.get();
2246    options.sRGB                    = sRGBMipmap;
2247
2248    cmdEncoder->setTexture(srcTexture, 0);
2249    cmdEncoder->markResourceBeingWrittenByGPU(srcTexture);
2250    while (remainMips)
2251    {
2252        const TextureRef &firstMipView =
2253            mipmapOutputViews->at(mtl::MipmapNativeLevel(batchSrcLevel + 1));
2254        gl::Extents size = firstMipView->sizeAt0();
2255        bool isPow2 = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
2256
2257        // Currently multiple mipmaps generation is only supported for power of two base level.
2258        if (isPow2)
2259        {
2260            options.numMipmapsToGenerate = std::min(remainMips, maxMipsPerBatch);
2261        }
2262        else
2263        {
2264            options.numMipmapsToGenerate = 1;
2265        }
2266
2267        cmdEncoder->setData(options, 0);
2268
2269        for (uint32_t i = 1; i <= options.numMipmapsToGenerate; ++i)
2270        {
2271            cmdEncoder->setTexture(
2272                mipmapOutputViews->at(mtl::MipmapNativeLevel(options.srcLevel + i)), i);
2273        }
2274
2275        uint32_t threadsPerZ = std::max(slices, firstMipView->depthAt0());
2276
2277        DispatchCompute(
2278            contextMtl, cmdEncoder,
2279            /** allowNonUniform */ false,
2280            MTLSizeMake(firstMipView->widthAt0(), firstMipView->heightAt0(), threadsPerZ),
2281            threadGroupSize);
2282
2283        remainMips -= options.numMipmapsToGenerate;
2284        batchSrcLevel    = batchSrcLevel + options.numMipmapsToGenerate;
2285        options.srcLevel = batchSrcLevel.get();
2286    }
2287
2288    return angle::Result::Continue;
2289}
2290
2291// CopyPixelsUtils implementation
2292CopyPixelsUtils::CopyPixelsUtils(const std::string &readShaderName,
2293                                 const std::string &writeShaderName)
2294    : mReadShaderName(readShaderName), mWriteShaderName(writeShaderName)
2295{}
2296
2297angle::Result CopyPixelsUtils::getPixelsCopyPipeline(
2298    ContextMtl *contextMtl,
2299    const angle::Format &angleFormat,
2300    const TextureRef &texture,
2301    bool bufferWrite,
2302    angle::ObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
2303{
2304    int formatIDValue     = static_cast<int>(angleFormat.id);
2305    int shaderTextureType = GetShaderTextureType(texture);
2306    int index2 = mtl_shader::kTextureTypeCount * (bufferWrite ? 1 : 0) + shaderTextureType;
2307
2308    auto &shader = mPixelsCopyComputeShaders[formatIDValue][index2];
2309
2310    if (!shader)
2311    {
2312        // Pipeline not cached, create it now:
2313        ANGLE_MTL_OBJC_SCOPE
2314        {
2315            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
2316                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
2317
2318            [funcConstants setConstantValue:&formatIDValue
2319                                       type:MTLDataTypeInt
2320                                   withName:COPY_FORMAT_TYPE_CONSTANT_NAME];
2321            [funcConstants setConstantValue:&shaderTextureType
2322                                       type:MTLDataTypeInt
2323                                   withName:PIXEL_COPY_TEXTURE_TYPE_CONSTANT_NAME];
2324
2325            NSString *shaderName = nil;
2326            if (bufferWrite)
2327            {
2328                shaderName = [NSString stringWithUTF8String:mWriteShaderName.c_str()];
2329            }
2330            else
2331            {
2332                shaderName = [NSString stringWithUTF8String:mReadShaderName.c_str()];
2333            }
2334
2335            ANGLE_TRY(
2336                EnsureSpecializedShaderInitialized(contextMtl, shaderName, funcConstants, &shader));
2337        }
2338    }
2339
2340    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, shader,
2341                                                             outComputePipeline);
2342}
2343
2344angle::Result CopyPixelsUtils::unpackPixelsFromBufferToTexture(
2345    ContextMtl *contextMtl,
2346    const angle::Format &srcAngleFormat,
2347    const CopyPixelsFromBufferParams &params)
2348{
2349    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
2350    ASSERT(cmdEncoder);
2351
2352    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2353    ANGLE_TRY(getPixelsCopyPipeline(contextMtl, srcAngleFormat, params.texture, false, &pipeline));
2354
2355    cmdEncoder->setComputePipelineState(pipeline);
2356    cmdEncoder->setBuffer(params.buffer, 0, 1);
2357    cmdEncoder->setTextureForWrite(params.texture, 0);
2358
2359    CopyPixelFromBufferUniforms options;
2360    options.copySize[0]       = params.textureArea.width;
2361    options.copySize[1]       = params.textureArea.height;
2362    options.copySize[2]       = params.textureArea.depth;
2363    options.bufferStartOffset = params.bufferStartOffset;
2364    options.pixelSize         = srcAngleFormat.pixelBytes;
2365    options.bufferRowPitch    = params.bufferRowPitch;
2366    options.bufferDepthPitch  = params.bufferDepthPitch;
2367    options.textureOffset[0]  = params.textureArea.x;
2368    options.textureOffset[1]  = params.textureArea.y;
2369    options.textureOffset[2]  = params.textureArea.z;
2370    cmdEncoder->setData(options, 0);
2371
2372    NSUInteger w                  = pipeline.get().threadExecutionWidth;
2373    MTLSize threadsPerThreadgroup = MTLSizeMake(w, 1, 1);
2374
2375    MTLSize threads =
2376        MTLSizeMake(params.textureArea.width, params.textureArea.height, params.textureArea.depth);
2377
2378    DispatchCompute(contextMtl, cmdEncoder,
2379                    /** allowNonUniform */ true, threads, threadsPerThreadgroup);
2380
2381    return angle::Result::Continue;
2382}
2383
2384angle::Result CopyPixelsUtils::packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
2385                                                             const angle::Format &dstAngleFormat,
2386                                                             const CopyPixelsToBufferParams &params)
2387{
2388    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
2389    ASSERT(cmdEncoder);
2390
2391    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2392    ANGLE_TRY(getPixelsCopyPipeline(contextMtl, dstAngleFormat, params.texture, true, &pipeline));
2393
2394    cmdEncoder->setComputePipelineState(pipeline);
2395    cmdEncoder->setTexture(params.texture, 0);
2396    cmdEncoder->setBufferForWrite(params.buffer, 0, 1);
2397
2398    WritePixelToBufferUniforms options;
2399    options.copySize[0]            = params.textureArea.width;
2400    options.copySize[1]            = params.textureArea.height;
2401    options.bufferStartOffset      = params.bufferStartOffset;
2402    options.pixelSize              = dstAngleFormat.pixelBytes;
2403    options.bufferRowPitch         = params.bufferRowPitch;
2404    options.textureOffset[0]       = params.textureArea.x;
2405    options.textureOffset[1]       = params.textureArea.y;
2406    options.textureLevel           = params.textureLevel.get();
2407    options.textureLayer           = params.textureSliceOrDeph;
2408    options.reverseTextureRowOrder = params.reverseTextureRowOrder;
2409    cmdEncoder->setData(options, 0);
2410
2411    NSUInteger w                  = pipeline.get().threadExecutionWidth;
2412    MTLSize threadsPerThreadgroup = MTLSizeMake(w, 1, 1);
2413
2414    MTLSize threads = MTLSizeMake(params.textureArea.width, params.textureArea.height, 1);
2415
2416    DispatchCompute(contextMtl, cmdEncoder,
2417                    /** allowNonUniform */ true, threads, threadsPerThreadgroup);
2418
2419    return angle::Result::Continue;
2420}
2421
2422angle::Result VertexFormatConversionUtils::convertVertexFormatToFloatCS(
2423    ContextMtl *contextMtl,
2424    const angle::Format &srcAngleFormat,
2425    const VertexFormatConvertParams &params)
2426{
2427    // Since vertex buffer doesn't depend on previous render commands we don't
2428    // need to end the current render encoder.
2429    ComputeCommandEncoder *cmdEncoder =
2430        contextMtl->getComputeCommandEncoderWithoutEndingRenderEncoder();
2431    ASSERT(cmdEncoder);
2432
2433    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2434    ANGLE_TRY(getFloatConverstionComputePipeline(contextMtl, srcAngleFormat, &pipeline));
2435
2436    ANGLE_TRY(setupCommonConvertVertexFormatToFloat(contextMtl, cmdEncoder, pipeline,
2437                                                    srcAngleFormat, params));
2438
2439    DispatchCompute(contextMtl, cmdEncoder, pipeline, params.vertexCount);
2440    return angle::Result::Continue;
2441}
2442
2443angle::Result VertexFormatConversionUtils::convertVertexFormatToFloatVS(
2444    const gl::Context *context,
2445    RenderCommandEncoder *cmdEncoder,
2446    const angle::Format &srcAngleFormat,
2447    const VertexFormatConvertParams &params)
2448{
2449    ContextMtl *contextMtl = GetImpl(context);
2450    ASSERT(cmdEncoder);
2451    ASSERT(contextMtl->getDisplay()->getFeatures().hasExplicitMemBarrier.enabled);
2452
2453    angle::ObjCPtr<id<MTLRenderPipelineState>> pipeline;
2454    ANGLE_TRY(getFloatConverstionRenderPipeline(contextMtl, cmdEncoder, srcAngleFormat, &pipeline));
2455
2456    ANGLE_TRY(setupCommonConvertVertexFormatToFloat(contextMtl, cmdEncoder, pipeline,
2457                                                    srcAngleFormat, params));
2458
2459    cmdEncoder->draw(MTLPrimitiveTypePoint, 0, params.vertexCount);
2460
2461    cmdEncoder->memoryBarrierWithResource(params.dstBuffer, MTLRenderStageVertex,
2462                                          MTLRenderStageVertex);
2463
2464    // Invalidate current context's state.
2465    // NOTE(hqle): Consider invalidating only affected states.
2466    contextMtl->invalidateState(context);
2467
2468    return angle::Result::Continue;
2469}
2470
2471template <typename EncoderType, typename PipelineType>
2472angle::Result VertexFormatConversionUtils::setupCommonConvertVertexFormatToFloat(
2473    ContextMtl *contextMtl,
2474    EncoderType cmdEncoder,
2475    const PipelineType &pipeline,
2476    const angle::Format &srcAngleFormat,
2477    const VertexFormatConvertParams &params)
2478{
2479    if (pipeline == nullptr)
2480    {
2481        return angle::Result::Stop;
2482    }
2483    SetPipelineState(cmdEncoder, pipeline);
2484    SetComputeOrVertexBuffer(cmdEncoder, params.srcBuffer, 0, 1);
2485    SetComputeOrVertexBufferForWrite(cmdEncoder, params.dstBuffer, 0, 2);
2486
2487    CopyVertexUniforms options;
2488    options.srcBufferStartOffset = params.srcBufferStartOffset;
2489    options.srcStride            = params.srcStride;
2490
2491    options.dstBufferStartOffset = params.dstBufferStartOffset;
2492    options.dstStride            = params.dstStride;
2493    options.dstComponents        = params.dstComponents;
2494
2495    options.vertexCount = params.vertexCount;
2496    SetComputeOrVertexData(cmdEncoder, options, 0);
2497
2498    return angle::Result::Continue;
2499}
2500
2501// Expand number of components per vertex's attribute
2502angle::Result VertexFormatConversionUtils::expandVertexFormatComponentsCS(
2503    ContextMtl *contextMtl,
2504    const angle::Format &srcAngleFormat,
2505    const VertexFormatConvertParams &params)
2506{
2507    // Since vertex buffer doesn't depend on previous render commands we don't
2508    // need to end the current render encoder.
2509    ComputeCommandEncoder *cmdEncoder =
2510        contextMtl->getComputeCommandEncoderWithoutEndingRenderEncoder();
2511    ASSERT(cmdEncoder);
2512
2513    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2514    ANGLE_TRY(getComponentsExpandComputePipeline(contextMtl, &pipeline));
2515
2516    ANGLE_TRY(setupCommonExpandVertexFormatComponents(contextMtl, cmdEncoder, pipeline,
2517                                                      srcAngleFormat, params));
2518
2519    DispatchCompute(contextMtl, cmdEncoder, pipeline, params.vertexCount);
2520    return angle::Result::Continue;
2521}
2522
2523angle::Result VertexFormatConversionUtils::expandVertexFormatComponentsVS(
2524    const gl::Context *context,
2525    RenderCommandEncoder *cmdEncoder,
2526    const angle::Format &srcAngleFormat,
2527    const VertexFormatConvertParams &params)
2528{
2529    ContextMtl *contextMtl = GetImpl(context);
2530    ASSERT(cmdEncoder);
2531    ASSERT(contextMtl->getDisplay()->getFeatures().hasExplicitMemBarrier.enabled);
2532
2533    angle::ObjCPtr<id<MTLRenderPipelineState>> pipeline;
2534    ANGLE_TRY(getComponentsExpandRenderPipeline(contextMtl, cmdEncoder, &pipeline));
2535
2536    ANGLE_TRY(setupCommonExpandVertexFormatComponents(contextMtl, cmdEncoder, pipeline,
2537                                                      srcAngleFormat, params));
2538
2539    cmdEncoder->draw(MTLPrimitiveTypePoint, 0, params.vertexCount);
2540
2541    cmdEncoder->memoryBarrierWithResource(params.dstBuffer, MTLRenderStageVertex,
2542                                          MTLRenderStageVertex);
2543
2544    // Invalidate current context's state.
2545    // NOTE(hqle): Consider invalidating only affected states.
2546    contextMtl->invalidateState(context);
2547
2548    return angle::Result::Continue;
2549}
2550
2551template <typename EncoderType, typename PipelineType>
2552angle::Result VertexFormatConversionUtils::setupCommonExpandVertexFormatComponents(
2553    ContextMtl *contextMtl,
2554    EncoderType cmdEncoder,
2555    const PipelineType &pipeline,
2556    const angle::Format &srcAngleFormat,
2557    const VertexFormatConvertParams &params)
2558{
2559    if (pipeline == nullptr)
2560    {
2561        return angle::Result::Stop;
2562    }
2563    SetPipelineState(cmdEncoder, pipeline);
2564    SetComputeOrVertexBuffer(cmdEncoder, params.srcBuffer, 0, 1);
2565    SetComputeOrVertexBufferForWrite(cmdEncoder, params.dstBuffer, 0, 2);
2566
2567    CopyVertexUniforms options;
2568    options.srcBufferStartOffset = params.srcBufferStartOffset;
2569    options.srcStride            = params.srcStride;
2570    options.srcComponentBytes    = srcAngleFormat.pixelBytes / srcAngleFormat.channelCount;
2571    options.srcComponents        = srcAngleFormat.channelCount;
2572    options.srcDefaultAlphaData  = params.srcDefaultAlphaData;
2573
2574    options.dstBufferStartOffset = params.dstBufferStartOffset;
2575    options.dstStride            = params.dstStride;
2576    options.dstComponents        = params.dstComponents;
2577
2578    options.vertexCount = params.vertexCount;
2579    SetComputeOrVertexData(cmdEncoder, options, 0);
2580
2581    return angle::Result::Continue;
2582}
2583
2584angle::Result VertexFormatConversionUtils::getComponentsExpandComputePipeline(
2585    ContextMtl *contextMtl,
2586    angle::ObjCPtr<id<MTLComputePipelineState>> *outPipelineState)
2587{
2588    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"expandVertexFormatComponentsCS",
2589                                      &mComponentsExpandComputeShader));
2590    return contextMtl->getPipelineCache().getComputePipeline(
2591        contextMtl, mComponentsExpandComputeShader, outPipelineState);
2592}
2593
2594angle::Result VertexFormatConversionUtils::getComponentsExpandRenderPipeline(
2595    ContextMtl *contextMtl,
2596    RenderCommandEncoder *cmdEncoder,
2597    angle::ObjCPtr<id<MTLRenderPipelineState>> *outPipelineState)
2598{
2599    ANGLE_MTL_OBJC_SCOPE
2600    {
2601        if (!mComponentsExpandVertexShader)
2602        {
2603            id<MTLLibrary> shaderLib      = contextMtl->getDisplay()->getDefaultShadersLib();
2604            mComponentsExpandVertexShader = angle::adoptObjCPtr(
2605                [shaderLib newFunctionWithName:@"expandVertexFormatComponentsVS"]);
2606            ANGLE_CHECK(contextMtl, mComponentsExpandVertexShader, gl::err::kInternalError,
2607                        GL_INVALID_OPERATION);
2608        }
2609
2610        RenderPipelineDesc pipelineDesc =
2611            GetComputingVertexShaderOnlyRenderPipelineDesc(cmdEncoder);
2612
2613        return contextMtl->getPipelineCache().getRenderPipeline(
2614            contextMtl, mComponentsExpandVertexShader, nullptr, pipelineDesc, outPipelineState);
2615    }
2616}
2617
2618angle::Result VertexFormatConversionUtils::getFloatConverstionComputePipeline(
2619    ContextMtl *contextMtl,
2620    const angle::Format &srcAngleFormat,
2621    angle::ObjCPtr<id<MTLComputePipelineState>> *outPipelineState)
2622{
2623    int formatIDValue = static_cast<int>(srcAngleFormat.id);
2624
2625    auto &shader = mConvertToFloatCompPipelineCaches[formatIDValue];
2626
2627    if (!shader)
2628    {
2629        // Pipeline not cached, create it now:
2630        ANGLE_MTL_OBJC_SCOPE
2631        {
2632            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
2633                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
2634
2635            [funcConstants setConstantValue:&formatIDValue
2636                                       type:MTLDataTypeInt
2637                                   withName:COPY_FORMAT_TYPE_CONSTANT_NAME];
2638
2639            ANGLE_TRY(EnsureSpecializedShaderInitialized(
2640                contextMtl, @"convertToFloatVertexFormatCS", funcConstants, &shader));
2641        }
2642    }
2643
2644    return contextMtl->getPipelineCache().getComputePipeline(contextMtl, shader, outPipelineState);
2645}
2646
2647angle::Result VertexFormatConversionUtils::getFloatConverstionRenderPipeline(
2648    ContextMtl *contextMtl,
2649    RenderCommandEncoder *cmdEncoder,
2650    const angle::Format &srcAngleFormat,
2651    angle::ObjCPtr<id<MTLRenderPipelineState>> *outPipelineState)
2652{
2653    ANGLE_MTL_OBJC_SCOPE
2654    {
2655        int formatIDValue = static_cast<int>(srcAngleFormat.id);
2656
2657        if (!mConvertToFloatVertexShaders[formatIDValue])
2658        {
2659            NSError *err             = nil;
2660            id<MTLLibrary> shaderLib = contextMtl->getDisplay()->getDefaultShadersLib();
2661            angle::ObjCPtr<MTLFunctionConstantValues> funcConstants =
2662                angle::adoptObjCPtr([[MTLFunctionConstantValues alloc] init]);
2663
2664            [funcConstants setConstantValue:&formatIDValue
2665                                       type:MTLDataTypeInt
2666                                   withName:COPY_FORMAT_TYPE_CONSTANT_NAME];
2667
2668            mConvertToFloatVertexShaders[formatIDValue] =
2669                angle::adoptObjCPtr([shaderLib newFunctionWithName:@"convertToFloatVertexFormatVS"
2670                                                    constantValues:funcConstants
2671                                                             error:&err]);
2672            ANGLE_MTL_CHECK(contextMtl, mConvertToFloatVertexShaders[formatIDValue], err);
2673        }
2674
2675        RenderPipelineDesc pipelineDesc =
2676            GetComputingVertexShaderOnlyRenderPipelineDesc(cmdEncoder);
2677
2678        return contextMtl->getPipelineCache().getRenderPipeline(
2679            contextMtl, mConvertToFloatVertexShaders[formatIDValue], nullptr, pipelineDesc,
2680            outPipelineState);
2681    }
2682}
2683
2684angle::Result BlockLinearizationUtils::linearizeBlocks(ContextMtl *contextMtl,
2685                                                       const BlockLinearizationParams &params)
2686{
2687    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
2688    ASSERT(cmdEncoder);
2689
2690    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2691    ANGLE_TRY(getBlockLinearizationComputePipeline(contextMtl, &pipeline));
2692    cmdEncoder->setComputePipelineState(pipeline);
2693
2694    // Block layout
2695    ASSERT(params.blocksWide >= 2 && params.blocksHigh >= 2);
2696    const uint32_t dimensions[2] = {params.blocksWide, params.blocksHigh};
2697    cmdEncoder->setData(dimensions, 0);
2698
2699    // Buffer with original PVRTC1 blocks
2700    cmdEncoder->setBuffer(params.srcBuffer, params.srcBufferOffset, 1);
2701
2702    // Buffer to hold linearized PVRTC1 blocks
2703    cmdEncoder->setBufferForWrite(params.dstBuffer, 0, 2);
2704
2705    NSUInteger w                  = pipeline.get().threadExecutionWidth;
2706    NSUInteger h                  = pipeline.get().maxTotalThreadsPerThreadgroup / w;
2707    MTLSize threadsPerThreadgroup = MTLSizeMake(w, h, 1);
2708    MTLSize threads               = MTLSizeMake(params.blocksWide, params.blocksHigh, 1);
2709    DispatchCompute(contextMtl, cmdEncoder,
2710                    /** allowNonUniform */ true, threads, threadsPerThreadgroup);
2711    return angle::Result::Continue;
2712}
2713
2714angle::Result BlockLinearizationUtils::getBlockLinearizationComputePipeline(
2715    ContextMtl *contextMtl,
2716    angle::ObjCPtr<id<MTLComputePipelineState>> *outPipelineState)
2717{
2718    ANGLE_TRY(
2719        EnsureShaderInitialized(contextMtl, @"linearizeBlocks", &mLinearizeBlocksComputeShader));
2720    return contextMtl->getPipelineCache().getComputePipeline(
2721        contextMtl, mLinearizeBlocksComputeShader, outPipelineState);
2722}
2723
2724angle::Result DepthSaturationUtils::saturateDepth(ContextMtl *contextMtl,
2725                                                  const DepthSaturationParams &params)
2726{
2727    ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
2728    ASSERT(cmdEncoder);
2729
2730    angle::ObjCPtr<id<MTLComputePipelineState>> pipeline;
2731    ANGLE_TRY(getDepthSaturationComputePipeline(contextMtl, &pipeline));
2732    cmdEncoder->setComputePipelineState(pipeline);
2733
2734    // Image layout
2735    ASSERT(params.dstWidth > 0 && params.dstHeight > 0);
2736    ASSERT(params.srcPitch >= params.dstWidth);
2737    const uint32_t dimensions[4] = {params.dstWidth, params.dstHeight, params.srcPitch, 0};
2738    cmdEncoder->setData(dimensions, 0);
2739
2740    cmdEncoder->setBuffer(params.srcBuffer, params.srcBufferOffset, 1);
2741    cmdEncoder->setBuffer(params.dstBuffer, 0, 2);
2742
2743    NSUInteger w                  = pipeline.get().threadExecutionWidth;
2744    NSUInteger h                  = pipeline.get().maxTotalThreadsPerThreadgroup / w;
2745    MTLSize threadsPerThreadgroup = MTLSizeMake(w, h, 1);
2746    MTLSize threads               = MTLSizeMake(params.dstWidth, params.dstHeight, 1);
2747    DispatchCompute(contextMtl, cmdEncoder,
2748                    /** allowNonUniform */ true, threads, threadsPerThreadgroup);
2749    return angle::Result::Continue;
2750}
2751
2752angle::Result DepthSaturationUtils::getDepthSaturationComputePipeline(
2753    ContextMtl *contextMtl,
2754    angle::ObjCPtr<id<MTLComputePipelineState>> *outPipelineState)
2755{
2756    ANGLE_TRY(EnsureShaderInitialized(contextMtl, @"saturateDepth", &mSaturateDepthComputeShader));
2757    return contextMtl->getPipelineCache().getComputePipeline(
2758        contextMtl, mSaturateDepthComputeShader, outPipelineState);
2759}
2760
2761}  // namespace mtl
2762}  // namespace rx
2763