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// ProgramMtl.mm:
7//    Implements the class methods for ProgramMtl.
8//
9
10#include "libANGLE/renderer/metal/ProgramMtl.h"
11
12#include <TargetConditionals.h>
13
14#include <sstream>
15
16#include "common/debug.h"
17#include "common/system_utils.h"
18#include "libANGLE/Context.h"
19#include "libANGLE/ProgramLinkedResources.h"
20#include "libANGLE/renderer/metal/BufferMtl.h"
21#include "libANGLE/renderer/metal/CompilerMtl.h"
22#include "libANGLE/renderer/metal/ContextMtl.h"
23#include "libANGLE/renderer/metal/DisplayMtl.h"
24#include "libANGLE/renderer/metal/TextureMtl.h"
25#include "libANGLE/renderer/metal/mtl_glslang_mtl_utils.h"
26#include "libANGLE/renderer/metal/mtl_utils.h"
27#include "libANGLE/renderer/renderer_utils.h"
28
29#if ANGLE_ENABLE_METAL_SPIRV
30#    include "libANGLE/renderer/metal/mtl_glslang_utils.h"
31#endif
32
33namespace rx
34{
35
36namespace
37{
38
39#define SHADER_ENTRY_NAME @"main0"
40#if ANGLE_ENABLE_METAL_SPIRV
41constexpr char kSpirvCrossSpecConstSuffix[] = "_tmp";
42#endif
43template <typename T>
44class ScopedAutoClearVector
45{
46  public:
47    ScopedAutoClearVector(std::vector<T> *array) : mArray(*array) {}
48    ~ScopedAutoClearVector() { mArray.clear(); }
49
50  private:
51    std::vector<T> &mArray;
52};
53
54angle::Result StreamUniformBufferData(ContextMtl *contextMtl,
55                                      mtl::BufferPool *dynamicBuffer,
56                                      const uint8_t *sourceData,
57                                      size_t bytesToAllocate,
58                                      size_t sizeToCopy,
59                                      mtl::BufferRef *bufferOut,
60                                      size_t *bufferOffsetOut)
61{
62    uint8_t *dst = nullptr;
63    dynamicBuffer->releaseInFlightBuffers(contextMtl);
64    ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, bufferOut, bufferOffsetOut,
65                                      nullptr));
66    memcpy(dst, sourceData, sizeToCopy);
67
68    ANGLE_TRY(dynamicBuffer->commit(contextMtl));
69    return angle::Result::Continue;
70}
71
72void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
73                             gl::Shader *shader,
74                             sh::BlockLayoutMap *blockLayoutMapOut,
75                             size_t *blockSizeOut)
76{
77    if (uniforms.empty())
78    {
79        *blockSizeOut = 0;
80        return;
81    }
82
83    sh::Std140BlockEncoder blockEncoder;
84    sh::GetActiveUniformBlockInfo(uniforms, "", &blockEncoder, blockLayoutMapOut);
85
86    size_t blockSize = blockEncoder.getCurrentOffset();
87
88    // TODO(jmadill): I think we still need a valid block for the pipeline even if zero sized.
89    if (blockSize == 0)
90    {
91        *blockSizeOut = 0;
92        return;
93    }
94
95    // Need to round up to multiple of vec4
96    *blockSizeOut = roundUp(blockSize, static_cast<size_t>(16));
97    return;
98}
99
100inline NSDictionary<NSString *, NSObject *> *getDefaultSubstitutionDictionary()
101{
102    return @{};
103}
104
105template <typename T>
106void UpdateDefaultUniformBlock(GLsizei count,
107                               uint32_t arrayIndex,
108                               int componentCount,
109                               const T *v,
110                               const sh::BlockMemberInfo &layoutInfo,
111                               angle::MemoryBuffer *uniformData)
112{
113    const int elementSize = sizeof(T) * componentCount;
114
115    uint8_t *dst = uniformData->data() + layoutInfo.offset;
116    if (layoutInfo.arrayStride == 0 || layoutInfo.arrayStride == elementSize)
117    {
118        uint32_t arrayOffset = arrayIndex * layoutInfo.arrayStride;
119        uint8_t *writePtr    = dst + arrayOffset;
120        ASSERT(writePtr + (elementSize * count) <= uniformData->data() + uniformData->size());
121        memcpy(writePtr, v, elementSize * count);
122    }
123    else
124    {
125        // Have to respect the arrayStride between each element of the array.
126        int maxIndex = arrayIndex + count;
127        for (int writeIndex = arrayIndex, readIndex = 0; writeIndex < maxIndex;
128             writeIndex++, readIndex++)
129        {
130            const int arrayOffset = writeIndex * layoutInfo.arrayStride;
131            uint8_t *writePtr     = dst + arrayOffset;
132            const T *readPtr      = v + (readIndex * componentCount);
133            ASSERT(writePtr + elementSize <= uniformData->data() + uniformData->size());
134            memcpy(writePtr, readPtr, elementSize);
135        }
136    }
137}
138
139template <typename T>
140void ReadFromDefaultUniformBlock(int componentCount,
141                                 uint32_t arrayIndex,
142                                 T *dst,
143                                 const sh::BlockMemberInfo &layoutInfo,
144                                 const angle::MemoryBuffer *uniformData)
145{
146    ASSERT(layoutInfo.offset != -1);
147
148    const int elementSize = sizeof(T) * componentCount;
149    const uint8_t *source = uniformData->data() + layoutInfo.offset;
150
151    if (layoutInfo.arrayStride == 0 || layoutInfo.arrayStride == elementSize)
152    {
153        const uint8_t *readPtr = source + arrayIndex * layoutInfo.arrayStride;
154        memcpy(dst, readPtr, elementSize);
155    }
156    else
157    {
158        // Have to respect the arrayStride between each element of the array.
159        const int arrayOffset  = arrayIndex * layoutInfo.arrayStride;
160        const uint8_t *readPtr = source + arrayOffset;
161        memcpy(dst, readPtr, elementSize);
162    }
163}
164
165class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
166{
167  public:
168    sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
169};
170
171void InitArgumentBufferEncoder(mtl::Context *context,
172                               id<MTLFunction> function,
173                               uint32_t bufferIndex,
174                               ProgramArgumentBufferEncoderMtl *encoder)
175{
176    encoder->metalArgBufferEncoder = [function newArgumentEncoderWithBufferIndex:bufferIndex];
177    if (encoder->metalArgBufferEncoder)
178    {
179        encoder->bufferPool.initialize(context, encoder->metalArgBufferEncoder.get().encodedLength,
180                                       mtl::kArgumentBufferOffsetAlignment, 0);
181    }
182}
183
184}  // namespace
185
186// ProgramArgumentBufferEncoderMtl implementation
187void ProgramArgumentBufferEncoderMtl::reset(ContextMtl *contextMtl)
188{
189    metalArgBufferEncoder = nil;
190    bufferPool.destroy(contextMtl);
191}
192
193// ProgramShaderObjVariantMtl implementation
194void ProgramShaderObjVariantMtl::reset(ContextMtl *contextMtl)
195{
196    metalShader = nil;
197
198    uboArgBufferEncoder.reset(contextMtl);
199
200    translatedSrcInfo = nullptr;
201}
202
203// ProgramMtl implementation
204ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {}
205
206ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default;
207
208ProgramMtl::ProgramMtl(const gl::ProgramState &state)
209    : ProgramImpl(state),
210      mProgramHasFlatAttributes(false),
211      mShadowCompareModes(),
212      mMetalRenderPipelineCache(this),
213      mAuxBufferPool(nullptr)
214{}
215
216ProgramMtl::~ProgramMtl() {}
217
218void ProgramMtl::destroy(const gl::Context *context)
219{
220    auto contextMtl = mtl::GetImpl(context);
221    if (mAuxBufferPool)
222    {
223        mAuxBufferPool->destroy(contextMtl);
224        delete mAuxBufferPool;
225        mAuxBufferPool = nullptr;
226    }
227    reset(contextMtl);
228}
229
230void ProgramMtl::reset(ContextMtl *context)
231{
232    mProgramHasFlatAttributes = false;
233
234    for (auto &block : mDefaultUniformBlocks)
235    {
236        block.uniformLayout.clear();
237    }
238
239    for (gl::ShaderType shaderType : gl::AllShaderTypes())
240    {
241        mMslShaderTranslateInfo[shaderType].reset();
242        mCurrentShaderVariants[shaderType] = nullptr;
243    }
244    mMslXfbOnlyVertexShaderInfo.reset();
245
246    for (ProgramShaderObjVariantMtl &var : mVertexShaderVariants)
247    {
248        var.reset(context);
249    }
250    for (ProgramShaderObjVariantMtl &var : mFragmentShaderVariants)
251    {
252        var.reset(context);
253    }
254    if (mAuxBufferPool)
255    {
256        if (mAuxBufferPool->reset(context, mtl::kDefaultUniformsMaxSize * 2,
257                                  mtl::kUniformBufferSettingOffsetMinAlignment,
258                                  3) != angle::Result::Continue)
259        {
260            mAuxBufferPool->destroy(context);
261            delete mAuxBufferPool;
262            mAuxBufferPool = nullptr;
263        }
264    }
265    mMetalRenderPipelineCache.clear();
266}
267
268void ProgramMtl::saveTranslatedShaders(gl::BinaryOutputStream *stream)
269{
270    // Write out shader sources for all shader types
271    for (const gl::ShaderType shaderType : gl::AllShaderTypes())
272    {
273        stream->writeString(mMslShaderTranslateInfo[shaderType].metalShaderSource);
274    }
275    stream->writeString(mMslXfbOnlyVertexShaderInfo.metalShaderSource);
276}
277
278void ProgramMtl::loadTranslatedShaders(gl::BinaryInputStream *stream)
279{
280    // Read in shader sources for all shader types
281    for (const gl::ShaderType shaderType : gl::AllShaderTypes())
282    {
283        mMslShaderTranslateInfo[shaderType].metalShaderSource = stream->readString();
284    }
285    mMslXfbOnlyVertexShaderInfo.metalShaderSource = stream->readString();
286}
287
288std::unique_ptr<rx::LinkEvent> ProgramMtl::load(const gl::Context *context,
289                                                gl::BinaryInputStream *stream,
290                                                gl::InfoLog &infoLog)
291{
292
293    return std::make_unique<LinkEventDone>(linkTranslatedShaders(context, stream, infoLog));
294}
295
296void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream)
297{
298    saveTranslatedShaders(stream);
299    saveShaderInternalInfo(stream);
300    saveDefaultUniformBlocksInfo(stream);
301}
302
303void ProgramMtl::setBinaryRetrievableHint(bool retrievable)
304{
305    UNIMPLEMENTED();
306}
307
308void ProgramMtl::setSeparable(bool separable)
309{
310    UNIMPLEMENTED();
311}
312
313std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
314                                            const gl::ProgramLinkedResources &resources,
315                                            gl::InfoLog &infoLog,
316                                            const gl::ProgramMergedVaryings &mergedVaryings)
317{
318    // Link resources before calling GetShaderSource to make sure they are ready for the set/binding
319    // assignment done in that function.
320    linkResources(resources);
321
322    // NOTE(hqle): Parallelize linking.
323    return std::make_unique<LinkEventDone>(linkImpl(context, resources, infoLog));
324}
325
326#if ANGLE_ENABLE_METAL_SPIRV
327angle::Result ProgramMtl::linkImplSpirv(const gl::Context *glContext,
328                                        const gl::ProgramLinkedResources &resources,
329                                        gl::InfoLog &infoLog)
330{
331    ContextMtl *contextMtl = mtl::GetImpl(glContext);
332
333    reset(contextMtl);
334
335    ANGLE_TRY(initDefaultUniformBlocks(glContext));
336
337    // Gather variable info and transform sources.
338    gl::ShaderMap<const angle::spirv::Blob *> spirvBlobs;
339    ShaderInterfaceVariableInfoMap variableInfoMap;
340    ShaderInterfaceVariableInfoMap xfbOnlyVariableInfoMap;
341    mtl::GlslangGetShaderSpirvCode(mState, resources, &spirvBlobs, &variableInfoMap,
342                                   &xfbOnlyVariableInfoMap);
343
344    // Convert GLSL to spirv code
345    gl::ShaderMap<angle::spirv::Blob> shaderCodes;
346    gl::ShaderMap<angle::spirv::Blob> xfbOnlyShaderCodes;  // only vertex shader is needed.
347    ANGLE_TRY(mtl::GlslangTransformSpirvCode(mState.getExecutable().getLinkedShaderStages(),
348                                             spirvBlobs, false, variableInfoMap, &shaderCodes));
349
350    if (!mState.getLinkedTransformFeedbackVaryings().empty())
351    {
352        gl::ShaderBitSet onlyVS;
353        onlyVS.set(gl::ShaderType::Vertex);
354        ANGLE_TRY(mtl::GlslangTransformSpirvCode(onlyVS, spirvBlobs, true, xfbOnlyVariableInfoMap,
355                                                 &xfbOnlyShaderCodes));
356    }
357
358    // Convert spirv code to MSL
359    ANGLE_TRY(mtl::SpirvCodeToMsl(contextMtl, mState, xfbOnlyVariableInfoMap, &shaderCodes,
360                                  &xfbOnlyShaderCodes[gl::ShaderType::Vertex],
361                                  &mMslShaderTranslateInfo, &mMslXfbOnlyVertexShaderInfo));
362
363    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
364    {
365        // Create actual Metal shader library
366        ANGLE_TRY(createMslShaderLib(contextMtl, shaderType, infoLog,
367                                     &mMslShaderTranslateInfo[shaderType],
368                                     getDefaultSubstitutionDictionary()));
369    }
370
371    return angle::Result::Continue;
372}
373#endif
374
375angle::Result ProgramMtl::linkImplDirect(const gl::Context *glContext,
376                                         const gl::ProgramLinkedResources &resources,
377                                         gl::InfoLog &infoLog)
378{
379    ContextMtl *contextMtl = mtl::GetImpl(glContext);
380
381    reset(contextMtl);
382    ANGLE_TRY(initDefaultUniformBlocks(glContext));
383    ShaderInterfaceVariableInfoMap variableInfoMap;
384
385    gl::ShaderMap<std::string> shaderSources;
386    gl::ShaderMap<std::string> translatedMslShaders;
387    mtl::MSLGetShaderSource(mState, resources, &shaderSources, &variableInfoMap);
388
389    ANGLE_TRY(mtl::GlslangGetMSL(glContext, mState, contextMtl->getCaps(), shaderSources,
390                                 variableInfoMap, &mMslShaderTranslateInfo, &translatedMslShaders,
391                                 mState.getExecutable().getTransformFeedbackBufferCount()));
392    mMslXfbOnlyVertexShaderInfo = mMslShaderTranslateInfo[gl::ShaderType::Vertex];
393    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
394    {
395        // Create actual Metal shader
396        ANGLE_TRY(createMslShaderLib(contextMtl, shaderType, infoLog,
397                                     &mMslShaderTranslateInfo[shaderType],
398                                     getDefaultSubstitutionDictionary()));
399    }
400    return angle::Result::Continue;
401}
402
403void ProgramMtl::linkUpdateHasFlatAttributes()
404{
405    mProgramHasFlatAttributes = false;
406
407    const auto &programInputs = mState.getProgramInputs();
408    for (auto &attribute : programInputs)
409    {
410        if (attribute.interpolation == sh::INTERPOLATION_FLAT)
411        {
412            mProgramHasFlatAttributes = true;
413            return;
414        }
415    }
416
417    const auto &flatVaryings =
418        mState.getAttachedShader(gl::ShaderType::Vertex)->getOutputVaryings();
419    for (auto &attribute : flatVaryings)
420    {
421        if (attribute.interpolation == sh::INTERPOLATION_FLAT)
422        {
423            mProgramHasFlatAttributes = true;
424            return;
425        }
426    }
427}
428
429angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
430                                   const gl::ProgramLinkedResources &resources,
431                                   gl::InfoLog &infoLog)
432{
433#if ANGLE_ENABLE_METAL_SPIRV
434    ContextMtl *contextMtl = mtl::GetImpl(glContext);
435    if (contextMtl->getDisplay()->useDirectToMetalCompiler())
436    {
437        ANGLE_TRY(linkImplDirect(glContext, resources, infoLog));
438    }
439    else
440    {
441        ANGLE_TRY(linkImplSpirv(glContext, resources, infoLog));
442    }
443#else
444    ANGLE_TRY(linkImplDirect(glContext, resources, infoLog));
445#endif
446    linkUpdateHasFlatAttributes();
447    return angle::Result::Continue;
448}
449
450angle::Result ProgramMtl::linkTranslatedShaders(const gl::Context *glContext,
451                                                gl::BinaryInputStream *stream,
452                                                gl::InfoLog &infoLog)
453{
454    ContextMtl *contextMtl = mtl::GetImpl(glContext);
455    // NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
456
457    reset(contextMtl);
458
459    loadTranslatedShaders(stream);
460    loadShaderInternalInfo(stream);
461    ANGLE_TRY(loadDefaultUniformBlocksInfo(glContext, stream));
462    ANGLE_TRY(createMslShaderLib(contextMtl, gl::ShaderType::Vertex, infoLog,
463                                 &mMslShaderTranslateInfo[gl::ShaderType::Vertex],
464                                 getDefaultSubstitutionDictionary()));
465    ANGLE_TRY(createMslShaderLib(contextMtl, gl::ShaderType::Fragment, infoLog,
466                                 &mMslShaderTranslateInfo[gl::ShaderType::Fragment],
467                                 getDefaultSubstitutionDictionary()));
468
469    return angle::Result::Continue;
470}
471
472mtl::BufferPool *ProgramMtl::getBufferPool(ContextMtl *context)
473{
474    if (mAuxBufferPool == nullptr)
475    {
476        mAuxBufferPool = new mtl::BufferPool(true);
477        mAuxBufferPool->initialize(context, mtl::kDefaultUniformsMaxSize * 2,
478                                   mtl::kUniformBufferSettingOffsetMinAlignment, 3);
479    }
480    return mAuxBufferPool;
481}
482void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)
483{
484    Std140BlockLayoutEncoderFactory std140EncoderFactory;
485    gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory);
486
487    linker.linkResources(mState, resources);
488}
489
490angle::Result ProgramMtl::initDefaultUniformBlocks(const gl::Context *glContext)
491{
492    // Process vertex and fragment uniforms into std140 packing.
493    gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
494    gl::ShaderMap<size_t> requiredBufferSize;
495    requiredBufferSize.fill(0);
496
497    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
498    {
499        gl::Shader *shader = mState.getAttachedShader(shaderType);
500        if (shader)
501        {
502            const std::vector<sh::Uniform> &uniforms = shader->getUniforms();
503            InitDefaultUniformBlock(uniforms, shader, &layoutMap[shaderType],
504                                    &requiredBufferSize[shaderType]);
505        }
506    }
507
508    // Init the default block layout info.
509    const auto &uniforms         = mState.getUniforms();
510    const auto &uniformLocations = mState.getUniformLocations();
511    for (size_t locSlot = 0; locSlot < uniformLocations.size(); ++locSlot)
512    {
513        const gl::VariableLocation &location = uniformLocations[locSlot];
514        gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
515
516        if (location.used() && !location.ignored)
517        {
518            const gl::LinkedUniform &uniform = uniforms[location.index];
519            if (uniform.isInDefaultBlock() && !uniform.isSampler())
520            {
521                std::string uniformName = uniform.name;
522                if (uniform.isArray())
523                {
524                    // Gets the uniform name without the [0] at the end.
525                    uniformName = gl::ParseResourceName(uniformName, nullptr);
526                }
527
528                bool found = false;
529
530                for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
531                {
532                    auto it = layoutMap[shaderType].find(uniformName);
533                    if (it != layoutMap[shaderType].end())
534                    {
535                        found                  = true;
536                        layoutInfo[shaderType] = it->second;
537                    }
538                }
539
540                ASSERT(found);
541            }
542        }
543
544        for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
545        {
546            mDefaultUniformBlocks[shaderType].uniformLayout.push_back(layoutInfo[shaderType]);
547        }
548    }
549
550    return resizeDefaultUniformBlocksMemory(glContext, requiredBufferSize);
551}
552
553angle::Result ProgramMtl::resizeDefaultUniformBlocksMemory(
554    const gl::Context *glContext,
555    const gl::ShaderMap<size_t> &requiredBufferSize)
556{
557    ContextMtl *contextMtl = mtl::GetImpl(glContext);
558
559    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
560    {
561        if (requiredBufferSize[shaderType] > 0)
562        {
563            ASSERT(requiredBufferSize[shaderType] <= mtl::kDefaultUniformsMaxSize);
564
565            if (!mDefaultUniformBlocks[shaderType].uniformData.resize(
566                    requiredBufferSize[shaderType]))
567            {
568                ANGLE_MTL_CHECK(contextMtl, false, GL_OUT_OF_MEMORY);
569            }
570
571            // Initialize uniform buffer memory to zero by default.
572            mDefaultUniformBlocks[shaderType].uniformData.fill(0);
573            mDefaultUniformBlocksDirty.set(shaderType);
574        }
575    }
576
577    return angle::Result::Continue;
578}
579
580angle::Result ProgramMtl::getSpecializedShader(mtl::Context *context,
581                                               gl::ShaderType shaderType,
582                                               const mtl::RenderPipelineDesc &renderPipelineDesc,
583                                               id<MTLFunction> *shaderOut)
584{
585    static_assert(YES == 1, "YES should have value of 1");
586#if ANGLE_ENABLE_METAL_SPIRV
587    const bool useSpirv = !context->getDisplay()->useDirectToMetalCompiler();
588#endif
589
590    mtl::TranslatedShaderInfo *translatedMslInfo = &mMslShaderTranslateInfo[shaderType];
591    ProgramShaderObjVariantMtl *shaderVariant;
592    MTLFunctionConstantValues *funcConstants = nil;
593
594    if (shaderType == gl::ShaderType::Vertex)
595    {
596        // For vertex shader, we need to create 3 variants, one with emulated rasterization
597        // discard, one with true rasterization discard and one without.
598        shaderVariant = &mVertexShaderVariants[renderPipelineDesc.rasterizationType];
599        if (shaderVariant->metalShader)
600        {
601            // Already created.
602            *shaderOut = shaderVariant->metalShader;
603            return angle::Result::Continue;
604        }
605
606        if (renderPipelineDesc.rasterizationType == mtl::RenderPipelineRasterization::Disabled)
607        {
608            // Special case: XFB output only vertex shader.
609            ASSERT(!mState.getLinkedTransformFeedbackVaryings().empty());
610            translatedMslInfo = &mMslXfbOnlyVertexShaderInfo;
611            if (!translatedMslInfo->metalLibrary)
612            {
613                // Lazily compile XFB only shader
614                gl::InfoLog infoLog;
615                ANGLE_TRY(createMslShaderLib(context, shaderType, infoLog,
616                                             &mMslXfbOnlyVertexShaderInfo,
617                                             @{@"TRANSFORM_FEEDBACK_ENABLED" : @"1"}));
618                translatedMslInfo->metalLibrary.get().label = @"TransformFeedback";
619            }
620        }
621
622        ANGLE_MTL_OBJC_SCOPE
623        {
624            BOOL emulateDiscard = renderPipelineDesc.rasterizationType ==
625                                  mtl::RenderPipelineRasterization::EmulatedDiscard;
626
627            NSString *discardEnabledStr;
628#if ANGLE_ENABLE_METAL_SPIRV
629            if (useSpirv)
630            {
631                discardEnabledStr =
632                    [NSString stringWithFormat:@"%s%s", sh::mtl::kRasterizerDiscardEnabledConstName,
633                                               kSpirvCrossSpecConstSuffix];
634            }
635            else
636#endif
637            {
638                discardEnabledStr =
639                    [NSString stringWithUTF8String:sh::mtl::kRasterizerDiscardEnabledConstName];
640            }
641
642            funcConstants = [[MTLFunctionConstantValues alloc] init];
643            [funcConstants setConstantValue:&emulateDiscard
644                                       type:MTLDataTypeBool
645                                   withName:discardEnabledStr];
646        }
647    }  // if (shaderType == gl::ShaderType::Vertex)
648    else if (shaderType == gl::ShaderType::Fragment)
649    {
650        // For fragment shader, we need to create 2 variants, one with sample coverage mask
651        // disabled, one with the mask enabled.
652        BOOL emulateCoverageMask = renderPipelineDesc.emulateCoverageMask;
653        shaderVariant            = &mFragmentShaderVariants[emulateCoverageMask];
654        if (shaderVariant->metalShader)
655        {
656            // Already created.
657            *shaderOut = shaderVariant->metalShader;
658            return angle::Result::Continue;
659        }
660
661        ANGLE_MTL_OBJC_SCOPE
662        {
663            NSString *coverageMaskEnabledStr;
664#if ANGLE_ENABLE_METAL_SPIRV
665            if (useSpirv)
666            {
667                coverageMaskEnabledStr =
668                    [NSString stringWithFormat:@"%s%s", sh::mtl::kCoverageMaskEnabledConstName,
669                                               kSpirvCrossSpecConstSuffix];
670            }
671            else
672#endif
673            {
674                coverageMaskEnabledStr =
675                    [NSString stringWithUTF8String:sh::mtl::kCoverageMaskEnabledConstName];
676            }
677
678            funcConstants = [[MTLFunctionConstantValues alloc] init];
679            [funcConstants setConstantValue:&emulateCoverageMask
680                                       type:MTLDataTypeBool
681                                   withName:coverageMaskEnabledStr];
682        }
683
684    }  // gl::ShaderType::Fragment
685    else
686    {
687        UNREACHABLE();
688        return angle::Result::Stop;
689    }
690    [funcConstants
691        setConstantValue:&(context->getDisplay()->getFeatures().allowSamplerCompareGradient.enabled)
692                    type:MTLDataTypeBool
693                withName:@"ANGLEUseSampleCompareGradient"];
694    [funcConstants
695        setConstantValue:&(context->getDisplay()->getFeatures().allowSamplerCompareLod.enabled)
696                    type:MTLDataTypeBool
697                withName:@"ANGLEUseSampleCompareLod"];
698    // Create Metal shader object
699    ANGLE_MTL_OBJC_SCOPE
700    {
701        ANGLE_TRY(CreateMslShader(context, translatedMslInfo->metalLibrary, SHADER_ENTRY_NAME,
702                                  funcConstants, &shaderVariant->metalShader));
703        [funcConstants ANGLE_MTL_AUTORELEASE];
704    }
705
706    // Store reference to the translated source for easily querying mapped bindings later.
707    shaderVariant->translatedSrcInfo = translatedMslInfo;
708
709    // Initialize argument buffer encoder if required
710    if (translatedMslInfo->hasUBOArgumentBuffer)
711    {
712        InitArgumentBufferEncoder(context, shaderVariant->metalShader,
713                                  mtl::kUBOArgumentBufferBindingIndex,
714                                  &shaderVariant->uboArgBufferEncoder);
715    }
716
717    *shaderOut = shaderVariant->metalShader;
718
719    return angle::Result::Continue;
720}
721
722bool ProgramMtl::hasSpecializedShader(gl::ShaderType shaderType,
723                                      const mtl::RenderPipelineDesc &renderPipelineDesc)
724{
725    return true;
726}
727
728angle::Result ProgramMtl::createMslShaderLib(
729    mtl::Context *context,
730    gl::ShaderType shaderType,
731    gl::InfoLog &infoLog,
732    mtl::TranslatedShaderInfo *translatedMslInfo,
733    NSDictionary<NSString *, NSObject *> *substitutionMacros)
734{
735    ANGLE_MTL_OBJC_SCOPE
736    {
737        DisplayMtl *display     = context->getDisplay();
738        id<MTLDevice> mtlDevice = display->getMetalDevice();
739
740        // Convert to actual binary shader
741        mtl::AutoObjCPtr<NSError *> err = nil;
742        bool disableFastMath = (context->getDisplay()->getFeatures().intelDisableFastMath.enabled &&
743                                translatedMslInfo->hasInvariantOrAtan);
744        translatedMslInfo->metalLibrary =
745            mtl::CreateShaderLibrary(mtlDevice, translatedMslInfo->metalShaderSource,
746                                     substitutionMacros, !disableFastMath, &err);
747        if (err && !translatedMslInfo->metalLibrary)
748        {
749            std::ostringstream ss;
750            ss << "Internal error compiling shader with Metal backend.\n";
751#if !defined(NDEBUG)
752            ss << err.get().localizedDescription.UTF8String << "\n";
753            ss << "-----\n";
754            ss << translatedMslInfo->metalShaderSource;
755            ss << "-----\n";
756#else
757            ss << "Please submit this shader, or website as a bug to https://bugs.webkit.org\n";
758#endif
759            ERR() << ss.str();
760
761            infoLog << ss.str();
762
763            ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION);
764        }
765
766        return angle::Result::Continue;
767    }
768}
769
770void ProgramMtl::saveDefaultUniformBlocksInfo(gl::BinaryOutputStream *stream)
771{
772    // Serializes the uniformLayout data of mDefaultUniformBlocks
773    for (gl::ShaderType shaderType : gl::AllShaderTypes())
774    {
775        const size_t uniformCount = mDefaultUniformBlocks[shaderType].uniformLayout.size();
776        stream->writeInt<size_t>(uniformCount);
777        for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
778        {
779            sh::BlockMemberInfo &blockInfo =
780                mDefaultUniformBlocks[shaderType].uniformLayout[uniformIndex];
781            gl::WriteBlockMemberInfo(stream, blockInfo);
782        }
783    }
784
785    // Serializes required uniform block memory sizes
786    for (gl::ShaderType shaderType : gl::AllShaderTypes())
787    {
788        stream->writeInt(mDefaultUniformBlocks[shaderType].uniformData.size());
789    }
790}
791
792angle::Result ProgramMtl::loadDefaultUniformBlocksInfo(const gl::Context *glContext,
793                                                       gl::BinaryInputStream *stream)
794{
795    gl::ShaderMap<size_t> requiredBufferSize;
796    requiredBufferSize.fill(0);
797
798    // Deserializes the uniformLayout data of mDefaultUniformBlocks
799    for (gl::ShaderType shaderType : gl::AllShaderTypes())
800    {
801        const size_t uniformCount = stream->readInt<size_t>();
802        for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
803        {
804            sh::BlockMemberInfo blockInfo;
805            gl::LoadBlockMemberInfo(stream, &blockInfo);
806            mDefaultUniformBlocks[shaderType].uniformLayout.push_back(blockInfo);
807        }
808    }
809
810    // Deserializes required uniform block memory sizes
811    for (gl::ShaderType shaderType : gl::AllShaderTypes())
812    {
813        requiredBufferSize[shaderType] = stream->readInt<size_t>();
814    }
815
816    return resizeDefaultUniformBlocksMemory(glContext, requiredBufferSize);
817}
818
819void ProgramMtl::saveShaderInternalInfo(gl::BinaryOutputStream *stream)
820{
821    for (gl::ShaderType shaderType : gl::AllShaderTypes())
822    {
823        stream->writeInt<int>(mMslShaderTranslateInfo[shaderType].hasUBOArgumentBuffer);
824        for (const mtl::SamplerBinding &binding :
825             mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
826        {
827            stream->writeInt<uint32_t>(binding.textureBinding);
828            stream->writeInt<uint32_t>(binding.samplerBinding);
829        }
830
831        for (uint32_t uboBinding : mMslShaderTranslateInfo[shaderType].actualUBOBindings)
832        {
833            stream->writeInt<uint32_t>(uboBinding);
834        }
835        stream->writeBool(mMslShaderTranslateInfo[shaderType].hasInvariantOrAtan);
836    }
837    for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
838    {
839        stream->writeInt(
840            mMslShaderTranslateInfo[gl::ShaderType::Vertex].actualXFBBindings[xfbBindIndex]);
841    }
842
843    // Write out XFB info.
844    {
845        stream->writeInt<int>(mMslXfbOnlyVertexShaderInfo.hasUBOArgumentBuffer);
846        for (mtl::SamplerBinding &binding : mMslXfbOnlyVertexShaderInfo.actualSamplerBindings)
847        {
848            stream->writeInt<uint32_t>(binding.textureBinding);
849            stream->writeInt<uint32_t>(binding.samplerBinding);
850        }
851
852        for (uint32_t &uboBinding : mMslXfbOnlyVertexShaderInfo.actualUBOBindings)
853        {
854            stream->writeInt<uint32_t>(uboBinding);
855        }
856        for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
857        {
858            stream->writeInt(mMslXfbOnlyVertexShaderInfo.actualXFBBindings[xfbBindIndex]);
859        }
860    }
861
862    stream->writeBool(mProgramHasFlatAttributes);
863}
864
865void ProgramMtl::loadShaderInternalInfo(gl::BinaryInputStream *stream)
866{
867    for (gl::ShaderType shaderType : gl::AllShaderTypes())
868    {
869        mMslShaderTranslateInfo[shaderType].hasUBOArgumentBuffer = stream->readInt<int>() != 0;
870        for (mtl::SamplerBinding &binding :
871             mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
872        {
873            binding.textureBinding = stream->readInt<uint32_t>();
874            binding.samplerBinding = stream->readInt<uint32_t>();
875        }
876
877        for (uint32_t &uboBinding : mMslShaderTranslateInfo[shaderType].actualUBOBindings)
878        {
879            uboBinding = stream->readInt<uint32_t>();
880        }
881        mMslShaderTranslateInfo[shaderType].hasInvariantOrAtan = stream->readBool();
882    }
883
884    for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
885    {
886        stream->readInt(
887            &mMslShaderTranslateInfo[gl::ShaderType::Vertex].actualXFBBindings[xfbBindIndex]);
888    }
889    // Load Transform Feedback info
890    {
891        mMslXfbOnlyVertexShaderInfo.hasUBOArgumentBuffer = stream->readInt<int>() != 0;
892        for (mtl::SamplerBinding &binding : mMslXfbOnlyVertexShaderInfo.actualSamplerBindings)
893        {
894            binding.textureBinding = stream->readInt<uint32_t>();
895            binding.samplerBinding = stream->readInt<uint32_t>();
896        }
897
898        for (uint32_t &uboBinding : mMslXfbOnlyVertexShaderInfo.actualUBOBindings)
899        {
900            uboBinding = stream->readInt<uint32_t>();
901        }
902        for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
903        {
904            stream->readInt(&mMslXfbOnlyVertexShaderInfo.actualXFBBindings[xfbBindIndex]);
905        }
906        mMslXfbOnlyVertexShaderInfo.metalLibrary = nullptr;
907    }
908
909    mProgramHasFlatAttributes = stream->readBool();
910}
911
912GLboolean ProgramMtl::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
913{
914    // No-op. The spec is very vague about the behavior of validation.
915    return GL_TRUE;
916}
917
918template <typename T>
919void ProgramMtl::setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType)
920{
921    const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
922    const gl::LinkedUniform &linkedUniform   = mState.getUniforms()[locationInfo.index];
923
924    if (linkedUniform.isSampler())
925    {
926        // Sampler binding has changed.
927        mSamplerBindingsDirty.set();
928        return;
929    }
930
931    if (linkedUniform.typeInfo->type == entryPointType)
932    {
933        for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
934        {
935            DefaultUniformBlock &uniformBlock     = mDefaultUniformBlocks[shaderType];
936            const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
937
938            // Assume an offset of -1 means the block is unused.
939            if (layoutInfo.offset == -1)
940            {
941                continue;
942            }
943
944            const GLint componentCount = linkedUniform.typeInfo->componentCount;
945            UpdateDefaultUniformBlock(count, locationInfo.arrayIndex, componentCount, v, layoutInfo,
946                                      &uniformBlock.uniformData);
947            mDefaultUniformBlocksDirty.set(shaderType);
948        }
949    }
950    else
951    {
952        for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
953        {
954            DefaultUniformBlock &uniformBlock     = mDefaultUniformBlocks[shaderType];
955            const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
956
957            // Assume an offset of -1 means the block is unused.
958            if (layoutInfo.offset == -1)
959            {
960                continue;
961            }
962
963            const GLint componentCount = linkedUniform.typeInfo->componentCount;
964
965            ASSERT(linkedUniform.typeInfo->type == gl::VariableBoolVectorType(entryPointType));
966
967            GLint initialArrayOffset =
968                locationInfo.arrayIndex * layoutInfo.arrayStride + layoutInfo.offset;
969            for (GLint i = 0; i < count; i++)
970            {
971                GLint elementOffset = i * layoutInfo.arrayStride + initialArrayOffset;
972                GLint *dest =
973                    reinterpret_cast<GLint *>(uniformBlock.uniformData.data() + elementOffset);
974                const T *source = v + i * componentCount;
975
976                for (int c = 0; c < componentCount; c++)
977                {
978                    dest[c] = (source[c] == static_cast<T>(0)) ? GL_FALSE : GL_TRUE;
979                }
980            }
981
982            mDefaultUniformBlocksDirty.set(shaderType);
983        }
984    }
985}
986
987template <typename T>
988void ProgramMtl::getUniformImpl(GLint location, T *v, GLenum entryPointType) const
989{
990    const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
991    const gl::LinkedUniform &linkedUniform   = mState.getUniforms()[locationInfo.index];
992
993    ASSERT(!linkedUniform.isSampler());
994
995    const gl::ShaderType shaderType = linkedUniform.getFirstShaderTypeWhereActive();
996    ASSERT(shaderType != gl::ShaderType::InvalidEnum);
997
998    const DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
999    const sh::BlockMemberInfo &layoutInfo   = uniformBlock.uniformLayout[location];
1000
1001    ASSERT(linkedUniform.typeInfo->componentType == entryPointType ||
1002           linkedUniform.typeInfo->componentType == gl::VariableBoolVectorType(entryPointType));
1003
1004    if (gl::IsMatrixType(linkedUniform.type))
1005    {
1006        const uint8_t *ptrToElement = uniformBlock.uniformData.data() + layoutInfo.offset +
1007                                      (locationInfo.arrayIndex * layoutInfo.arrayStride);
1008        GetMatrixUniform(linkedUniform.type, v, reinterpret_cast<const T *>(ptrToElement), false);
1009    }
1010    else
1011    {
1012        ReadFromDefaultUniformBlock(linkedUniform.typeInfo->componentCount, locationInfo.arrayIndex,
1013                                    v, layoutInfo, &uniformBlock.uniformData);
1014    }
1015}
1016
1017void ProgramMtl::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
1018{
1019    setUniformImpl(location, count, v, GL_FLOAT);
1020}
1021
1022void ProgramMtl::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
1023{
1024    setUniformImpl(location, count, v, GL_FLOAT_VEC2);
1025}
1026
1027void ProgramMtl::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
1028{
1029    setUniformImpl(location, count, v, GL_FLOAT_VEC3);
1030}
1031
1032void ProgramMtl::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
1033{
1034    setUniformImpl(location, count, v, GL_FLOAT_VEC4);
1035}
1036
1037void ProgramMtl::setUniform1iv(GLint startLocation, GLsizei count, const GLint *v)
1038{
1039    setUniformImpl(startLocation, count, v, GL_INT);
1040}
1041
1042void ProgramMtl::setUniform2iv(GLint location, GLsizei count, const GLint *v)
1043{
1044    setUniformImpl(location, count, v, GL_INT_VEC2);
1045}
1046
1047void ProgramMtl::setUniform3iv(GLint location, GLsizei count, const GLint *v)
1048{
1049    setUniformImpl(location, count, v, GL_INT_VEC3);
1050}
1051
1052void ProgramMtl::setUniform4iv(GLint location, GLsizei count, const GLint *v)
1053{
1054    setUniformImpl(location, count, v, GL_INT_VEC4);
1055}
1056
1057void ProgramMtl::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
1058{
1059    setUniformImpl(location, count, v, GL_UNSIGNED_INT);
1060}
1061
1062void ProgramMtl::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
1063{
1064    setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC2);
1065}
1066
1067void ProgramMtl::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
1068{
1069    setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC3);
1070}
1071
1072void ProgramMtl::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
1073{
1074    setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC4);
1075}
1076
1077template <int cols, int rows>
1078void ProgramMtl::setUniformMatrixfv(GLint location,
1079                                    GLsizei count,
1080                                    GLboolean transpose,
1081                                    const GLfloat *value)
1082{
1083    const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
1084    const gl::LinkedUniform &linkedUniform   = mState.getUniforms()[locationInfo.index];
1085
1086    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
1087    {
1088        DefaultUniformBlock &uniformBlock     = mDefaultUniformBlocks[shaderType];
1089        const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
1090
1091        // Assume an offset of -1 means the block is unused.
1092        if (layoutInfo.offset == -1)
1093        {
1094            continue;
1095        }
1096
1097        SetFloatUniformMatrixGLSL<cols, rows>::Run(
1098            locationInfo.arrayIndex, linkedUniform.getArraySizeProduct(), count, transpose, value,
1099            uniformBlock.uniformData.data() + layoutInfo.offset);
1100
1101        mDefaultUniformBlocksDirty.set(shaderType);
1102    }
1103}
1104
1105void ProgramMtl::setUniformMatrix2fv(GLint location,
1106                                     GLsizei count,
1107                                     GLboolean transpose,
1108                                     const GLfloat *value)
1109{
1110    setUniformMatrixfv<2, 2>(location, count, transpose, value);
1111}
1112
1113void ProgramMtl::setUniformMatrix3fv(GLint location,
1114                                     GLsizei count,
1115                                     GLboolean transpose,
1116                                     const GLfloat *value)
1117{
1118    setUniformMatrixfv<3, 3>(location, count, transpose, value);
1119}
1120
1121void ProgramMtl::setUniformMatrix4fv(GLint location,
1122                                     GLsizei count,
1123                                     GLboolean transpose,
1124                                     const GLfloat *value)
1125{
1126    setUniformMatrixfv<4, 4>(location, count, transpose, value);
1127}
1128
1129void ProgramMtl::setUniformMatrix2x3fv(GLint location,
1130                                       GLsizei count,
1131                                       GLboolean transpose,
1132                                       const GLfloat *value)
1133{
1134    setUniformMatrixfv<2, 3>(location, count, transpose, value);
1135}
1136
1137void ProgramMtl::setUniformMatrix3x2fv(GLint location,
1138                                       GLsizei count,
1139                                       GLboolean transpose,
1140                                       const GLfloat *value)
1141{
1142    setUniformMatrixfv<3, 2>(location, count, transpose, value);
1143}
1144
1145void ProgramMtl::setUniformMatrix2x4fv(GLint location,
1146                                       GLsizei count,
1147                                       GLboolean transpose,
1148                                       const GLfloat *value)
1149{
1150    setUniformMatrixfv<2, 4>(location, count, transpose, value);
1151}
1152
1153void ProgramMtl::setUniformMatrix4x2fv(GLint location,
1154                                       GLsizei count,
1155                                       GLboolean transpose,
1156                                       const GLfloat *value)
1157{
1158    setUniformMatrixfv<4, 2>(location, count, transpose, value);
1159}
1160
1161void ProgramMtl::setUniformMatrix3x4fv(GLint location,
1162                                       GLsizei count,
1163                                       GLboolean transpose,
1164                                       const GLfloat *value)
1165{
1166    setUniformMatrixfv<3, 4>(location, count, transpose, value);
1167}
1168
1169void ProgramMtl::setUniformMatrix4x3fv(GLint location,
1170                                       GLsizei count,
1171                                       GLboolean transpose,
1172                                       const GLfloat *value)
1173{
1174    setUniformMatrixfv<4, 3>(location, count, transpose, value);
1175}
1176
1177void ProgramMtl::getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const
1178{
1179    getUniformImpl(location, params, GL_FLOAT);
1180}
1181
1182void ProgramMtl::getUniformiv(const gl::Context *context, GLint location, GLint *params) const
1183{
1184    getUniformImpl(location, params, GL_INT);
1185}
1186
1187void ProgramMtl::getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const
1188{
1189    getUniformImpl(location, params, GL_UNSIGNED_INT);
1190}
1191
1192angle::Result ProgramMtl::setupDraw(const gl::Context *glContext,
1193                                    mtl::RenderCommandEncoder *cmdEncoder,
1194                                    const mtl::RenderPipelineDesc &pipelineDesc,
1195                                    bool pipelineDescChanged,
1196                                    bool forceTexturesSetting,
1197                                    bool uniformBuffersDirty)
1198{
1199    ContextMtl *context = mtl::GetImpl(glContext);
1200    if (pipelineDescChanged)
1201    {
1202        // Render pipeline state needs to be changed
1203        id<MTLRenderPipelineState> pipelineState =
1204            mMetalRenderPipelineCache.getRenderPipelineState(context, pipelineDesc);
1205        if (!pipelineState)
1206        {
1207            // Error already logged inside getRenderPipelineState()
1208            return angle::Result::Stop;
1209        }
1210        cmdEncoder->setRenderPipelineState(pipelineState);
1211
1212        // We need to rebind uniform buffers & textures also
1213        mDefaultUniformBlocksDirty.set();
1214        mSamplerBindingsDirty.set();
1215
1216        // Cache current shader variant references for easier querying.
1217        mCurrentShaderVariants[gl::ShaderType::Vertex] =
1218            &mVertexShaderVariants[pipelineDesc.rasterizationType];
1219        mCurrentShaderVariants[gl::ShaderType::Fragment] =
1220            pipelineDesc.rasterizationEnabled()
1221                ? &mFragmentShaderVariants[pipelineDesc.emulateCoverageMask]
1222                : nullptr;
1223    }
1224
1225    ANGLE_TRY(commitUniforms(context, cmdEncoder));
1226    ANGLE_TRY(updateTextures(glContext, cmdEncoder, forceTexturesSetting));
1227
1228    if (uniformBuffersDirty || pipelineDescChanged)
1229    {
1230        ANGLE_TRY(updateUniformBuffers(context, cmdEncoder, pipelineDesc));
1231    }
1232
1233    if (pipelineDescChanged)
1234    {
1235        ANGLE_TRY(updateXfbBuffers(context, cmdEncoder, pipelineDesc));
1236    }
1237
1238    return angle::Result::Continue;
1239}
1240
1241angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder)
1242{
1243    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
1244    {
1245        if (!mDefaultUniformBlocksDirty[shaderType] || !mCurrentShaderVariants[shaderType])
1246        {
1247            continue;
1248        }
1249        DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
1250
1251        if (!uniformBlock.uniformData.size())
1252        {
1253            continue;
1254        }
1255        // If we exceed the default uniform max size, try to allocate a buffer. Worst case
1256        // scenario, fall back on a large setBytes.
1257        bool needsCommitUniform = true;
1258        if (needsCommitUniform)
1259        {
1260            ASSERT(uniformBlock.uniformData.size() <= mtl::kDefaultUniformsMaxSize);
1261            cmdEncoder->setBytes(shaderType, uniformBlock.uniformData.data(),
1262                                 uniformBlock.uniformData.size(),
1263                                 mtl::kDefaultUniformsBindingIndex);
1264        }
1265
1266        mDefaultUniformBlocksDirty.reset(shaderType);
1267    }
1268
1269    return angle::Result::Continue;
1270}
1271
1272angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
1273                                         mtl::RenderCommandEncoder *cmdEncoder,
1274                                         bool forceUpdate)
1275{
1276    ContextMtl *contextMtl                          = mtl::GetImpl(glContext);
1277    const auto &glState                             = glContext->getState();
1278    const gl::ActiveTexturesCache &completeTextures = glState.getActiveTexturesCache();
1279
1280    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
1281    {
1282        if ((!mSamplerBindingsDirty[shaderType] && !forceUpdate) ||
1283            !mCurrentShaderVariants[shaderType])
1284        {
1285            continue;
1286        }
1287
1288        const mtl::TranslatedShaderInfo &shaderInfo =
1289            mCurrentShaderVariants[shaderType]->translatedSrcInfo
1290                ? *mCurrentShaderVariants[shaderType]->translatedSrcInfo
1291                : mMslShaderTranslateInfo[shaderType];
1292        bool hasDepthSampler = false;
1293
1294        for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
1295             ++textureIndex)
1296        {
1297            const gl::SamplerBinding &samplerBinding = mState.getSamplerBindings()[textureIndex];
1298            const mtl::SamplerBinding &mslBinding = shaderInfo.actualSamplerBindings[textureIndex];
1299            if (mslBinding.textureBinding >= mtl::kMaxShaderSamplers)
1300            {
1301                // No binding assigned
1302                continue;
1303            }
1304
1305            gl::TextureType textureType = samplerBinding.textureType;
1306
1307            for (uint32_t arrayElement = 0; arrayElement < samplerBinding.boundTextureUnits.size();
1308                 ++arrayElement)
1309            {
1310                GLuint textureUnit   = samplerBinding.boundTextureUnits[arrayElement];
1311                gl::Texture *texture = completeTextures[textureUnit];
1312                gl::Sampler *sampler = contextMtl->getState().getSampler(textureUnit);
1313                uint32_t textureSlot = mslBinding.textureBinding + arrayElement;
1314                uint32_t samplerSlot = mslBinding.samplerBinding + arrayElement;
1315                if (!texture)
1316                {
1317                    ANGLE_TRY(contextMtl->getIncompleteTexture(glContext, textureType, &texture));
1318                }
1319                const gl::SamplerState *samplerState =
1320                    sampler ? &sampler->getSamplerState() : &texture->getSamplerState();
1321                TextureMtl *textureMtl = mtl::GetImpl(texture);
1322                if (samplerBinding.format == gl::SamplerFormat::Shadow)
1323                {
1324                    hasDepthSampler                  = true;
1325                    mShadowCompareModes[textureSlot] = mtl::MslGetShaderShadowCompareMode(
1326                        samplerState->getCompareMode(), samplerState->getCompareFunc());
1327                }
1328                ANGLE_TRY(textureMtl->bindToShader(glContext, cmdEncoder, shaderType, sampler,
1329                                                   textureSlot, samplerSlot));
1330            }  // for array elements
1331        }      // for sampler bindings
1332
1333        if (hasDepthSampler)
1334        {
1335            cmdEncoder->setData(shaderType, mShadowCompareModes,
1336                                mtl::kShadowSamplerCompareModesBindingIndex);
1337        }
1338    }  // for shader types
1339
1340    return angle::Result::Continue;
1341}
1342
1343angle::Result ProgramMtl::updateUniformBuffers(ContextMtl *context,
1344                                               mtl::RenderCommandEncoder *cmdEncoder,
1345                                               const mtl::RenderPipelineDesc &pipelineDesc)
1346{
1347    const std::vector<gl::InterfaceBlock> &blocks = mState.getUniformBlocks();
1348    if (blocks.empty())
1349    {
1350        return angle::Result::Continue;
1351    }
1352
1353    // This array is only used inside this function and its callees.
1354    ScopedAutoClearVector<uint32_t> scopeArrayClear(&mArgumentBufferRenderStageUsages);
1355    ScopedAutoClearVector<std::pair<mtl::BufferRef, uint32_t>> scopeArrayClear2(
1356        &mLegalizedOffsetedUniformBuffers);
1357    mArgumentBufferRenderStageUsages.resize(blocks.size());
1358    mLegalizedOffsetedUniformBuffers.resize(blocks.size());
1359
1360    ANGLE_TRY(legalizeUniformBufferOffsets(context, blocks));
1361
1362    const gl::State &glState = context->getState();
1363
1364    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
1365    {
1366        if (!mCurrentShaderVariants[shaderType])
1367        {
1368            continue;
1369        }
1370
1371        if (mCurrentShaderVariants[shaderType]->translatedSrcInfo->hasUBOArgumentBuffer)
1372        {
1373            ANGLE_TRY(
1374                encodeUniformBuffersInfoArgumentBuffer(context, cmdEncoder, blocks, shaderType));
1375        }
1376        else
1377        {
1378            ANGLE_TRY(bindUniformBuffersToDiscreteSlots(context, cmdEncoder, blocks, shaderType));
1379        }
1380    }  // for shader types
1381
1382    // After encode the uniform buffers into an argument buffer, we need to tell Metal that
1383    // the buffers are being used by what shader stages.
1384    for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
1385    {
1386        const gl::InterfaceBlock &block = blocks[bufferIndex];
1387        const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
1388            glState.getIndexedUniformBuffer(block.binding);
1389        if (bufferBinding.get() == nullptr)
1390        {
1391            continue;
1392        }
1393
1394        // Remove any other stages other than vertex and fragment.
1395        uint32_t stages = mArgumentBufferRenderStageUsages[bufferIndex] &
1396                          (mtl::kRenderStageVertex | mtl::kRenderStageFragment);
1397
1398        if (stages == 0)
1399        {
1400            continue;
1401        }
1402
1403        cmdEncoder->useResource(mLegalizedOffsetedUniformBuffers[bufferIndex].first,
1404                                MTLResourceUsageRead, static_cast<mtl::RenderStages>(stages));
1405    }
1406
1407    return angle::Result::Continue;
1408}
1409
1410angle::Result ProgramMtl::legalizeUniformBufferOffsets(
1411    ContextMtl *context,
1412    const std::vector<gl::InterfaceBlock> &blocks)
1413{
1414    const gl::State &glState = context->getState();
1415
1416    for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
1417    {
1418        const gl::InterfaceBlock &block = blocks[bufferIndex];
1419        const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
1420            glState.getIndexedUniformBuffer(block.binding);
1421
1422        if (bufferBinding.get() == nullptr)
1423        {
1424            continue;
1425        }
1426
1427        BufferMtl *bufferMtl = mtl::GetImpl(bufferBinding.get());
1428        size_t srcOffset     = std::min<size_t>(bufferBinding.getOffset(), bufferMtl->size());
1429        size_t offsetModulo  = srcOffset % mtl::kUniformBufferSettingOffsetMinAlignment;
1430        if (offsetModulo)
1431        {
1432            ConversionBufferMtl *conversion =
1433                bufferMtl->getUniformConversionBuffer(context, offsetModulo);
1434            // Has the content of the buffer has changed since last conversion?
1435            if (conversion->dirty)
1436            {
1437                const uint8_t *srcBytes = bufferMtl->getClientShadowCopyData(context);
1438                srcBytes += offsetModulo;
1439                size_t sizeToCopy      = bufferMtl->size() - offsetModulo;
1440                size_t bytesToAllocate = roundUp<size_t>(sizeToCopy, 16u);
1441                ANGLE_TRY(StreamUniformBufferData(
1442                    context, &conversion->data, srcBytes, bytesToAllocate, sizeToCopy,
1443                    &conversion->convertedBuffer, &conversion->convertedOffset));
1444#ifndef NDEBUG
1445                ANGLE_MTL_OBJC_SCOPE
1446                {
1447                    conversion->convertedBuffer->get().label = [NSString
1448                        stringWithFormat:@"Converted from %p offset=%zu", bufferMtl, offsetModulo];
1449                }
1450#endif
1451                conversion->dirty = false;
1452            }
1453            // reuse the converted buffer
1454            mLegalizedOffsetedUniformBuffers[bufferIndex].first = conversion->convertedBuffer;
1455            mLegalizedOffsetedUniformBuffers[bufferIndex].second =
1456                static_cast<uint32_t>(conversion->convertedOffset + srcOffset - offsetModulo);
1457        }
1458        else
1459        {
1460            mLegalizedOffsetedUniformBuffers[bufferIndex].first = bufferMtl->getCurrentBuffer();
1461            mLegalizedOffsetedUniformBuffers[bufferIndex].second =
1462                static_cast<uint32_t>(bufferBinding.getOffset());
1463        }
1464    }
1465    return angle::Result::Continue;
1466}
1467
1468angle::Result ProgramMtl::bindUniformBuffersToDiscreteSlots(
1469    ContextMtl *context,
1470    mtl::RenderCommandEncoder *cmdEncoder,
1471    const std::vector<gl::InterfaceBlock> &blocks,
1472    gl::ShaderType shaderType)
1473{
1474    const gl::State &glState = context->getState();
1475    const mtl::TranslatedShaderInfo &shaderInfo =
1476        *mCurrentShaderVariants[shaderType]->translatedSrcInfo;
1477    for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
1478    {
1479        const gl::InterfaceBlock &block = blocks[bufferIndex];
1480        const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
1481            glState.getIndexedUniformBuffer(block.binding);
1482
1483        if (bufferBinding.get() == nullptr || !block.activeShaders().test(shaderType))
1484        {
1485            continue;
1486        }
1487
1488        uint32_t actualBufferIdx = shaderInfo.actualUBOBindings[bufferIndex];
1489
1490        if (actualBufferIdx >= mtl::kMaxShaderBuffers)
1491        {
1492            continue;
1493        }
1494
1495        mtl::BufferRef mtlBuffer = mLegalizedOffsetedUniformBuffers[bufferIndex].first;
1496        uint32_t offset          = mLegalizedOffsetedUniformBuffers[bufferIndex].second;
1497        cmdEncoder->setBuffer(shaderType, mtlBuffer, offset, actualBufferIdx);
1498    }
1499    return angle::Result::Continue;
1500}
1501angle::Result ProgramMtl::encodeUniformBuffersInfoArgumentBuffer(
1502    ContextMtl *context,
1503    mtl::RenderCommandEncoder *cmdEncoder,
1504    const std::vector<gl::InterfaceBlock> &blocks,
1505    gl::ShaderType shaderType)
1506{
1507    const gl::State &glState = context->getState();
1508    ASSERT(mCurrentShaderVariants[shaderType]->translatedSrcInfo);
1509    const mtl::TranslatedShaderInfo &shaderInfo =
1510        *mCurrentShaderVariants[shaderType]->translatedSrcInfo;
1511
1512    // Encode all uniform buffers into an argument buffer.
1513    ProgramArgumentBufferEncoderMtl &bufferEncoder =
1514        mCurrentShaderVariants[shaderType]->uboArgBufferEncoder;
1515
1516    mtl::BufferRef argumentBuffer;
1517    size_t argumentBufferOffset;
1518    bufferEncoder.bufferPool.releaseInFlightBuffers(context);
1519    ANGLE_TRY(bufferEncoder.bufferPool.allocate(
1520        context, bufferEncoder.metalArgBufferEncoder.get().encodedLength, nullptr, &argumentBuffer,
1521        &argumentBufferOffset));
1522
1523    [bufferEncoder.metalArgBufferEncoder setArgumentBuffer:argumentBuffer->get()
1524                                                    offset:argumentBufferOffset];
1525
1526    constexpr gl::ShaderMap<MTLRenderStages> kShaderStageMap = {
1527        {gl::ShaderType::Vertex, mtl::kRenderStageVertex},
1528        {gl::ShaderType::Fragment, mtl::kRenderStageFragment},
1529    };
1530
1531    auto mtlRenderStage = kShaderStageMap[shaderType];
1532
1533    for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
1534    {
1535        const gl::InterfaceBlock &block = blocks[bufferIndex];
1536        const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
1537            glState.getIndexedUniformBuffer(block.binding);
1538
1539        if (bufferBinding.get() == nullptr || !block.activeShaders().test(shaderType))
1540        {
1541            continue;
1542        }
1543
1544        mArgumentBufferRenderStageUsages[bufferIndex] |= mtlRenderStage;
1545
1546        uint32_t actualBufferIdx = shaderInfo.actualUBOBindings[bufferIndex];
1547        if (actualBufferIdx >= mtl::kMaxShaderBuffers)
1548        {
1549            continue;
1550        }
1551
1552        mtl::BufferRef mtlBuffer = mLegalizedOffsetedUniformBuffers[bufferIndex].first;
1553        uint32_t offset          = mLegalizedOffsetedUniformBuffers[bufferIndex].second;
1554        [bufferEncoder.metalArgBufferEncoder setBuffer:mtlBuffer->get()
1555                                                offset:offset
1556                                               atIndex:actualBufferIdx];
1557    }
1558
1559    ANGLE_TRY(bufferEncoder.bufferPool.commit(context));
1560
1561    cmdEncoder->setBuffer(shaderType, argumentBuffer, static_cast<uint32_t>(argumentBufferOffset),
1562                          mtl::kUBOArgumentBufferBindingIndex);
1563    return angle::Result::Continue;
1564}
1565
1566angle::Result ProgramMtl::updateXfbBuffers(ContextMtl *context,
1567                                           mtl::RenderCommandEncoder *cmdEncoder,
1568                                           const mtl::RenderPipelineDesc &pipelineDesc)
1569{
1570    const gl::State &glState                 = context->getState();
1571    gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
1572
1573    if (pipelineDesc.rasterizationEnabled() || !glState.isTransformFeedbackActiveUnpaused() ||
1574        ANGLE_UNLIKELY(!transformFeedback))
1575    {
1576        // XFB output can only be used with rasterization disabled.
1577        return angle::Result::Continue;
1578    }
1579
1580    size_t xfbBufferCount = glState.getProgramExecutable()->getTransformFeedbackBufferCount();
1581
1582    ASSERT(xfbBufferCount > 0);
1583    ASSERT(mState.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
1584           xfbBufferCount == 1);
1585
1586    for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
1587    {
1588        uint32_t actualBufferIdx = mMslXfbOnlyVertexShaderInfo.actualXFBBindings[bufferIndex];
1589
1590        if (actualBufferIdx >= mtl::kMaxShaderBuffers)
1591        {
1592            continue;
1593        }
1594
1595        const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
1596            transformFeedback->getIndexedBuffer(bufferIndex);
1597        gl::Buffer *buffer = bufferBinding.get();
1598        ASSERT((bufferBinding.getOffset() % 4) == 0);
1599        ASSERT(buffer != nullptr);
1600
1601        BufferMtl *bufferMtl = mtl::GetImpl(buffer);
1602
1603        // Use offset=0, actual offset will be set in Driver Uniform inside ContextMtl.
1604        cmdEncoder->setBufferForWrite(gl::ShaderType::Vertex, bufferMtl->getCurrentBuffer(), 0,
1605                                      actualBufferIdx);
1606    }
1607
1608    return angle::Result::Continue;
1609}
1610
1611}  // namespace rx
1612