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