xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/mtl_msl_utils.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// mtl_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/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    hasIsnanOrIsinf      = false;
144    hasInvariant         = false;
145    for (mtl::SamplerBinding &binding : actualSamplerBindings)
146    {
147        binding.textureBinding = mtl::kMaxShaderSamplers;
148        binding.samplerBinding = 0;
149    }
150    for (int &rwTextureBinding : actualImageBindings)
151    {
152        rwTextureBinding = -1;
153    }
154    for (uint32_t &binding : actualUBOBindings)
155    {
156        binding = mtl::kMaxShaderBuffers;
157    }
158
159    for (uint32_t &binding : actualXFBBindings)
160    {
161        binding = mtl::kMaxShaderBuffers;
162    }
163}
164
165// Original mapping of front end from sampler name to multiple sampler slots (in form of
166// slot:count pair)
167using OriginalSamplerBindingMap =
168    std::unordered_map<std::string, std::vector<std::pair<uint32_t, uint32_t>>>;
169
170bool MappedSamplerNameNeedsUserDefinedPrefix(const std::string &originalName)
171{
172    return originalName.find('.') == std::string::npos;
173}
174
175static std::string MSLGetMappedSamplerName(const std::string &originalName)
176{
177    std::string samplerName = originalName;
178
179    // Samplers in structs are extracted.
180    std::replace(samplerName.begin(), samplerName.end(), '.', '_');
181
182    // Remove array elements
183    auto out = samplerName.begin();
184    for (auto in = samplerName.begin(); in != samplerName.end(); in++)
185    {
186        if (*in == '[')
187        {
188            while (*in != ']')
189            {
190                in++;
191                ASSERT(in != samplerName.end());
192            }
193        }
194        else
195        {
196            *out++ = *in;
197        }
198    }
199
200    samplerName.erase(out, samplerName.end());
201
202    if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
203    {
204        samplerName = sh::kUserDefinedNamePrefix + samplerName;
205    }
206
207    return samplerName;
208}
209
210void MSLGetShaderSource(const gl::ProgramState &programState,
211                        const gl::ProgramLinkedResources &resources,
212                        gl::ShaderMap<std::string> *shaderSourcesOut)
213{
214    for (const gl::ShaderType shaderType : gl::AllShaderTypes())
215    {
216        const gl::SharedCompiledShaderState &glShader = programState.getAttachedShader(shaderType);
217        (*shaderSourcesOut)[shaderType]               = glShader ? glShader->translatedSource : "";
218    }
219}
220
221void GetAssignedSamplerBindings(const sh::TranslatorMetalReflection *reflection,
222                                const OriginalSamplerBindingMap &originalBindings,
223                                std::unordered_set<std::string> &structSamplers,
224                                std::array<SamplerBinding, mtl::kMaxGLSamplerBindings> *bindings)
225{
226    for (auto &sampler : reflection->getSamplerBindings())
227    {
228        const std::string &name          = sampler.first;
229        const uint32_t actualSamplerSlot = (uint32_t)reflection->getSamplerBinding(name);
230        const uint32_t actualTextureSlot = (uint32_t)reflection->getTextureBinding(name);
231
232        // Assign sequential index for subsequent array elements
233        const bool structSampler = structSamplers.find(name) != structSamplers.end();
234        const std::string mappedName =
235            structSampler ? name : MSLGetMappedSamplerName(sh::kUserDefinedNamePrefix + name);
236        auto original = originalBindings.find(mappedName);
237        if (original != originalBindings.end())
238        {
239            const std::vector<std::pair<uint32_t, uint32_t>> &resOrignalBindings =
240                originalBindings.at(mappedName);
241            uint32_t currentTextureSlot = actualTextureSlot;
242            uint32_t currentSamplerSlot = actualSamplerSlot;
243            for (const std::pair<uint32_t, uint32_t> &originalBindingRange : resOrignalBindings)
244            {
245                SamplerBinding &actualBinding = bindings->at(originalBindingRange.first);
246                actualBinding.textureBinding  = currentTextureSlot;
247                actualBinding.samplerBinding  = currentSamplerSlot;
248
249                currentTextureSlot += originalBindingRange.second;
250                currentSamplerSlot += originalBindingRange.second;
251            }
252        }
253    }
254}
255
256std::string UpdateAliasedShaderAttributes(std::string shaderSourceIn,
257                                          const gl::ProgramExecutable &executable)
258{
259    // Cache max number of components for each attribute location
260    std::array<uint8_t, gl::MAX_VERTEX_ATTRIBS> maxComponents{};
261    for (auto &attribute : executable.getProgramInputs())
262    {
263        const int location       = attribute.getLocation();
264        const int registers      = gl::VariableRegisterCount(attribute.getType());
265        const uint8_t components = gl::VariableColumnCount(attribute.getType());
266        for (int i = 0; i < registers; ++i)
267        {
268            ASSERT(location + i < static_cast<int>(maxComponents.size()));
269            maxComponents[location + i] = std::max(maxComponents[location + i], components);
270        }
271    }
272
273    // Define aliased names pointing to real attributes with swizzles as needed
274    std::ostringstream stream;
275    for (auto &attribute : executable.getProgramInputs())
276    {
277        const int location       = attribute.getLocation();
278        const int registers      = gl::VariableRegisterCount(attribute.getType());
279        const uint8_t components = gl::VariableColumnCount(attribute.getType());
280        for (int i = 0; i < registers; i++)
281        {
282            stream << "#define ANGLE_ALIASED_"
283                   << ApplyOStream{ResolveModifiedAttributeName(attribute.name, i, registers)}
284                   << " ANGLE_modified.ANGLE_ATTRIBUTE_" << (location + i);
285            if (components != maxComponents[location + i])
286            {
287                ASSERT(components < maxComponents[location + i]);
288                switch (components)
289                {
290                    case 1:
291                        stream << ".x";
292                        break;
293                    case 2:
294                        stream << ".xy";
295                        break;
296                    case 3:
297                        stream << ".xyz";
298                        break;
299                }
300            }
301            stream << "\n";
302        }
303    }
304
305    // Declare actual MSL attributes
306    for (size_t i : executable.getActiveAttribLocationsMask())
307    {
308        stream << "  float";
309        if (maxComponents[i] > 1)
310        {
311            stream << static_cast<int>(maxComponents[i]);
312        }
313        stream << " ANGLE_ATTRIBUTE_" << i << "[[attribute(" << i << ")]];\n";
314    }
315
316    std::string outputSource = shaderSourceIn;
317    size_t markerFound       = outputSource.find(kAttribBindingsMarker);
318    ASSERT(markerFound != std::string::npos);
319    outputSource.replace(markerFound, angle::ConstStrLen(kAttribBindingsMarker), stream.str());
320    return outputSource;
321}
322
323std::string updateShaderAttributes(std::string shaderSourceIn,
324                                   const gl::ProgramExecutable &executable)
325{
326    // Build string to attrib map.
327    const auto &programAttributes = executable.getProgramInputs();
328    std::ostringstream stream;
329    std::unordered_map<std::string, uint32_t> attributeBindings;
330    for (auto &attribute : programAttributes)
331    {
332        const int registers = gl::VariableRegisterCount(attribute.getType());
333        for (int i = 0; i < registers; i++)
334        {
335            stream.str("");
336            stream << ' '
337                   << ApplyOStream{ResolveModifiedAttributeName(attribute.name, i, registers)}
338                   << sh::kUnassignedAttributeString;
339            attributeBindings.insert({stream.str(), i + attribute.getLocation()});
340        }
341    }
342    // Rewrite attributes
343    std::string outputSource = shaderSourceIn;
344    for (auto it = attributeBindings.begin(); it != attributeBindings.end(); ++it)
345    {
346        std::size_t attribFound = outputSource.find(it->first);
347        if (attribFound != std::string::npos)
348        {
349            stream.str("");
350            stream << "[[attribute(" << it->second << ")]]";
351            outputSource = outputSource.replace(
352                attribFound + it->first.length() -
353                    angle::ConstStrLen(sh::kUnassignedAttributeString),
354                angle::ConstStrLen(sh::kUnassignedAttributeString), stream.str());
355        }
356    }
357    return outputSource;
358}
359
360std::string UpdateFragmentShaderOutputs(std::string shaderSourceIn,
361                                        const gl::ProgramExecutable &executable,
362                                        bool defineAlpha0)
363{
364    std::ostringstream stream;
365    std::string outputSource    = shaderSourceIn;
366    const auto &outputVariables = executable.getOutputVariables();
367
368    // For alpha-to-coverage emulation, a reference to the alpha channel
369    // of color output 0 is needed. For ESSL 1.00, it is gl_FragColor or
370    // gl_FragData[0]; for ESSL 3.xx, it is a user-defined output.
371    std::string alphaOutputName;
372
373    auto assignLocations = [&](const std::vector<gl::VariableLocation> &locations, bool secondary) {
374        for (auto &outputLocation : locations)
375        {
376            if (!outputLocation.used())
377            {
378                continue;
379            }
380            const int index                    = outputLocation.arrayIndex;
381            const gl::ProgramOutput &outputVar = outputVariables[outputLocation.index];
382            ASSERT(outputVar.pod.location >= 0);
383            const int location  = outputVar.pod.location + index;
384            const int arraySize = outputVar.getOutermostArraySize();
385            stream.str("");
386            stream << ApplyOStream{ResolveModifiedOutputName(outputVar.name, index, arraySize)}
387                   << " [[" << sh::kUnassignedFragmentOutputString;
388            const std::string placeholder(stream.str());
389
390            size_t outputFound = outputSource.find(placeholder);
391            if (outputFound != std::string::npos)
392            {
393                stream.str("");
394                stream << "color(" << location << (secondary ? "), index(1)" : ")");
395                outputSource = outputSource.replace(
396                    outputFound + placeholder.length() -
397                        angle::ConstStrLen(sh::kUnassignedFragmentOutputString),
398                    angle::ConstStrLen(sh::kUnassignedFragmentOutputString), stream.str());
399            }
400
401            if (defineAlpha0 && location == 0 && !secondary && outputVar.pod.type == GL_FLOAT_VEC4)
402            {
403                ASSERT(index == 0);
404                ASSERT(alphaOutputName.empty());
405                std::ostringstream nameStream;
406                nameStream << "ANGLE_fragmentOut."
407                           << ApplyOStream{ResolveUserDefinedName(outputVar.name, index, arraySize)}
408                           << ".a";
409                alphaOutputName = nameStream.str();
410            }
411        }
412    };
413    assignLocations(executable.getOutputLocations(), false);
414    assignLocations(executable.getSecondaryOutputLocations(), true);
415
416    if (defineAlpha0)
417    {
418        // Locations are empty for ESSL 1.00 shaders, try built-in outputs
419        if (alphaOutputName.empty())
420        {
421            for (auto &v : outputVariables)
422            {
423                if (v.name == "gl_FragColor")
424                {
425                    alphaOutputName = "ANGLE_fragmentOut.gl_FragColor.a";
426                    break;
427                }
428                else if (v.name == "gl_FragData")
429                {
430                    alphaOutputName = "ANGLE_fragmentOut.ANGLE_gl_FragData_0.a";
431                    break;
432                }
433            }
434        }
435
436        // Set a value used for alpha-to-coverage emulation
437        const std::string alphaPlaceholder("#define ANGLE_ALPHA0");
438        size_t alphaFound = outputSource.find(alphaPlaceholder);
439        ASSERT(alphaFound != std::string::npos);
440
441        std::ostringstream alphaStream;
442        alphaStream << alphaPlaceholder << " ";
443        alphaStream << (alphaOutputName.empty() ? "1.0" : alphaOutputName);
444        outputSource =
445            outputSource.replace(alphaFound, alphaPlaceholder.length(), alphaStream.str());
446    }
447
448    return outputSource;
449}
450
451std::string SubstituteTransformFeedbackMarkers(const std::string &originalSource,
452                                               const std::string &xfbBindings,
453                                               const std::string &xfbOut)
454{
455    const size_t xfbBindingsMarkerStart = originalSource.find(kXfbBindingsMarker);
456    bool hasBindingsMarker              = xfbBindingsMarkerStart != std::string::npos;
457    const size_t xfbBindingsMarkerEnd =
458        xfbBindingsMarkerStart + angle::ConstStrLen(kXfbBindingsMarker);
459
460    const size_t xfbOutMarkerStart = originalSource.find(kXfbOutMarker, xfbBindingsMarkerStart);
461    bool hasOutMarker              = xfbOutMarkerStart != std::string::npos;
462    const size_t xfbOutMarkerEnd   = xfbOutMarkerStart + angle::ConstStrLen(kXfbOutMarker);
463
464    // The shader is the following form:
465    //
466    // ..part1..
467    // @@ XFB-BINDINGS @@
468    // ..part2..
469    // @@ XFB-OUT @@;
470    // ..part3..
471    //
472    // Construct the string by concatenating these five pieces, replacing the markers with the given
473    // values.
474    std::string result;
475    if (hasBindingsMarker && hasOutMarker)
476    {
477        result.append(&originalSource[0], &originalSource[xfbBindingsMarkerStart]);
478        result.append(xfbBindings);
479        result.append(&originalSource[xfbBindingsMarkerEnd], &originalSource[xfbOutMarkerStart]);
480        result.append(xfbOut);
481        result.append(&originalSource[xfbOutMarkerEnd], &originalSource[originalSource.size()]);
482        return result;
483    }
484    return originalSource;
485}
486
487std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVarying &varying,
488                                                   const gl::UniformTypeInfo &info,
489                                                   size_t strideBytes,
490                                                   size_t offset,
491                                                   const std::string &bufferIndex)
492{
493    std::ostringstream result;
494
495    ASSERT(strideBytes % 4 == 0);
496    size_t stride = strideBytes / 4;
497
498    const size_t arrayIndexStart = varying.arrayIndex == GL_INVALID_INDEX ? 0 : varying.arrayIndex;
499    const size_t arrayIndexEnd   = arrayIndexStart + varying.size();
500
501    for (size_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
502    {
503        for (int col = 0; col < info.columnCount; ++col)
504        {
505            for (int row = 0; row < info.rowCount; ++row)
506            {
507                result << "        ";
508                result << "ANGLE_" << "xfbBuffer" << bufferIndex << "[" << "ANGLE_"
509                       << std::string(sh::kUniformsVar) << ".ANGLE_xfbBufferOffsets[" << bufferIndex
510                       << "] + (ANGLE_vertexIDMetal + (ANGLE_instanceIdMod - ANGLE_baseInstance) * "
511                       << "ANGLE_" << std::string(sh::kUniformsVar)
512                       << ".ANGLE_xfbVerticesPerInstance) * " << stride << " + " << offset
513                       << "] = " << "as_type<float>" << "(" << "ANGLE_vertexOut.";
514                if (!varying.isBuiltIn())
515                {
516                    result << kUserDefinedNamePrefix;
517                }
518                result << varying.name;
519
520                if (varying.isArray())
521                {
522                    result << "[" << arrayIndex << "]";
523                }
524
525                if (info.columnCount > 1)
526                {
527                    result << "[" << col << "]";
528                }
529
530                if (info.rowCount > 1)
531                {
532                    result << "[" << row << "]";
533                }
534
535                result << ");\n";
536                ++offset;
537            }
538        }
539    }
540
541    return result.str();
542}
543
544void GenerateTransformFeedbackEmulationOutputs(
545    const gl::ProgramExecutable &executable,
546    std::string *vertexShader,
547    std::array<uint32_t, kMaxShaderXFBs> *xfbBindingRemapOut)
548{
549    const std::vector<gl::TransformFeedbackVarying> &varyings =
550        executable.getLinkedTransformFeedbackVaryings();
551    const std::vector<GLsizei> &bufferStrides = executable.getTransformFeedbackStrides();
552    const bool isInterleaved =
553        executable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
554    const size_t bufferCount = isInterleaved ? 1 : varyings.size();
555
556    std::vector<std::string> xfbIndices(bufferCount);
557
558    std::string xfbBindings;
559
560    for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
561    {
562        const std::string xfbBinding = Str(0);
563        xfbIndices[bufferIndex]      = Str(bufferIndex);
564
565        std::string bufferName = GetXfbBufferNameMtl(bufferIndex);
566
567        xfbBindings += ", ";
568        // TODO: offset from last used buffer binding from front end
569        // XFB buffer is allocated slot starting from last discrete Metal buffer slot.
570        uint32_t bindingPoint               = kMaxShaderBuffers - 1 - bufferIndex;
571        xfbBindingRemapOut->at(bufferIndex) = bindingPoint;
572        xfbBindings +=
573            "device float* ANGLE_" + bufferName + " [[buffer(" + Str(bindingPoint) + ")]]";
574    }
575
576    std::string xfbOut  = "#if TRANSFORM_FEEDBACK_ENABLED\n    {\n";
577    size_t outputOffset = 0;
578    for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
579    {
580        const size_t bufferIndex                    = isInterleaved ? 0 : varyingIndex;
581        const gl::TransformFeedbackVarying &varying = varyings[varyingIndex];
582
583        // For every varying, output to the respective buffer packed.  If interleaved, the output is
584        // always to the same buffer, but at different offsets.
585        const gl::UniformTypeInfo &info = gl::GetUniformTypeInfo(varying.type);
586        xfbOut += GenerateTransformFeedbackVaryingOutput(varying, info, bufferStrides[bufferIndex],
587                                                         outputOffset, xfbIndices[bufferIndex]);
588
589        if (isInterleaved)
590        {
591            outputOffset += info.columnCount * info.rowCount * varying.size();
592        }
593    }
594    xfbOut += "    }\n#endif\n";
595
596    *vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, xfbBindings, xfbOut);
597}
598
599angle::Result MTLGetMSL(const angle::FeaturesMtl &features,
600                        const gl::ProgramExecutable &executable,
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            const bool defineAlpha0 = features.emulateAlphaToCoverage.enabled ||
664                                      features.generateShareableShaders.enabled;
665            source = UpdateFragmentShaderOutputs(shaderSources[type], executable, defineAlpha0);
666        }
667        (*mslShaderInfoOut)[type].metalShaderSource =
668            std::make_shared<const std::string>(std::move(source));
669        const sh::TranslatorMetalReflection *reflection =
670            &shadersState[type]->translatorMetalReflection;
671        if (reflection->hasUBOs)
672        {
673            (*mslShaderInfoOut)[type].hasUBOArgumentBuffer = true;
674
675            for (auto &uboBinding : reflection->getUniformBufferBindings())
676            {
677                const std::string &uboName         = uboBinding.first;
678                const sh::UBOBindingInfo &bindInfo = uboBinding.second;
679                const uint32_t uboBindIndex        = static_cast<uint32_t>(bindInfo.bindIndex);
680                const uint32_t uboArraySize        = static_cast<uint32_t>(bindInfo.arraySize);
681                const uint32_t originalBinding     = uboOriginalBindings.at(uboName);
682                uint32_t currentSlot               = static_cast<uint>(uboBindIndex);
683                for (uint32_t i = 0; i < uboArraySize; ++i)
684                {
685                    // Use consecutive slot for member in array
686                    (*mslShaderInfoOut)[type].actualUBOBindings[originalBinding + i] =
687                        currentSlot + i;
688                }
689            }
690        }
691        // Retrieve automatic texture slot assignments
692        if (originalSamplerBindings.size() > 0)
693        {
694            GetAssignedSamplerBindings(reflection, originalSamplerBindings, structSamplers,
695                                       &mslShaderInfoOut->at(type).actualSamplerBindings);
696        }
697        for (uint32_t i = 0; i < kMaxShaderImages; ++i)
698        {
699            mslShaderInfoOut->at(type).actualImageBindings[i] = reflection->getRWTextureBinding(i);
700        }
701        (*mslShaderInfoOut)[type].hasIsnanOrIsinf = reflection->hasIsnanOrIsinf;
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