• 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// 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 <spirv_msl.hpp>
17
18#include "common/debug.h"
19#include "libANGLE/Context.h"
20#include "libANGLE/ProgramLinkedResources.h"
21#include "libANGLE/renderer/metal/ContextMtl.h"
22#include "libANGLE/renderer/metal/DisplayMtl.h"
23#include "libANGLE/renderer/metal/TextureMtl.h"
24#include "libANGLE/renderer/metal/mtl_glslang_utils.h"
25#include "libANGLE/renderer/metal/mtl_utils.h"
26#include "libANGLE/renderer/renderer_utils.h"
27
28namespace rx
29{
30
31namespace
32{
33
34#define SHADER_ENTRY_NAME @"main0"
35
36spv::ExecutionModel ShaderTypeToSpvExecutionModel(gl::ShaderType shaderType)
37{
38    switch (shaderType)
39    {
40        case gl::ShaderType::Vertex:
41            return spv::ExecutionModelVertex;
42        case gl::ShaderType::Fragment:
43            return spv::ExecutionModelFragment;
44        default:
45            UNREACHABLE();
46            return spv::ExecutionModelMax;
47    }
48}
49
50// Some GLSL variables need 2 binding points in metal. For example,
51// glsl sampler will be converted to 2 metal objects: texture and sampler.
52// Thus we need to set 2 binding points for one glsl sampler variable.
53using BindingField = uint32_t spirv_cross::MSLResourceBinding::*;
54template <BindingField bindingField1, BindingField bindingField2 = bindingField1>
55angle::Result BindResources(spirv_cross::CompilerMSL *compiler,
56                            const spirv_cross::SmallVector<spirv_cross::Resource> &resources,
57                            gl::ShaderType shaderType)
58{
59    auto &compilerMsl = *compiler;
60
61    for (const spirv_cross::Resource &resource : resources)
62    {
63        spirv_cross::MSLResourceBinding resBinding;
64        resBinding.stage = ShaderTypeToSpvExecutionModel(shaderType);
65
66        if (compilerMsl.has_decoration(resource.id, spv::DecorationDescriptorSet))
67        {
68            resBinding.desc_set =
69                compilerMsl.get_decoration(resource.id, spv::DecorationDescriptorSet);
70        }
71
72        if (!compilerMsl.has_decoration(resource.id, spv::DecorationBinding))
73        {
74            continue;
75        }
76
77        resBinding.binding = compilerMsl.get_decoration(resource.id, spv::DecorationBinding);
78
79        uint32_t bindingPoint;
80        // NOTE(hqle): We use separate discrete binding point for now, in future, we should use
81        // one argument buffer for each descriptor set.
82        switch (resBinding.desc_set)
83        {
84            case 0:
85                // Use resBinding.binding as binding point.
86                bindingPoint = resBinding.binding;
87                break;
88            case mtl::kDriverUniformsBindingIndex:
89                bindingPoint = mtl::kDriverUniformsBindingIndex;
90                break;
91            case mtl::kDefaultUniformsBindingIndex:
92                // NOTE(hqle): Properly handle transform feedbacks and UBO binding once ES 3.0 is
93                // implemented.
94                bindingPoint = mtl::kDefaultUniformsBindingIndex;
95                break;
96            default:
97                // We don't support this descriptor set.
98                continue;
99        }
100
101        // bindingField can be buffer or texture, which will be translated to [[buffer(d)]] or
102        // [[texture(d)]] or [[sampler(d)]]
103        resBinding.*bindingField1 = bindingPoint;
104        if (bindingField1 != bindingField2)
105        {
106            resBinding.*bindingField2 = bindingPoint;
107        }
108
109        compilerMsl.add_msl_resource_binding(resBinding);
110    }
111
112    return angle::Result::Continue;
113}
114
115void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
116                             gl::Shader *shader,
117                             sh::BlockLayoutMap *blockLayoutMapOut,
118                             size_t *blockSizeOut)
119{
120    if (uniforms.empty())
121    {
122        *blockSizeOut = 0;
123        return;
124    }
125
126    sh::Std140BlockEncoder blockEncoder;
127    sh::GetActiveUniformBlockInfo(uniforms, "", &blockEncoder, blockLayoutMapOut);
128
129    size_t blockSize = blockEncoder.getCurrentOffset();
130
131    if (blockSize == 0)
132    {
133        *blockSizeOut = 0;
134        return;
135    }
136
137    // Need to round up to multiple of vec4
138    *blockSizeOut = roundUp(blockSize, static_cast<size_t>(16));
139    return;
140}
141
142template <typename T>
143void UpdateDefaultUniformBlock(GLsizei count,
144                               uint32_t arrayIndex,
145                               int componentCount,
146                               const T *v,
147                               const sh::BlockMemberInfo &layoutInfo,
148                               angle::MemoryBuffer *uniformData)
149{
150    const int elementSize = sizeof(T) * componentCount;
151
152    uint8_t *dst = uniformData->data() + layoutInfo.offset;
153    if (layoutInfo.arrayStride == 0 || layoutInfo.arrayStride == elementSize)
154    {
155        uint32_t arrayOffset = arrayIndex * layoutInfo.arrayStride;
156        uint8_t *writePtr    = dst + arrayOffset;
157        ASSERT(writePtr + (elementSize * count) <= uniformData->data() + uniformData->size());
158        memcpy(writePtr, v, elementSize * count);
159    }
160    else
161    {
162        // Have to respect the arrayStride between each element of the array.
163        int maxIndex = arrayIndex + count;
164        for (int writeIndex = arrayIndex, readIndex = 0; writeIndex < maxIndex;
165             writeIndex++, readIndex++)
166        {
167            const int arrayOffset = writeIndex * layoutInfo.arrayStride;
168            uint8_t *writePtr     = dst + arrayOffset;
169            const T *readPtr      = v + (readIndex * componentCount);
170            ASSERT(writePtr + elementSize <= uniformData->data() + uniformData->size());
171            memcpy(writePtr, readPtr, elementSize);
172        }
173    }
174}
175
176void BindNullSampler(const gl::Context *glContext,
177                     mtl::RenderCommandEncoder *encoder,
178                     gl::ShaderType shaderType,
179                     gl::TextureType textureType,
180                     int bindingPoint)
181{
182    ContextMtl *contextMtl = mtl::GetImpl(glContext);
183
184    const mtl::TextureRef &nullTex =
185        contextMtl->getDisplay()->getNullTexture(glContext, textureType);
186
187    mtl::AutoObjCPtr<id<MTLSamplerState>> nullSampler =
188        contextMtl->getDisplay()->getStateCache().getNullSamplerState(contextMtl);
189
190    switch (shaderType)
191    {
192        case gl::ShaderType::Vertex:
193            encoder->setVertexTexture(nullTex, bindingPoint);
194            encoder->setVertexSamplerState(nullSampler, 0, 0, bindingPoint);
195            break;
196        case gl::ShaderType::Fragment:
197            encoder->setFragmentTexture(nullTex, bindingPoint);
198            encoder->setFragmentSamplerState(nullSampler, 0, 0, bindingPoint);
199            break;
200        default:
201            UNREACHABLE();
202    }
203}
204
205template <typename T>
206void ReadFromDefaultUniformBlock(int componentCount,
207                                 uint32_t arrayIndex,
208                                 T *dst,
209                                 const sh::BlockMemberInfo &layoutInfo,
210                                 const angle::MemoryBuffer *uniformData)
211{
212    ASSERT(layoutInfo.offset != -1);
213
214    const int elementSize = sizeof(T) * componentCount;
215    const uint8_t *source = uniformData->data() + layoutInfo.offset;
216
217    if (layoutInfo.arrayStride == 0 || layoutInfo.arrayStride == elementSize)
218    {
219        const uint8_t *readPtr = source + arrayIndex * layoutInfo.arrayStride;
220        memcpy(dst, readPtr, elementSize);
221    }
222    else
223    {
224        // Have to respect the arrayStride between each element of the array.
225        const int arrayOffset  = arrayIndex * layoutInfo.arrayStride;
226        const uint8_t *readPtr = source + arrayOffset;
227        memcpy(dst, readPtr, elementSize);
228    }
229}
230
231class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
232{
233  public:
234    sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
235};
236
237}  // namespace
238
239// ProgramMtl implementation
240ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {}
241
242ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default;
243
244ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {}
245
246ProgramMtl::~ProgramMtl() {}
247
248void ProgramMtl::destroy(const gl::Context *context)
249{
250    auto contextMtl = mtl::GetImpl(context);
251
252    reset(contextMtl);
253}
254
255void ProgramMtl::reset(ContextMtl *context)
256{
257    for (auto &block : mDefaultUniformBlocks)
258    {
259        block.uniformLayout.clear();
260    }
261
262    mMetalRenderPipelineCache.clear();
263}
264
265std::unique_ptr<rx::LinkEvent> ProgramMtl::load(const gl::Context *context,
266                                                gl::BinaryInputStream *stream,
267                                                gl::InfoLog &infoLog)
268{
269    // NOTE(hqle): support binary shader
270    UNIMPLEMENTED();
271    return std::make_unique<LinkEventDone>(angle::Result::Stop);
272}
273
274void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream)
275{
276    // NOTE(hqle): support binary shader
277    UNIMPLEMENTED();
278}
279
280void ProgramMtl::setBinaryRetrievableHint(bool retrievable)
281{
282    UNIMPLEMENTED();
283}
284
285void ProgramMtl::setSeparable(bool separable)
286{
287    UNIMPLEMENTED();
288}
289
290std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
291                                            const gl::ProgramLinkedResources &resources,
292                                            gl::InfoLog &infoLog)
293{
294    // Link resources before calling GetShaderSource to make sure they are ready for the set/binding
295    // assignment done in that function.
296    linkResources(resources);
297
298    // NOTE(hqle): Parallelize linking.
299    return std::make_unique<LinkEventDone>(linkImpl(context, resources, infoLog));
300}
301
302angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
303                                   const gl::ProgramLinkedResources &resources,
304                                   gl::InfoLog &infoLog)
305{
306    ContextMtl *contextMtl = mtl::GetImpl(glContext);
307    // NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
308
309    reset(contextMtl);
310
311    ANGLE_TRY(initDefaultUniformBlocks(glContext));
312
313    // Gather variable info and transform sources.
314    gl::ShaderMap<std::string> shaderSources;
315    ShaderMapInterfaceVariableInfoMap variableInfoMap;
316    mtl::GlslangGetShaderSource(mState, resources, &shaderSources, &variableInfoMap);
317
318    // Convert GLSL to spirv code
319    gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
320    ANGLE_TRY(mtl::GlslangGetShaderSpirvCode(contextMtl, contextMtl->getCaps(), shaderSources,
321                                             variableInfoMap, &shaderCodes));
322
323    // Convert spirv code to MSL
324    ANGLE_TRY(convertToMsl(glContext, gl::ShaderType::Vertex, infoLog,
325                           &shaderCodes[gl::ShaderType::Vertex]));
326    ANGLE_TRY(convertToMsl(glContext, gl::ShaderType::Fragment, infoLog,
327                           &shaderCodes[gl::ShaderType::Fragment]));
328
329    return angle::Result::Continue;
330}
331
332void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)
333{
334    Std140BlockLayoutEncoderFactory std140EncoderFactory;
335    gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory);
336
337    linker.linkResources(mState, resources);
338}
339
340angle::Result ProgramMtl::initDefaultUniformBlocks(const gl::Context *glContext)
341{
342    ContextMtl *contextMtl = mtl::GetImpl(glContext);
343
344    // Process vertex and fragment uniforms into std140 packing.
345    gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
346    gl::ShaderMap<size_t> requiredBufferSize;
347    requiredBufferSize.fill(0);
348
349    for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
350    {
351        gl::Shader *shader = mState.getAttachedShader(shaderType);
352        if (shader)
353        {
354            const std::vector<sh::Uniform> &uniforms = shader->getUniforms();
355            InitDefaultUniformBlock(uniforms, shader, &layoutMap[shaderType],
356                                    &requiredBufferSize[shaderType]);
357        }
358    }
359
360    // Init the default block layout info.
361    const auto &uniforms         = mState.getUniforms();
362    const auto &uniformLocations = mState.getUniformLocations();
363    for (size_t locSlot = 0; locSlot < uniformLocations.size(); ++locSlot)
364    {
365        const gl::VariableLocation &location = uniformLocations[locSlot];
366        gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
367
368        if (location.used() && !location.ignored)
369        {
370            const gl::LinkedUniform &uniform = uniforms[location.index];
371            if (uniform.isInDefaultBlock() && !uniform.isSampler())
372            {
373                std::string uniformName = uniform.name;
374                if (uniform.isArray())
375                {
376                    // Gets the uniform name without the [0] at the end.
377                    uniformName = gl::ParseResourceName(uniformName, nullptr);
378                }
379
380                bool found = false;
381
382                for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
383                {
384                    auto it = layoutMap[shaderType].find(uniformName);
385                    if (it != layoutMap[shaderType].end())
386                    {
387                        found                  = true;
388                        layoutInfo[shaderType] = it->second;
389                    }
390                }
391
392                ASSERT(found);
393            }
394        }
395
396        for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
397        {
398            mDefaultUniformBlocks[shaderType].uniformLayout.push_back(layoutInfo[shaderType]);
399        }
400    }
401
402    for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
403    {
404        if (requiredBufferSize[shaderType] > 0)
405        {
406            ASSERT(requiredBufferSize[shaderType] <= mtl::kDefaultUniformsMaxSize);
407
408            if (!mDefaultUniformBlocks[shaderType].uniformData.resize(
409                    requiredBufferSize[shaderType]))
410            {
411                ANGLE_MTL_CHECK(contextMtl, false, GL_OUT_OF_MEMORY);
412            }
413
414            // Initialize uniform buffer memory to zero by default.
415            mDefaultUniformBlocks[shaderType].uniformData.fill(0);
416            mDefaultUniformBlocksDirty.set(shaderType);
417        }
418    }
419
420    return angle::Result::Continue;
421}
422
423angle::Result ProgramMtl::convertToMsl(const gl::Context *glContext,
424                                       gl::ShaderType shaderType,
425                                       gl::InfoLog &infoLog,
426                                       std::vector<uint32_t> *sprivCode)
427{
428    ContextMtl *contextMtl = mtl::GetImpl(glContext);
429
430    spirv_cross::CompilerMSL compilerMsl(std::move(*sprivCode));
431
432    spirv_cross::CompilerMSL::Options compOpt;
433#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
434    compOpt.platform = spirv_cross::CompilerMSL::Options::macOS;
435#else
436    compOpt.platform = spirv_cross::CompilerMSL::Options::iOS;
437#endif
438
439    if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.0, 12))
440    {
441        // Use Metal 2.1
442        compOpt.set_msl_version(2, 1);
443    }
444    else
445    {
446        // Always use at least Metal 2.0.
447        compOpt.set_msl_version(2);
448    }
449
450    compilerMsl.set_msl_options(compOpt);
451
452    // Tell spirv-cross to map default & driver uniform blocks & samplers as we want
453    spirv_cross::ShaderResources mslRes = compilerMsl.get_shader_resources();
454
455    ANGLE_TRY(BindResources<&spirv_cross::MSLResourceBinding::msl_buffer>(
456        &compilerMsl, mslRes.uniform_buffers, shaderType));
457
458    ANGLE_TRY((BindResources<&spirv_cross::MSLResourceBinding::msl_sampler,
459                             &spirv_cross::MSLResourceBinding::msl_texture>(
460        &compilerMsl, mslRes.sampled_images, shaderType)));
461
462    // NOTE(hqle): spirv-cross uses exceptions to report error, what should we do here
463    // in case of error?
464    std::string translatedMsl = compilerMsl.compile();
465    if (translatedMsl.size() == 0)
466    {
467        ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
468    }
469
470    // Create actual Metal shader
471    ANGLE_TRY(createMslShader(glContext, shaderType, infoLog, translatedMsl));
472
473    return angle::Result::Continue;
474}
475
476angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
477                                          gl::ShaderType shaderType,
478                                          gl::InfoLog &infoLog,
479                                          const std::string &translatedMsl)
480{
481    ANGLE_MTL_OBJC_SCOPE
482    {
483        ContextMtl *contextMtl  = mtl::GetImpl(glContext);
484        DisplayMtl *display     = contextMtl->getDisplay();
485        id<MTLDevice> mtlDevice = display->getMetalDevice();
486
487        // Convert to actual binary shader
488        mtl::AutoObjCPtr<NSError *> err = nil;
489        mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib =
490            mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
491        if (err && !mtlShaderLib)
492        {
493            std::ostringstream ss;
494            ss << "Internal error compiling Metal shader:\n"
495               << err.get().localizedDescription.UTF8String << "\n";
496
497            ERR() << ss.str();
498
499            infoLog << ss.str();
500
501            ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
502        }
503
504        auto mtlShader = [mtlShaderLib.get() newFunctionWithName:SHADER_ENTRY_NAME];
505        [mtlShader ANGLE_MTL_AUTORELEASE];
506        ASSERT(mtlShader);
507        if (shaderType == gl::ShaderType::Vertex)
508        {
509            mMetalRenderPipelineCache.setVertexShader(contextMtl, mtlShader);
510        }
511        else if (shaderType == gl::ShaderType::Fragment)
512        {
513            mMetalRenderPipelineCache.setFragmentShader(contextMtl, mtlShader);
514        }
515
516        return angle::Result::Continue;
517    }
518}
519
520GLboolean ProgramMtl::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
521{
522    // No-op. The spec is very vague about the behavior of validation.
523    return GL_TRUE;
524}
525
526template <typename T>
527void ProgramMtl::setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType)
528{
529    const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
530    const gl::LinkedUniform &linkedUniform   = mState.getUniforms()[locationInfo.index];
531
532    if (linkedUniform.isSampler())
533    {
534        // Sampler binding has changed.
535        mSamplerBindingsDirty.set();
536        return;
537    }
538
539    if (linkedUniform.typeInfo->type == entryPointType)
540    {
541        for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
542        {
543            DefaultUniformBlock &uniformBlock     = mDefaultUniformBlocks[shaderType];
544            const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
545
546            // Assume an offset of -1 means the block is unused.
547            if (layoutInfo.offset == -1)
548            {
549                continue;
550            }
551
552            const GLint componentCount = linkedUniform.typeInfo->componentCount;
553            UpdateDefaultUniformBlock(count, locationInfo.arrayIndex, componentCount, v, layoutInfo,
554                                      &uniformBlock.uniformData);
555            mDefaultUniformBlocksDirty.set(shaderType);
556        }
557    }
558    else
559    {
560        for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
561        {
562            DefaultUniformBlock &uniformBlock     = mDefaultUniformBlocks[shaderType];
563            const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
564
565            // Assume an offset of -1 means the block is unused.
566            if (layoutInfo.offset == -1)
567            {
568                continue;
569            }
570
571            const GLint componentCount = linkedUniform.typeInfo->componentCount;
572
573            ASSERT(linkedUniform.typeInfo->type == gl::VariableBoolVectorType(entryPointType));
574
575            GLint initialArrayOffset =
576                locationInfo.arrayIndex * layoutInfo.arrayStride + layoutInfo.offset;
577            for (GLint i = 0; i < count; i++)
578            {
579                GLint elementOffset = i * layoutInfo.arrayStride + initialArrayOffset;
580                GLint *dest =
581                    reinterpret_cast<GLint *>(uniformBlock.uniformData.data() + elementOffset);
582                const T *source = v + i * componentCount;
583
584                for (int c = 0; c < componentCount; c++)
585                {
586                    dest[c] = (source[c] == static_cast<T>(0)) ? GL_FALSE : GL_TRUE;
587                }
588            }
589
590            mDefaultUniformBlocksDirty.set(shaderType);
591        }
592    }
593}
594
595template <typename T>
596void ProgramMtl::getUniformImpl(GLint location, T *v, GLenum entryPointType) const
597{
598    const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
599    const gl::LinkedUniform &linkedUniform   = mState.getUniforms()[locationInfo.index];
600
601    ASSERT(!linkedUniform.isSampler());
602
603    const gl::ShaderType shaderType = linkedUniform.getFirstShaderTypeWhereActive();
604    ASSERT(shaderType != gl::ShaderType::InvalidEnum);
605
606    const DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
607    const sh::BlockMemberInfo &layoutInfo   = uniformBlock.uniformLayout[location];
608
609    ASSERT(linkedUniform.typeInfo->componentType == entryPointType ||
610           linkedUniform.typeInfo->componentType == gl::VariableBoolVectorType(entryPointType));
611
612    if (gl::IsMatrixType(linkedUniform.type))
613    {
614        const uint8_t *ptrToElement = uniformBlock.uniformData.data() + layoutInfo.offset +
615                                      (locationInfo.arrayIndex * layoutInfo.arrayStride);
616        GetMatrixUniform(linkedUniform.type, v, reinterpret_cast<const T *>(ptrToElement), false);
617    }
618    else
619    {
620        ReadFromDefaultUniformBlock(linkedUniform.typeInfo->componentCount, locationInfo.arrayIndex,
621                                    v, layoutInfo, &uniformBlock.uniformData);
622    }
623}
624
625void ProgramMtl::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
626{
627    setUniformImpl(location, count, v, GL_FLOAT);
628}
629
630void ProgramMtl::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
631{
632    setUniformImpl(location, count, v, GL_FLOAT_VEC2);
633}
634
635void ProgramMtl::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
636{
637    setUniformImpl(location, count, v, GL_FLOAT_VEC3);
638}
639
640void ProgramMtl::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
641{
642    setUniformImpl(location, count, v, GL_FLOAT_VEC4);
643}
644
645void ProgramMtl::setUniform1iv(GLint startLocation, GLsizei count, const GLint *v)
646{
647    setUniformImpl(startLocation, count, v, GL_INT);
648}
649
650void ProgramMtl::setUniform2iv(GLint location, GLsizei count, const GLint *v)
651{
652    setUniformImpl(location, count, v, GL_INT_VEC2);
653}
654
655void ProgramMtl::setUniform3iv(GLint location, GLsizei count, const GLint *v)
656{
657    setUniformImpl(location, count, v, GL_INT_VEC3);
658}
659
660void ProgramMtl::setUniform4iv(GLint location, GLsizei count, const GLint *v)
661{
662    setUniformImpl(location, count, v, GL_INT_VEC4);
663}
664
665void ProgramMtl::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
666{
667    setUniformImpl(location, count, v, GL_UNSIGNED_INT);
668}
669
670void ProgramMtl::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
671{
672    setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC2);
673}
674
675void ProgramMtl::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
676{
677    setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC3);
678}
679
680void ProgramMtl::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
681{
682    setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC4);
683}
684
685template <int cols, int rows>
686void ProgramMtl::setUniformMatrixfv(GLint location,
687                                    GLsizei count,
688                                    GLboolean transpose,
689                                    const GLfloat *value)
690{
691    const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
692    const gl::LinkedUniform &linkedUniform   = mState.getUniforms()[locationInfo.index];
693
694    for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
695    {
696        DefaultUniformBlock &uniformBlock     = mDefaultUniformBlocks[shaderType];
697        const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
698
699        // Assume an offset of -1 means the block is unused.
700        if (layoutInfo.offset == -1)
701        {
702            continue;
703        }
704
705        SetFloatUniformMatrixGLSL<cols, rows>::Run(
706            locationInfo.arrayIndex, linkedUniform.getArraySizeProduct(), count, transpose, value,
707            uniformBlock.uniformData.data() + layoutInfo.offset);
708
709        mDefaultUniformBlocksDirty.set(shaderType);
710    }
711}
712
713void ProgramMtl::setUniformMatrix2fv(GLint location,
714                                     GLsizei count,
715                                     GLboolean transpose,
716                                     const GLfloat *value)
717{
718    setUniformMatrixfv<2, 2>(location, count, transpose, value);
719}
720
721void ProgramMtl::setUniformMatrix3fv(GLint location,
722                                     GLsizei count,
723                                     GLboolean transpose,
724                                     const GLfloat *value)
725{
726    setUniformMatrixfv<3, 3>(location, count, transpose, value);
727}
728
729void ProgramMtl::setUniformMatrix4fv(GLint location,
730                                     GLsizei count,
731                                     GLboolean transpose,
732                                     const GLfloat *value)
733{
734    setUniformMatrixfv<4, 4>(location, count, transpose, value);
735}
736
737void ProgramMtl::setUniformMatrix2x3fv(GLint location,
738                                       GLsizei count,
739                                       GLboolean transpose,
740                                       const GLfloat *value)
741{
742    setUniformMatrixfv<2, 3>(location, count, transpose, value);
743}
744
745void ProgramMtl::setUniformMatrix3x2fv(GLint location,
746                                       GLsizei count,
747                                       GLboolean transpose,
748                                       const GLfloat *value)
749{
750    setUniformMatrixfv<3, 2>(location, count, transpose, value);
751}
752
753void ProgramMtl::setUniformMatrix2x4fv(GLint location,
754                                       GLsizei count,
755                                       GLboolean transpose,
756                                       const GLfloat *value)
757{
758    setUniformMatrixfv<2, 4>(location, count, transpose, value);
759}
760
761void ProgramMtl::setUniformMatrix4x2fv(GLint location,
762                                       GLsizei count,
763                                       GLboolean transpose,
764                                       const GLfloat *value)
765{
766    setUniformMatrixfv<4, 2>(location, count, transpose, value);
767}
768
769void ProgramMtl::setUniformMatrix3x4fv(GLint location,
770                                       GLsizei count,
771                                       GLboolean transpose,
772                                       const GLfloat *value)
773{
774    setUniformMatrixfv<3, 4>(location, count, transpose, value);
775}
776
777void ProgramMtl::setUniformMatrix4x3fv(GLint location,
778                                       GLsizei count,
779                                       GLboolean transpose,
780                                       const GLfloat *value)
781{
782    setUniformMatrixfv<4, 3>(location, count, transpose, value);
783}
784
785void ProgramMtl::getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const
786{
787    getUniformImpl(location, params, GL_FLOAT);
788}
789
790void ProgramMtl::getUniformiv(const gl::Context *context, GLint location, GLint *params) const
791{
792    getUniformImpl(location, params, GL_INT);
793}
794
795void ProgramMtl::getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const
796{
797    getUniformImpl(location, params, GL_UNSIGNED_INT);
798}
799
800angle::Result ProgramMtl::setupDraw(const gl::Context *glContext,
801                                    mtl::RenderCommandEncoder *cmdEncoder,
802                                    const Optional<mtl::RenderPipelineDesc> &changedPipelineDescOpt,
803                                    bool forceTexturesSetting)
804{
805    ContextMtl *context = mtl::GetImpl(glContext);
806    if (changedPipelineDescOpt.valid())
807    {
808        const auto &changedPipelineDesc = changedPipelineDescOpt.value();
809        // Render pipeline state needs to be changed
810        id<MTLRenderPipelineState> pipelineState =
811            mMetalRenderPipelineCache.getRenderPipelineState(context, changedPipelineDesc);
812        if (!pipelineState)
813        {
814            // Error already logged inside getRenderPipelineState()
815            return angle::Result::Stop;
816        }
817        cmdEncoder->setRenderPipelineState(pipelineState);
818
819        // We need to rebind uniform buffers & textures also
820        mDefaultUniformBlocksDirty.set();
821        mSamplerBindingsDirty.set();
822    }
823
824    ANGLE_TRY(commitUniforms(context, cmdEncoder));
825    ANGLE_TRY(updateTextures(glContext, cmdEncoder, forceTexturesSetting));
826
827    return angle::Result::Continue;
828}
829
830angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder)
831{
832    for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
833    {
834        if (!mDefaultUniformBlocksDirty[shaderType])
835        {
836            continue;
837        }
838        DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
839
840        if (!uniformBlock.uniformData.size())
841        {
842            continue;
843        }
844        switch (shaderType)
845        {
846            case gl::ShaderType::Vertex:
847                cmdEncoder->setVertexBytes(uniformBlock.uniformData.data(),
848                                           uniformBlock.uniformData.size(),
849                                           mtl::kDefaultUniformsBindingIndex);
850                break;
851            case gl::ShaderType::Fragment:
852                cmdEncoder->setFragmentBytes(uniformBlock.uniformData.data(),
853                                             uniformBlock.uniformData.size(),
854                                             mtl::kDefaultUniformsBindingIndex);
855                break;
856            default:
857                UNREACHABLE();
858        }
859
860        mDefaultUniformBlocksDirty.reset(shaderType);
861    }
862
863    return angle::Result::Continue;
864}
865
866angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
867                                         mtl::RenderCommandEncoder *cmdEncoder,
868                                         bool forceUpdate)
869{
870    const auto &glState = glContext->getState();
871
872    const gl::ActiveTexturesCache &completeTextures = glState.getActiveTexturesCache();
873
874    for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
875    {
876        if (!mSamplerBindingsDirty[shaderType] && !forceUpdate)
877        {
878            continue;
879        }
880
881        for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
882             ++textureIndex)
883        {
884            const gl::SamplerBinding &samplerBinding = mState.getSamplerBindings()[textureIndex];
885
886            ASSERT(!samplerBinding.unreferenced);
887
888            for (uint32_t arrayElement = 0; arrayElement < samplerBinding.boundTextureUnits.size();
889                 ++arrayElement)
890            {
891                GLuint textureUnit    = samplerBinding.boundTextureUnits[arrayElement];
892                gl::Texture *texture  = completeTextures[textureUnit];
893                auto destBindingPoint = textureIndex + arrayElement;
894                if (!texture)
895                {
896                    BindNullSampler(glContext, cmdEncoder, shaderType, samplerBinding.textureType,
897                                    destBindingPoint);
898
899                    continue;
900                }
901
902                TextureMtl *textureMtl = mtl::GetImpl(texture);
903
904                switch (shaderType)
905                {
906                    case gl::ShaderType::Vertex:
907                        ANGLE_TRY(textureMtl->bindVertexShader(glContext, cmdEncoder,
908                                                               destBindingPoint, destBindingPoint));
909                        break;
910                    case gl::ShaderType::Fragment:
911                        ANGLE_TRY(textureMtl->bindFragmentShader(
912                            glContext, cmdEncoder, destBindingPoint, destBindingPoint));
913                        break;
914                    default:
915                        UNREACHABLE();
916                }
917            }  // for array elements
918        }      // for sampler bindings
919    }          // for shader types
920
921    return angle::Result::Continue;
922}
923
924}  // namespace rx
925