Edit

kc3-lang/angle/src/libANGLE/Shader.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2021-09-02 13:47:34
    Hash : b6e99bb3
    Message : Rename and expand shader variable init feature. This feature now forces both output and unitialized locals to be initialized by the shader translator. This feature is needed by the trace validator to ensure we get deterministic behaviour in traces that exhibit some undefined results. Bug: angleproject:5133 Change-Id: Id1242cd077a57e891eed217f7671976ce1631a58 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3140216 Reviewed-by: Cody Northrop <cnorthrop@google.com> Reviewed-by: Tim Van Patten <timvp@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/Shader.cpp
  • //
    // Copyright 2002 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // Shader.cpp: Implements the gl::Shader class and its  derived classes
    // VertexShader and FragmentShader. Implements GL shader objects and related
    // functionality. [OpenGL ES 2.0.24] section 2.10 page 24 and section 3.8 page 84.
    
    #include "libANGLE/Shader.h"
    
    #include <functional>
    #include <sstream>
    
    #include "GLSLANG/ShaderLang.h"
    #include "common/utilities.h"
    #include "libANGLE/Caps.h"
    #include "libANGLE/Compiler.h"
    #include "libANGLE/Constants.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/ResourceManager.h"
    #include "libANGLE/renderer/GLImplFactory.h"
    #include "libANGLE/renderer/ShaderImpl.h"
    #include "platform/FrontendFeatures.h"
    
    namespace gl
    {
    
    namespace
    {
    template <typename VarT>
    std::vector<VarT> GetActiveShaderVariables(const std::vector<VarT> *variableList)
    {
        ASSERT(variableList);
        std::vector<VarT> result;
        for (size_t varIndex = 0; varIndex < variableList->size(); varIndex++)
        {
            const VarT &var = variableList->at(varIndex);
            if (var.active)
            {
                result.push_back(var);
            }
        }
        return result;
    }
    
    template <typename VarT>
    const std::vector<VarT> &GetShaderVariables(const std::vector<VarT> *variableList)
    {
        ASSERT(variableList);
        return *variableList;
    }
    
    }  // anonymous namespace
    
    // true if varying x has a higher priority in packing than y
    bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y)
    {
        if (x.type == y.type)
        {
            return x.getArraySizeProduct() > y.getArraySizeProduct();
        }
    
        // Special case for handling structs: we sort these to the end of the list
        if (x.type == GL_NONE)
        {
            return false;
        }
    
        if (y.type == GL_NONE)
        {
            return true;
        }
    
        return gl::VariableSortOrder(x.type) < gl::VariableSortOrder(y.type);
    }
    
    const char *GetShaderTypeString(ShaderType type)
    {
        switch (type)
        {
            case ShaderType::Vertex:
                return "VERTEX";
    
            case ShaderType::Fragment:
                return "FRAGMENT";
    
            case ShaderType::Compute:
                return "COMPUTE";
    
            case ShaderType::Geometry:
                return "GEOMETRY";
    
            case ShaderType::TessControl:
                return "TESS_CONTROL";
    
            case ShaderType::TessEvaluation:
                return "TESS_EVALUATION";
    
            default:
                UNREACHABLE();
                return "";
        }
    }
    
    class ScopedExit final : angle::NonCopyable
    {
      public:
        ScopedExit(std::function<void()> exit) : mExit(exit) {}
        ~ScopedExit() { mExit(); }
    
      private:
        std::function<void()> mExit;
    };
    
    struct Shader::CompilingState
    {
        std::shared_ptr<rx::WaitableCompileEvent> compileEvent;
        ShCompilerInstance shCompilerInstance;
    };
    
    ShaderState::ShaderState(ShaderType shaderType)
        : mLabel(),
          mShaderType(shaderType),
          mShaderVersion(100),
          mNumViews(-1),
          mGeometryShaderInvocations(1),
          mCompileStatus(CompileStatus::NOT_COMPILED)
    {
        mLocalSize.fill(-1);
    }
    
    ShaderState::~ShaderState() {}
    
    Shader::Shader(ShaderProgramManager *manager,
                   rx::GLImplFactory *implFactory,
                   const gl::Limitations &rendererLimitations,
                   ShaderType type,
                   ShaderProgramID handle)
        : mState(type),
          mImplementation(implFactory->createShader(mState)),
          mRendererLimitations(rendererLimitations),
          mHandle(handle),
          mType(type),
          mRefCount(0),
          mDeleteStatus(false),
          mResourceManager(manager),
          mCurrentMaxComputeWorkGroupInvocations(0u)
    {
        ASSERT(mImplementation);
    }
    
    void Shader::onDestroy(const gl::Context *context)
    {
        resolveCompile();
        mImplementation->destroy();
        mBoundCompiler.set(context, nullptr);
        mImplementation.reset(nullptr);
        delete this;
    }
    
    Shader::~Shader()
    {
        ASSERT(!mImplementation);
    }
    
    void Shader::setLabel(const Context *context, const std::string &label)
    {
        mState.mLabel = label;
    }
    
    const std::string &Shader::getLabel() const
    {
        return mState.mLabel;
    }
    
    ShaderProgramID Shader::getHandle() const
    {
        return mHandle;
    }
    
    void Shader::setSource(GLsizei count, const char *const *string, const GLint *length)
    {
        std::ostringstream stream;
    
        for (int i = 0; i < count; i++)
        {
            if (length == nullptr || length[i] < 0)
            {
                stream.write(string[i], strlen(string[i]));
            }
            else
            {
                stream.write(string[i], length[i]);
            }
        }
    
        mState.mSource = stream.str();
    }
    
    int Shader::getInfoLogLength()
    {
        resolveCompile();
        if (mInfoLog.empty())
        {
            return 0;
        }
    
        return (static_cast<int>(mInfoLog.length()) + 1);
    }
    
    void Shader::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog)
    {
        resolveCompile();
    
        int index = 0;
    
        if (bufSize > 0)
        {
            index = std::min(bufSize - 1, static_cast<GLsizei>(mInfoLog.length()));
            memcpy(infoLog, mInfoLog.c_str(), index);
    
            infoLog[index] = '\0';
        }
    
        if (length)
        {
            *length = index;
        }
    }
    
    int Shader::getSourceLength() const
    {
        return mState.mSource.empty() ? 0 : (static_cast<int>(mState.mSource.length()) + 1);
    }
    
    int Shader::getTranslatedSourceLength()
    {
        resolveCompile();
    
        if (mState.mTranslatedSource.empty())
        {
            return 0;
        }
    
        return (static_cast<int>(mState.mTranslatedSource.length()) + 1);
    }
    
    int Shader::getTranslatedSourceWithDebugInfoLength()
    {
        resolveCompile();
    
        const std::string &debugInfo = mImplementation->getDebugInfo();
        if (debugInfo.empty())
        {
            return 0;
        }
    
        return (static_cast<int>(debugInfo.length()) + 1);
    }
    
    // static
    void Shader::GetSourceImpl(const std::string &source,
                               GLsizei bufSize,
                               GLsizei *length,
                               char *buffer)
    {
        int index = 0;
    
        if (bufSize > 0)
        {
            index = std::min(bufSize - 1, static_cast<GLsizei>(source.length()));
            memcpy(buffer, source.c_str(), index);
    
            buffer[index] = '\0';
        }
    
        if (length)
        {
            *length = index;
        }
    }
    
    void Shader::getSource(GLsizei bufSize, GLsizei *length, char *buffer) const
    {
        GetSourceImpl(mState.mSource, bufSize, length, buffer);
    }
    
    void Shader::getTranslatedSource(GLsizei bufSize, GLsizei *length, char *buffer)
    {
        GetSourceImpl(getTranslatedSource(), bufSize, length, buffer);
    }
    
    const std::string &Shader::getTranslatedSource()
    {
        resolveCompile();
        return mState.mTranslatedSource;
    }
    
    const sh::BinaryBlob &Shader::getCompiledBinary()
    {
        resolveCompile();
        return mState.mCompiledBinary;
    }
    
    void Shader::getTranslatedSourceWithDebugInfo(GLsizei bufSize, GLsizei *length, char *buffer)
    {
        resolveCompile();
        const std::string &debugInfo = mImplementation->getDebugInfo();
        GetSourceImpl(debugInfo, bufSize, length, buffer);
    }
    
    void Shader::compile(const Context *context)
    {
        resolveCompile();
    
        mState.mTranslatedSource.clear();
        mState.mCompiledBinary.clear();
        mInfoLog.clear();
        mState.mShaderVersion = 100;
        mState.mInputVaryings.clear();
        mState.mOutputVaryings.clear();
        mState.mUniforms.clear();
        mState.mUniformBlocks.clear();
        mState.mShaderStorageBlocks.clear();
        mState.mActiveAttributes.clear();
        mState.mActiveOutputVariables.clear();
        mState.mNumViews = -1;
        mState.mGeometryShaderInputPrimitiveType.reset();
        mState.mGeometryShaderOutputPrimitiveType.reset();
        mState.mGeometryShaderMaxVertices.reset();
        mState.mGeometryShaderInvocations      = 1;
        mState.mTessControlShaderVertices      = 0;
        mState.mTessGenMode                    = 0;
        mState.mTessGenSpacing                 = 0;
        mState.mTessGenVertexOrder             = 0;
        mState.mTessGenPointMode               = 0;
        mState.mEarlyFragmentTestsOptimization = false;
        mState.mSpecConstUsageBits.reset();
    
        mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
        mBoundCompiler.set(context, context->getCompiler());
    
        ShCompileOptions options = (SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID |
                                    SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE);
    
        // Add default options to WebGL shaders to prevent unexpected behavior during
        // compilation.
        if (context->getExtensions().webglCompatibility)
        {
            options |= SH_INIT_GL_POSITION;
            options |= SH_LIMIT_CALL_STACK_DEPTH;
            options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
            options |= SH_ENFORCE_PACKING_RESTRICTIONS;
            options |= SH_INIT_SHARED_VARIABLES;
        }
    
        // Some targets (e.g. D3D11 Feature Level 9_3 and below) do not support non-constant loop
        // indexes in fragment shaders. Shader compilation will fail. To provide a better error
        // message we can instruct the compiler to pre-validate.
        if (mRendererLimitations.shadersRequireIndexedLoopValidation)
        {
            options |= SH_VALIDATE_LOOP_INDEXING;
        }
    
        if (context->getFrontendFeatures().scalarizeVecAndMatConstructorArgs.enabled)
        {
            options |= SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS;
        }
    
        if (context->getFrontendFeatures().forceInitShaderVariables.enabled)
        {
            options |= SH_INIT_OUTPUT_VARIABLES;
            options |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
        }
    
        mCurrentMaxComputeWorkGroupInvocations =
            static_cast<GLuint>(context->getCaps().maxComputeWorkGroupInvocations);
    
        mMaxComputeSharedMemory = context->getCaps().maxComputeSharedMemorySize;
    
        ASSERT(mBoundCompiler.get());
        ShCompilerInstance compilerInstance = mBoundCompiler->getInstance(mState.mShaderType);
        ShHandle compilerHandle             = compilerInstance.getHandle();
        ASSERT(compilerHandle);
        mCompilerResourcesString = compilerInstance.getBuiltinResourcesString();
    
        mCompilingState.reset(new CompilingState());
        mCompilingState->shCompilerInstance = std::move(compilerInstance);
        mCompilingState->compileEvent =
            mImplementation->compile(context, &(mCompilingState->shCompilerInstance), options);
    }
    
    void Shader::resolveCompile()
    {
        if (!mState.compilePending())
        {
            return;
        }
    
        ASSERT(mCompilingState.get());
    
        mCompilingState->compileEvent->wait();
    
        mInfoLog += mCompilingState->compileEvent->getInfoLog();
    
        ScopedExit exit([this]() {
            mBoundCompiler->putInstance(std::move(mCompilingState->shCompilerInstance));
            mCompilingState->compileEvent.reset();
            mCompilingState.reset();
        });
    
        ShHandle compilerHandle = mCompilingState->shCompilerInstance.getHandle();
        if (!mCompilingState->compileEvent->getResult())
        {
            mInfoLog += sh::GetInfoLog(compilerHandle);
            INFO() << std::endl << mInfoLog;
            mState.mCompileStatus = CompileStatus::NOT_COMPILED;
            return;
        }
    
        const ShShaderOutput outputType = mCompilingState->shCompilerInstance.getShaderOutputType();
        const bool isBinaryOutput =
            outputType == SH_SPIRV_VULKAN_OUTPUT || outputType == SH_SPIRV_METAL_OUTPUT;
    
        if (isBinaryOutput)
        {
            mState.mCompiledBinary = sh::GetObjectBinaryBlob(compilerHandle);
        }
        else
        {
            mState.mTranslatedSource = sh::GetObjectCode(compilerHandle);
    
    #if !defined(NDEBUG)
            // Prefix translated shader with commented out un-translated shader.
            // Useful in diagnostics tools which capture the shader source.
            std::ostringstream shaderStream;
            shaderStream << "// GLSL\n";
            shaderStream << "//\n";
    
            std::istringstream inputSourceStream(mState.mSource);
            std::string line;
            while (std::getline(inputSourceStream, line))
            {
                // Remove null characters from the source line
                line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
    
                shaderStream << "// " << line;
    
                // glslang complains if a comment ends with backslash
                if (!line.empty() && line.back() == '\\')
                {
                    shaderStream << "\\";
                }
    
                shaderStream << std::endl;
            }
            shaderStream << "\n\n";
            shaderStream << mState.mTranslatedSource;
            mState.mTranslatedSource = shaderStream.str();
    #endif  // !defined(NDEBUG)
        }
    
        // Gather the shader information
        mState.mShaderVersion = sh::GetShaderVersion(compilerHandle);
    
        mState.mUniforms            = GetShaderVariables(sh::GetUniforms(compilerHandle));
        mState.mUniformBlocks       = GetShaderVariables(sh::GetUniformBlocks(compilerHandle));
        mState.mShaderStorageBlocks = GetShaderVariables(sh::GetShaderStorageBlocks(compilerHandle));
        mState.mSpecConstUsageBits =
            rx::SpecConstUsageBits(sh::GetShaderSpecConstUsageBits(compilerHandle));
    
        switch (mState.mShaderType)
        {
            case ShaderType::Compute:
            {
                mState.mAllAttributes    = GetShaderVariables(sh::GetAttributes(compilerHandle));
                mState.mActiveAttributes = GetActiveShaderVariables(&mState.mAllAttributes);
                mState.mLocalSize        = sh::GetComputeShaderLocalGroupSize(compilerHandle);
                if (mState.mLocalSize.isDeclared())
                {
                    angle::CheckedNumeric<uint32_t> checked_local_size_product(mState.mLocalSize[0]);
                    checked_local_size_product *= mState.mLocalSize[1];
                    checked_local_size_product *= mState.mLocalSize[2];
    
                    if (!checked_local_size_product.IsValid())
                    {
                        WARN() << std::endl
                               << "Integer overflow when computing the product of local_size_x, "
                               << "local_size_y and local_size_z.";
                        mState.mCompileStatus = CompileStatus::NOT_COMPILED;
                        return;
                    }
                    if (checked_local_size_product.ValueOrDie() >
                        mCurrentMaxComputeWorkGroupInvocations)
                    {
                        WARN() << std::endl
                               << "The total number of invocations within a work group exceeds "
                               << "MAX_COMPUTE_WORK_GROUP_INVOCATIONS.";
                        mState.mCompileStatus = CompileStatus::NOT_COMPILED;
                        return;
                    }
                }
    
                unsigned int sharedMemSize = sh::GetShaderSharedMemorySize(compilerHandle);
                if (sharedMemSize > mMaxComputeSharedMemory)
                {
                    WARN() << std::endl << "Exceeded maximum shared memory size";
                    mState.mCompileStatus = CompileStatus::NOT_COMPILED;
                    return;
                }
                break;
            }
            case ShaderType::Vertex:
            {
                mState.mOutputVaryings   = GetShaderVariables(sh::GetOutputVaryings(compilerHandle));
                mState.mAllAttributes    = GetShaderVariables(sh::GetAttributes(compilerHandle));
                mState.mActiveAttributes = GetActiveShaderVariables(&mState.mAllAttributes);
                mState.mNumViews         = sh::GetVertexShaderNumViews(compilerHandle);
                break;
            }
            case ShaderType::Fragment:
            {
                mState.mAllAttributes    = GetShaderVariables(sh::GetAttributes(compilerHandle));
                mState.mActiveAttributes = GetActiveShaderVariables(&mState.mAllAttributes);
                mState.mInputVaryings    = GetShaderVariables(sh::GetInputVaryings(compilerHandle));
                // TODO(jmadill): Figure out why we only sort in the FS, and if we need to.
                std::sort(mState.mInputVaryings.begin(), mState.mInputVaryings.end(), CompareShaderVar);
                mState.mActiveOutputVariables =
                    GetActiveShaderVariables(sh::GetOutputVariables(compilerHandle));
                mState.mEarlyFragmentTestsOptimization =
                    sh::HasEarlyFragmentTestsOptimization(compilerHandle);
                break;
            }
            case ShaderType::Geometry:
            {
                mState.mInputVaryings  = GetShaderVariables(sh::GetInputVaryings(compilerHandle));
                mState.mOutputVaryings = GetShaderVariables(sh::GetOutputVaryings(compilerHandle));
    
                if (sh::HasValidGeometryShaderInputPrimitiveType(compilerHandle))
                {
                    mState.mGeometryShaderInputPrimitiveType = FromGLenum<PrimitiveMode>(
                        sh::GetGeometryShaderInputPrimitiveType(compilerHandle));
                }
                if (sh::HasValidGeometryShaderOutputPrimitiveType(compilerHandle))
                {
                    mState.mGeometryShaderOutputPrimitiveType = FromGLenum<PrimitiveMode>(
                        sh::GetGeometryShaderOutputPrimitiveType(compilerHandle));
                }
                if (sh::HasValidGeometryShaderMaxVertices(compilerHandle))
                {
                    mState.mGeometryShaderMaxVertices =
                        sh::GetGeometryShaderMaxVertices(compilerHandle);
                }
                mState.mGeometryShaderInvocations = sh::GetGeometryShaderInvocations(compilerHandle);
                break;
            }
            case ShaderType::TessControl:
            {
                mState.mInputVaryings  = GetShaderVariables(sh::GetInputVaryings(compilerHandle));
                mState.mOutputVaryings = GetShaderVariables(sh::GetOutputVaryings(compilerHandle));
                mState.mTessControlShaderVertices = sh::GetTessControlShaderVertices(compilerHandle);
                break;
            }
            case ShaderType::TessEvaluation:
            {
                mState.mInputVaryings  = GetShaderVariables(sh::GetInputVaryings(compilerHandle));
                mState.mOutputVaryings = GetShaderVariables(sh::GetOutputVaryings(compilerHandle));
                if (sh::HasValidTessGenMode(compilerHandle))
                {
                    mState.mTessGenMode = sh::GetTessGenMode(compilerHandle);
                }
                if (sh::HasValidTessGenSpacing(compilerHandle))
                {
                    mState.mTessGenSpacing = sh::GetTessGenSpacing(compilerHandle);
                }
                if (sh::HasValidTessGenVertexOrder(compilerHandle))
                {
                    mState.mTessGenVertexOrder = sh::GetTessGenVertexOrder(compilerHandle);
                }
                if (sh::HasValidTessGenPointMode(compilerHandle))
                {
                    mState.mTessGenPointMode = sh::GetTessGenPointMode(compilerHandle);
                }
                break;
            }
    
            default:
                UNREACHABLE();
        }
    
        ASSERT(!mState.mTranslatedSource.empty() || !mState.mCompiledBinary.empty());
    
        bool success          = mCompilingState->compileEvent->postTranslate(&mInfoLog);
        mState.mCompileStatus = success ? CompileStatus::COMPILED : CompileStatus::NOT_COMPILED;
    }
    
    void Shader::addRef()
    {
        mRefCount++;
    }
    
    void Shader::release(const Context *context)
    {
        mRefCount--;
    
        if (mRefCount == 0 && mDeleteStatus)
        {
            mResourceManager->deleteShader(context, mHandle);
        }
    }
    
    unsigned int Shader::getRefCount() const
    {
        return mRefCount;
    }
    
    bool Shader::isFlaggedForDeletion() const
    {
        return mDeleteStatus;
    }
    
    void Shader::flagForDeletion()
    {
        mDeleteStatus = true;
    }
    
    bool Shader::isCompiled()
    {
        resolveCompile();
        return mState.mCompileStatus == CompileStatus::COMPILED;
    }
    
    bool Shader::isCompleted()
    {
        return (!mState.compilePending() || mCompilingState->compileEvent->isReady());
    }
    
    int Shader::getShaderVersion()
    {
        resolveCompile();
        return mState.mShaderVersion;
    }
    
    const std::vector<sh::ShaderVariable> &Shader::getInputVaryings()
    {
        resolveCompile();
        return mState.getInputVaryings();
    }
    
    const std::vector<sh::ShaderVariable> &Shader::getOutputVaryings()
    {
        resolveCompile();
        return mState.getOutputVaryings();
    }
    
    const std::vector<sh::ShaderVariable> &Shader::getUniforms()
    {
        resolveCompile();
        return mState.getUniforms();
    }
    
    const std::vector<sh::InterfaceBlock> &Shader::getUniformBlocks()
    {
        resolveCompile();
        return mState.getUniformBlocks();
    }
    
    const std::vector<sh::InterfaceBlock> &Shader::getShaderStorageBlocks()
    {
        resolveCompile();
        return mState.getShaderStorageBlocks();
    }
    
    const std::vector<sh::ShaderVariable> &Shader::getActiveAttributes()
    {
        resolveCompile();
        return mState.getActiveAttributes();
    }
    
    const std::vector<sh::ShaderVariable> &Shader::getAllAttributes()
    {
        resolveCompile();
        return mState.getAllAttributes();
    }
    
    const std::vector<sh::ShaderVariable> &Shader::getActiveOutputVariables()
    {
        resolveCompile();
        return mState.getActiveOutputVariables();
    }
    
    std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfVaryingName)
    {
        ASSERT(mState.getShaderType() != ShaderType::Fragment &&
               mState.getShaderType() != ShaderType::Compute);
        const auto &varyings = getOutputVaryings();
        auto bracketPos      = tfVaryingName.find("[");
        if (bracketPos != std::string::npos)
        {
            auto tfVaryingBaseName = tfVaryingName.substr(0, bracketPos);
            for (const auto &varying : varyings)
            {
                if (varying.name == tfVaryingBaseName)
                {
                    std::string mappedNameWithArrayIndex =
                        varying.mappedName + tfVaryingName.substr(bracketPos);
                    return mappedNameWithArrayIndex;
                }
            }
        }
        else
        {
            for (const auto &varying : varyings)
            {
                if (varying.name == tfVaryingName)
                {
                    return varying.mappedName;
                }
                else if (varying.isStruct())
                {
                    GLuint fieldIndex = 0;
                    const auto *field = varying.findField(tfVaryingName, &fieldIndex);
                    if (field == nullptr)
                    {
                        continue;
                    }
                    ASSERT(field != nullptr && !field->isStruct() &&
                           (!field->isArray() || varying.isShaderIOBlock));
                    std::string mappedName;
                    // If it's an I/O block without an instance name, don't include the block name.
                    if (!varying.isShaderIOBlock || !varying.name.empty())
                    {
                        mappedName = varying.isShaderIOBlock ? varying.mappedStructOrBlockName
                                                             : varying.mappedName;
                        mappedName += '.';
                    }
                    return mappedName + field->mappedName;
                }
            }
        }
        UNREACHABLE();
        return std::string();
    }
    
    const sh::WorkGroupSize &Shader::getWorkGroupSize()
    {
        resolveCompile();
        return mState.mLocalSize;
    }
    
    int Shader::getNumViews()
    {
        resolveCompile();
        return mState.mNumViews;
    }
    
    Optional<PrimitiveMode> Shader::getGeometryShaderInputPrimitiveType()
    {
        resolveCompile();
        return mState.mGeometryShaderInputPrimitiveType;
    }
    
    Optional<PrimitiveMode> Shader::getGeometryShaderOutputPrimitiveType()
    {
        resolveCompile();
        return mState.mGeometryShaderOutputPrimitiveType;
    }
    
    int Shader::getGeometryShaderInvocations()
    {
        resolveCompile();
        return mState.mGeometryShaderInvocations;
    }
    
    Optional<GLint> Shader::getGeometryShaderMaxVertices()
    {
        resolveCompile();
        return mState.mGeometryShaderMaxVertices;
    }
    
    int Shader::getTessControlShaderVertices()
    {
        resolveCompile();
        return mState.mTessControlShaderVertices;
    }
    
    GLenum Shader::getTessGenMode()
    {
        resolveCompile();
        return mState.mTessGenMode;
    }
    
    GLenum Shader::getTessGenSpacing()
    {
        resolveCompile();
        return mState.mTessGenSpacing;
    }
    
    GLenum Shader::getTessGenVertexOrder()
    {
        resolveCompile();
        return mState.mTessGenVertexOrder;
    }
    
    GLenum Shader::getTessGenPointMode()
    {
        resolveCompile();
        return mState.mTessGenPointMode;
    }
    
    const std::string &Shader::getCompilerResourcesString() const
    {
        return mCompilerResourcesString;
    }
    
    }  // namespace gl