• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// mtl_msl_utils.h: Utilities to manipulate MSL.
7//
8
9#import <Foundation/Foundation.h>
10
11#include <variant>
12#include "common/string_utils.h"
13#include "common/utilities.h"
14#include "compiler/translator/msl/Name.h"
15#include "compiler/translator/msl/TranslatorMSL.h"
16#include "libANGLE/renderer/metal/ContextMtl.h"
17#include "libANGLE/renderer/metal/ShaderMtl.h"
18#include "libANGLE/renderer/metal/mtl_msl_utils.h"
19
20namespace rx
21{
22namespace
23{
24constexpr char kXfbBindingsMarker[]     = "@@XFB-Bindings@@";
25constexpr char kXfbOutMarker[]          = "ANGLE_@@XFB-OUT@@";
26constexpr char kUserDefinedNamePrefix[] = "_u";  // Defined in GLSLANG/ShaderLang.h
27constexpr char kAttribBindingsMarker[]  = "@@Attrib-Bindings@@\n";
28
29std::string GetXfbBufferNameMtl(const uint32_t bufferIndex)
30{
31    return "xfbBuffer" + Str(bufferIndex);
32}
33
34// Name format needs to match sh::Name.
35struct UserDefinedNameExpr
36{
37    std::string name;
38};
39
40std::ostream &operator<<(std::ostream &stream, const UserDefinedNameExpr &expr)
41{
42    return stream << kUserDefinedNamePrefix << expr.name;
43}
44
45struct UserDefinedNameComponentExpr
46{
47    UserDefinedNameExpr name;
48    const int component;
49};
50
51std::ostream &operator<<(std::ostream &stream, const UserDefinedNameComponentExpr &expr)
52{
53    return stream << expr.name << '[' << expr.component << ']';
54}
55
56struct InternalNameExpr
57{
58    std::string name;
59};
60
61std::ostream &operator<<(std::ostream &stream, const InternalNameExpr &expr)
62{
63    return stream << sh::kAngleInternalPrefix << '_' << expr.name;
64}
65
66struct InternalNameComponentExpr
67{
68    InternalNameExpr name;
69    const int component;
70};
71
72std::ostream &operator<<(std::ostream &stream, const InternalNameComponentExpr &expr)
73{
74    return stream << expr.name << '_' << expr.component;
75}
76
77// ModifyStructs phase forwarded a single-component user-defined name or created a new AngleInternal
78// field name to support multi-component fields as multiple single-component fields.
79std::variant<UserDefinedNameExpr, InternalNameComponentExpr>
80ResolveModifiedAttributeName(const std::string &name, int registerIndex, int registerCount)
81{
82    if (registerCount < 2)
83    {
84        return UserDefinedNameExpr{name};
85    }
86    return InternalNameComponentExpr{InternalNameExpr{name}, registerIndex};
87}
88
89std::variant<UserDefinedNameExpr, InternalNameComponentExpr>
90ResolveModifiedOutputName(const std::string &name, int component, int componentCount)
91{
92    if (componentCount == 0)
93    {
94        return UserDefinedNameExpr{name};
95    }
96    return InternalNameComponentExpr{InternalNameExpr{name}, component};
97}
98
99// Accessing unmodified structs uses user-defined name, business as usual.
100std::variant<UserDefinedNameExpr, UserDefinedNameComponentExpr>
101ResolveUserDefinedName(const std::string &name, int component, int componentCount)
102{
103    if (componentCount == 0)
104    {
105        return UserDefinedNameExpr{name};
106    }
107    return UserDefinedNameComponentExpr{{name}, component};
108}
109
110template <class T>
111struct ApplyOStream
112{
113    const T &value;
114};
115
116template <class T>
117ApplyOStream(T) -> ApplyOStream<T>;
118
119template <class T>
120std::ostream &operator<<(std::ostream &stream, ApplyOStream<T> sv)
121{
122    stream << sv.value;
123    return stream;
124}
125
126template <class... Ts>
127std::ostream &operator<<(std::ostream &stream, ApplyOStream<std::variant<Ts...>> sv)
128{
129    std::visit([&stream](auto &&v) { stream << ApplyOStream{v}; }, sv.value);
130    return stream;
131}
132
133}  // namespace
134
135namespace mtl
136{
137
138void TranslatedShaderInfo::reset()
139{
140    metalShaderSource    = nullptr;
141    metalLibrary         = nil;
142    hasUBOArgumentBuffer = false;
143    hasInvariant         = false;
144    for (mtl::SamplerBinding &binding : actualSamplerBindings)
145    {
146        binding.textureBinding = mtl::kMaxShaderSamplers;
147        binding.samplerBinding = 0;
148    }
149    for (int &rwTextureBinding : actualImageBindings)
150    {
151        rwTextureBinding = -1;
152    }
153    for (uint32_t &binding : actualUBOBindings)
154    {
155        binding = mtl::kMaxShaderBuffers;
156    }
157
158    for (uint32_t &binding : actualXFBBindings)
159    {
160        binding = mtl::kMaxShaderBuffers;
161    }
162}
163
164// Original mapping of front end from sampler name to multiple sampler slots (in form of
165// slot:count pair)
166using OriginalSamplerBindingMap =
167    std::unordered_map<std::string, std::vector<std::pair<uint32_t, uint32_t>>>;
168
169bool MappedSamplerNameNeedsUserDefinedPrefix(const std::string &originalName)
170{
171    return originalName.find('.') == std::string::npos;
172}
173
174static std::string MSLGetMappedSamplerName(const std::string &originalName)
175{
176    std::string samplerName = originalName;
177
178    // Samplers in structs are extracted.
179    std::replace(samplerName.begin(), samplerName.end(), '.', '_');
180
181    // Remove array elements
182    auto out = samplerName.begin();
183    for (auto in = samplerName.begin(); in != samplerName.end(); in++)
184    {
185        if (*in == '[')
186        {
187            while (*in != ']')
188            {
189                in++;
190                ASSERT(in != samplerName.end());
191            }
192        }
193        else
194        {
195            *out++ = *in;
196        }
197    }
198
199    samplerName.erase(out, samplerName.end());
200
201    if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
202    {
203        samplerName = sh::kUserDefinedNamePrefix + samplerName;
204    }
205
206    return samplerName;
207}
208
209void MSLGetShaderSource(const gl::ProgramState &programState,
210                        const gl::ProgramLinkedResources &resources,
211                        gl::ShaderMap<std::string> *shaderSourcesOut)
212{
213    for (const gl::ShaderType shaderType : gl::AllShaderTypes())
214    {
215        const gl::SharedCompiledShaderState &glShader = programState.getAttachedShader(shaderType);
216        (*shaderSourcesOut)[shaderType]               = glShader ? glShader->translatedSource : "";
217    }
218}
219
220void GetAssignedSamplerBindings(const sh::TranslatorMetalReflection *reflection,
221                                const OriginalSamplerBindingMap &originalBindings,
222                                std::unordered_set<std::string> &structSamplers,
223                                std::array<SamplerBinding, mtl::kMaxGLSamplerBindings> *bindings)
224{
225    for (auto &sampler : reflection->getSamplerBindings())
226    {
227        const std::string &name          = sampler.first;
228        const uint32_t actualSamplerSlot = (uint32_t)reflection->getSamplerBinding(name);
229        const uint32_t actualTextureSlot = (uint32_t)reflection->getTextureBinding(name);
230
231        // Assign sequential index for subsequent array elements
232        const bool structSampler = structSamplers.find(name) != structSamplers.end();
233        const std::string mappedName =
234            structSampler ? name : MSLGetMappedSamplerName(sh::kUserDefinedNamePrefix + name);
235        auto original = originalBindings.find(mappedName);
236        if (original != originalBindings.end())
237        {
238            const std::vector<std::pair<uint32_t, uint32_t>> &resOrignalBindings =
239                originalBindings.at(mappedName);
240            uint32_t currentTextureSlot = actualTextureSlot;
241            uint32_t currentSamplerSlot = actualSamplerSlot;
242            for (const std::pair<uint32_t, uint32_t> &originalBindingRange : resOrignalBindings)
243            {
244                SamplerBinding &actualBinding = bindings->at(originalBindingRange.first);
245                actualBinding.textureBinding  = currentTextureSlot;
246                actualBinding.samplerBinding  = currentSamplerSlot;
247
248                currentTextureSlot += originalBindingRange.second;
249                currentSamplerSlot += originalBindingRange.second;
250            }
251        }
252    }
253}
254
255std::string UpdateAliasedShaderAttributes(std::string shaderSourceIn,
256                                          const gl::ProgramExecutable &executable)
257{
258    // Cache max number of components for each attribute location
259    std::array<uint8_t, gl::MAX_VERTEX_ATTRIBS> maxComponents{};
260    for (auto &attribute : executable.getProgramInputs())
261    {
262        const int location       = attribute.getLocation();
263        const int registers      = gl::VariableRegisterCount(attribute.getType());
264        const uint8_t components = gl::VariableColumnCount(attribute.getType());
265        for (int i = 0; i < registers; ++i)
266        {
267            ASSERT(location + i < static_cast<int>(maxComponents.size()));
268            maxComponents[location + i] = std::max(maxComponents[location + i], components);
269        }
270    }
271
272    // Define aliased names pointing to real attributes with swizzles as needed
273    std::ostringstream stream;
274    for (auto &attribute : executable.getProgramInputs())
275    {
276        const int location       = attribute.getLocation();
277        const int registers      = gl::VariableRegisterCount(attribute.getType());
278        const uint8_t components = gl::VariableColumnCount(attribute.getType());
279        for (int i = 0; i < registers; i++)
280        {
281            stream << "#define ANGLE_ALIASED_"
282                   << ApplyOStream{ResolveModifiedAttributeName(attribute.name, i, registers)}
283                   << " ANGLE_modified.ANGLE_ATTRIBUTE_" << (location + i);
284            if (components != maxComponents[location + i])
285            {
286                ASSERT(components < maxComponents[location + i]);
287                switch (components)
288                {
289                    case 1:
290                        stream << ".x";
291                        break;
292                    case 2:
293                        stream << ".xy";
294                        break;
295                    case 3:
296                        stream << ".xyz";
297                        break;
298                }
299            }
300            stream << "\n";
301        }
302    }
303
304    // Declare actual MSL attributes
305    for (size_t i : executable.getActiveAttribLocationsMask())
306    {
307        stream << "  float";
308        if (maxComponents[i] > 1)
309        {
310            stream << static_cast<int>(maxComponents[i]);
311        }
312        stream << " ANGLE_ATTRIBUTE_" << i << "[[attribute(" << i << ")]];\n";
313    }
314
315    std::string outputSource = shaderSourceIn;
316    size_t markerFound       = outputSource.find(kAttribBindingsMarker);
317    ASSERT(markerFound != std::string::npos);
318    outputSource.replace(markerFound, angle::ConstStrLen(kAttribBindingsMarker), stream.str());
319    return outputSource;
320}
321
322std::string updateShaderAttributes(std::string shaderSourceIn,
323                                   const gl::ProgramExecutable &executable)
324{
325    // Build string to attrib map.
326    const auto &programAttributes = executable.getProgramInputs();
327    std::ostringstream stream;
328    std::unordered_map<std::string, uint32_t> attributeBindings;
329    for (auto &attribute : programAttributes)
330    {
331        const int registers = gl::VariableRegisterCount(attribute.getType());
332        for (int i = 0; i < registers; i++)
333        {
334            stream.str("");
335            stream << ' '
336                   << ApplyOStream{ResolveModifiedAttributeName(attribute.name, i, registers)}
337                   << sh::kUnassignedAttributeString;
338            attributeBindings.insert({stream.str(), i + attribute.getLocation()});
339        }
340    }
341    // Rewrite attributes
342    std::string outputSource = shaderSourceIn;
343    for (auto it = attributeBindings.begin(); it != attributeBindings.end(); ++it)
344    {
345        std::size_t attribFound = outputSource.find(it->first);
346        if (attribFound != std::string::npos)
347        {
348            stream.str("");
349            stream << "[[attribute(" << it->second << ")]]";
350            outputSource = outputSource.replace(
351                attribFound + it->first.length() -
352                    angle::ConstStrLen(sh::kUnassignedAttributeString),
353                angle::ConstStrLen(sh::kUnassignedAttributeString), stream.str());
354        }
355    }
356    return outputSource;
357}
358
359std::string UpdateFragmentShaderOutputs(std::string shaderSourceIn,
360                                        const gl::ProgramExecutable &executable,
361                                        bool defineAlpha0)
362{
363    std::ostringstream stream;
364    std::string outputSource    = shaderSourceIn;
365    const auto &outputVariables = executable.getOutputVariables();
366
367    // For alpha-to-coverage emulation, a reference to the alpha channel
368    // of color output 0 is needed. For ESSL 1.00, it is gl_FragColor or
369    // gl_FragData[0]; for ESSL 3.xx, it is a user-defined output.
370    std::string alphaOutputName;
371
372    auto assignLocations = [&](const std::vector<gl::VariableLocation> &locations, bool secondary) {
373        for (auto &outputLocation : locations)
374        {
375            if (!outputLocation.used())
376            {
377                continue;
378            }
379            const int index                    = outputLocation.arrayIndex;
380            const gl::ProgramOutput &outputVar = outputVariables[outputLocation.index];
381            ASSERT(outputVar.pod.location >= 0);
382            const int location  = outputVar.pod.location + index;
383            const int arraySize = outputVar.getOutermostArraySize();
384            stream.str("");
385            stream << ApplyOStream{ResolveModifiedOutputName(outputVar.name, index, arraySize)}
386                   << " [[" << sh::kUnassignedFragmentOutputString;
387            const std::string placeholder(stream.str());
388
389            size_t outputFound = outputSource.find(placeholder);
390            if (outputFound != std::string::npos)
391            {
392                stream.str("");
393                stream << "color(" << location << (secondary ? "), index(1)" : ")");
394                outputSource = outputSource.replace(
395                    outputFound + placeholder.length() -
396                        angle::ConstStrLen(sh::kUnassignedFragmentOutputString),
397                    angle::ConstStrLen(sh::kUnassignedFragmentOutputString), stream.str());
398            }
399
400            if (defineAlpha0 && location == 0 && !secondary && outputVar.pod.type == GL_FLOAT_VEC4)
401            {
402                ASSERT(index == 0);
403                ASSERT(alphaOutputName.empty());
404                std::ostringstream nameStream;
405                nameStream << "ANGLE_fragmentOut."
406                           << ApplyOStream{ResolveUserDefinedName(outputVar.name, index, arraySize)}
407                           << ".a";
408                alphaOutputName = nameStream.str();
409            }
410        }
411    };
412    assignLocations(executable.getOutputLocations(), false);
413    assignLocations(executable.getSecondaryOutputLocations(), true);
414
415    if (defineAlpha0)
416    {
417        // Locations are empty for ESSL 1.00 shaders, try built-in outputs
418        if (alphaOutputName.empty())
419        {
420            for (auto &v : outputVariables)
421            {
422                if (v.name == "gl_FragColor")
423                {
424                    alphaOutputName = "ANGLE_fragmentOut.gl_FragColor.a";
425                    break;
426                }
427                else if (v.name == "gl_FragData")
428                {
429                    alphaOutputName = "ANGLE_fragmentOut.ANGLE_gl_FragData_0.a";
430                    break;
431                }
432            }
433        }
434
435        // Set a value used for alpha-to-coverage emulation
436        const std::string alphaPlaceholder("#define ANGLE_ALPHA0");
437        size_t alphaFound = outputSource.find(alphaPlaceholder);
438        ASSERT(alphaFound != std::string::npos);
439
440        std::ostringstream alphaStream;
441        alphaStream << alphaPlaceholder << " ";
442        alphaStream << (alphaOutputName.empty() ? "1.0" : alphaOutputName);
443        outputSource =
444            outputSource.replace(alphaFound, alphaPlaceholder.length(), alphaStream.str());
445    }
446
447    return outputSource;
448}
449
450std::string SubstituteTransformFeedbackMarkers(const std::string &originalSource,
451                                               const std::string &xfbBindings,
452                                               const std::string &xfbOut)
453{
454    const size_t xfbBindingsMarkerStart = originalSource.find(kXfbBindingsMarker);
455    bool hasBindingsMarker              = xfbBindingsMarkerStart != std::string::npos;
456    const size_t xfbBindingsMarkerEnd =
457        xfbBindingsMarkerStart + angle::ConstStrLen(kXfbBindingsMarker);
458
459    const size_t xfbOutMarkerStart = originalSource.find(kXfbOutMarker, xfbBindingsMarkerStart);
460    bool hasOutMarker              = xfbOutMarkerStart != std::string::npos;
461    const size_t xfbOutMarkerEnd   = xfbOutMarkerStart + angle::ConstStrLen(kXfbOutMarker);
462
463    // The shader is the following form:
464    //
465    // ..part1..
466    // @@ XFB-BINDINGS @@
467    // ..part2..
468    // @@ XFB-OUT @@;
469    // ..part3..
470    //
471    // Construct the string by concatenating these five pieces, replacing the markers with the given
472    // values.
473    std::string result;
474    if (hasBindingsMarker && hasOutMarker)
475    {
476        result.append(&originalSource[0], &originalSource[xfbBindingsMarkerStart]);
477        result.append(xfbBindings);
478        result.append(&originalSource[xfbBindingsMarkerEnd], &originalSource[xfbOutMarkerStart]);
479        result.append(xfbOut);
480        result.append(&originalSource[xfbOutMarkerEnd], &originalSource[originalSource.size()]);
481        return result;
482    }
483    return originalSource;
484}
485
486std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVarying &varying,
487                                                   const gl::UniformTypeInfo &info,
488                                                   size_t strideBytes,
489                                                   size_t offset,
490                                                   const std::string &bufferIndex)
491{
492    std::ostringstream result;
493
494    ASSERT(strideBytes % 4 == 0);
495    size_t stride = strideBytes / 4;
496
497    const size_t arrayIndexStart = varying.arrayIndex == GL_INVALID_INDEX ? 0 : varying.arrayIndex;
498    const size_t arrayIndexEnd   = arrayIndexStart + varying.size();
499
500    for (size_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
501    {
502        for (int col = 0; col < info.columnCount; ++col)
503        {
504            for (int row = 0; row < info.rowCount; ++row)
505            {
506                result << "        ";
507                result << "ANGLE_" << "xfbBuffer" << bufferIndex << "[" << "ANGLE_"
508                       << std::string(sh::kUniformsVar) << ".ANGLE_xfbBufferOffsets[" << bufferIndex
509                       << "] + (ANGLE_vertexIDMetal + (ANGLE_instanceIdMod - ANGLE_baseInstance) * "
510                       << "ANGLE_" << std::string(sh::kUniformsVar)
511                       << ".ANGLE_xfbVerticesPerInstance) * " << stride << " + " << offset
512                       << "] = " << "as_type<float>" << "(" << "ANGLE_vertexOut.";
513                if (!varying.isBuiltIn())
514                {
515                    result << kUserDefinedNamePrefix;
516                }
517                result << varying.name;
518
519                if (varying.isArray())
520                {
521                    result << "[" << arrayIndex << "]";
522                }
523
524                if (info.columnCount > 1)
525                {
526                    result << "[" << col << "]";
527                }
528
529                if (info.rowCount > 1)
530                {
531                    result << "[" << row << "]";
532                }
533
534                result << ");\n";
535                ++offset;
536            }
537        }
538    }
539
540    return result.str();
541}
542
543void GenerateTransformFeedbackEmulationOutputs(
544    const gl::ProgramExecutable &executable,
545    std::string *vertexShader,
546    std::array<uint32_t, kMaxShaderXFBs> *xfbBindingRemapOut)
547{
548    const std::vector<gl::TransformFeedbackVarying> &varyings =
549        executable.getLinkedTransformFeedbackVaryings();
550    const std::vector<GLsizei> &bufferStrides = executable.getTransformFeedbackStrides();
551    const bool isInterleaved =
552        executable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
553    const size_t bufferCount = isInterleaved ? 1 : varyings.size();
554
555    std::vector<std::string> xfbIndices(bufferCount);
556
557    std::string xfbBindings;
558
559    for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
560    {
561        const std::string xfbBinding = Str(0);
562        xfbIndices[bufferIndex]      = Str(bufferIndex);
563
564        std::string bufferName = GetXfbBufferNameMtl(bufferIndex);
565
566        xfbBindings += ", ";
567        // TODO: offset from last used buffer binding from front end
568        // XFB buffer is allocated slot starting from last discrete Metal buffer slot.
569        uint32_t bindingPoint               = kMaxShaderBuffers - 1 - bufferIndex;
570        xfbBindingRemapOut->at(bufferIndex) = bindingPoint;
571        xfbBindings +=
572            "device float* ANGLE_" + bufferName + " [[buffer(" + Str(bindingPoint) + ")]]";
573    }
574
575    std::string xfbOut  = "#if TRANSFORM_FEEDBACK_ENABLED\n    {\n";
576    size_t outputOffset = 0;
577    for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
578    {
579        const size_t bufferIndex                    = isInterleaved ? 0 : varyingIndex;
580        const gl::TransformFeedbackVarying &varying = varyings[varyingIndex];
581
582        // For every varying, output to the respective buffer packed.  If interleaved, the output is
583        // always to the same buffer, but at different offsets.
584        const gl::UniformTypeInfo &info = gl::GetUniformTypeInfo(varying.type);
585        xfbOut += GenerateTransformFeedbackVaryingOutput(varying, info, bufferStrides[bufferIndex],
586                                                         outputOffset, xfbIndices[bufferIndex]);
587
588        if (isInterleaved)
589        {
590            outputOffset += info.columnCount * info.rowCount * varying.size();
591        }
592    }
593    xfbOut += "    }\n#endif\n";
594
595    *vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, xfbBindings, xfbOut);
596}
597
598angle::Result MTLGetMSL(Context *context,
599                        const gl::ProgramExecutable &executable,
600                        const gl::Caps &glCaps,
601                        const gl::ShaderMap<std::string> &shaderSources,
602                        const gl::ShaderMap<SharedCompiledShaderStateMtl> &shadersState,
603                        gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut)
604{
605    // Retrieve original uniform buffer bindings generated by front end. We will need to do a remap.
606    std::unordered_map<std::string, uint32_t> uboOriginalBindings;
607    const std::vector<gl::InterfaceBlock> &blocks = executable.getUniformBlocks();
608    for (uint32_t bufferIdx = 0; bufferIdx < blocks.size(); ++bufferIdx)
609    {
610        const gl::InterfaceBlock &block = blocks[bufferIdx];
611        if (!uboOriginalBindings.count(block.name))
612        {
613            uboOriginalBindings[block.name] = bufferIdx;
614        }
615    }
616    // Retrieve original sampler bindings produced by front end.
617    OriginalSamplerBindingMap originalSamplerBindings;
618    const std::vector<gl::SamplerBinding> &samplerBindings = executable.getSamplerBindings();
619    std::unordered_set<std::string> structSamplers         = {};
620
621    for (uint32_t textureIndex = 0; textureIndex < samplerBindings.size(); ++textureIndex)
622    {
623        const gl::SamplerBinding &samplerBinding = samplerBindings[textureIndex];
624        uint32_t uniformIndex          = executable.getUniformIndexFromSamplerIndex(textureIndex);
625        const std::string &uniformName = executable.getUniformNames()[uniformIndex];
626        const std::string &uniformMappedName = executable.getUniformMappedNames()[uniformIndex];
627        bool isSamplerInStruct               = uniformName.find('.') != std::string::npos;
628        std::string mappedSamplerName        = isSamplerInStruct
629                                                   ? MSLGetMappedSamplerName(uniformName)
630                                                   : MSLGetMappedSamplerName(uniformMappedName);
631        // These need to be prefixed later seperately
632        if (isSamplerInStruct)
633            structSamplers.insert(mappedSamplerName);
634        originalSamplerBindings[mappedSamplerName].push_back(
635            {textureIndex, static_cast<uint32_t>(samplerBinding.textureUnitsCount)});
636    }
637    for (gl::ShaderType type : {gl::ShaderType::Vertex, gl::ShaderType::Fragment})
638    {
639        std::string source;
640        if (type == gl::ShaderType::Vertex)
641        {
642            source =
643                shadersState[gl::ShaderType::Vertex]->translatorMetalReflection.hasAttributeAliasing
644                    ? UpdateAliasedShaderAttributes(shaderSources[type], executable)
645                    : updateShaderAttributes(shaderSources[type], executable);
646            // Write transform feedback output code.
647            if (!source.empty())
648            {
649                if (executable.getLinkedTransformFeedbackVaryings().empty())
650                {
651                    source = SubstituteTransformFeedbackMarkers(source, "", "");
652                }
653                else
654                {
655                    GenerateTransformFeedbackEmulationOutputs(
656                        executable, &source, &(*mslShaderInfoOut)[type].actualXFBBindings);
657                }
658            }
659        }
660        else
661        {
662            ASSERT(type == gl::ShaderType::Fragment);
663            bool defineAlpha0 =
664                context->getDisplay()->getFeatures().emulateAlphaToCoverage.enabled ||
665                context->getDisplay()->getFeatures().generateShareableShaders.enabled;
666            source = UpdateFragmentShaderOutputs(shaderSources[type], executable, defineAlpha0);
667        }
668        (*mslShaderInfoOut)[type].metalShaderSource =
669            std::make_shared<const std::string>(std::move(source));
670        const sh::TranslatorMetalReflection *reflection =
671            &shadersState[type]->translatorMetalReflection;
672        if (reflection->hasUBOs)
673        {
674            (*mslShaderInfoOut)[type].hasUBOArgumentBuffer = true;
675
676            for (auto &uboBinding : reflection->getUniformBufferBindings())
677            {
678                const std::string &uboName         = uboBinding.first;
679                const sh::UBOBindingInfo &bindInfo = uboBinding.second;
680                const uint32_t uboBindIndex        = static_cast<uint32_t>(bindInfo.bindIndex);
681                const uint32_t uboArraySize        = static_cast<uint32_t>(bindInfo.arraySize);
682                const uint32_t originalBinding     = uboOriginalBindings.at(uboName);
683                uint32_t currentSlot               = static_cast<uint>(uboBindIndex);
684                for (uint32_t i = 0; i < uboArraySize; ++i)
685                {
686                    // Use consecutive slot for member in array
687                    (*mslShaderInfoOut)[type].actualUBOBindings[originalBinding + i] =
688                        currentSlot + i;
689                }
690            }
691        }
692        // Retrieve automatic texture slot assignments
693        if (originalSamplerBindings.size() > 0)
694        {
695            GetAssignedSamplerBindings(reflection, originalSamplerBindings, structSamplers,
696                                       &mslShaderInfoOut->at(type).actualSamplerBindings);
697        }
698        for (uint32_t i = 0; i < kMaxShaderImages; ++i)
699        {
700            mslShaderInfoOut->at(type).actualImageBindings[i] = reflection->getRWTextureBinding(i);
701        }
702        (*mslShaderInfoOut)[type].hasInvariant = reflection->hasInvariance;
703    }
704    return angle::Result::Continue;
705}
706
707uint MslGetShaderShadowCompareMode(GLenum mode, GLenum func)
708{
709    // See SpirvToMslCompiler::emit_header()
710    if (mode == GL_NONE)
711    {
712        return 0;
713    }
714    else
715    {
716        switch (func)
717        {
718            case GL_LESS:
719                return 1;
720            case GL_LEQUAL:
721                return 2;
722            case GL_GREATER:
723                return 3;
724            case GL_GEQUAL:
725                return 4;
726            case GL_NEVER:
727                return 5;
728            case GL_ALWAYS:
729                return 6;
730            case GL_EQUAL:
731                return 7;
732            case GL_NOTEQUAL:
733                return 8;
734            default:
735                UNREACHABLE();
736                return 1;
737        }
738    }
739}
740
741}  // namespace mtl
742}  // namespace rx
743