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