Edit

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

Branch :

  • Show log

    Commit

  • Author : jchen10
    Date : 2018-12-29 16:39:55
    Hash : a100d8f4
    Message : ParallelCompile: add GL backend support For GL backend, at first each worker thread must have a naitve context for its own to work in. These worker contexts have to be shared from the main context, so that all shader and program objects are seen in any context. This extends backend displays to create and destroy the worker contexts. RendererGL manages and allocates them to the worker threads. ShaderImpl has a new compile method added to do the actual glCompile work in worker thread. The ProgramGL's link method is broken down by introducing the LinkEventGL class. Bug: chromium:849576 Change-Id: Idc2c51b4b6c978781ae77810e62c480acc67ebb5 Reviewed-on: https://chromium-review.googlesource.com/c/1373015 Commit-Queue: Jie A Chen <jie.a.chen@intel.com> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/libANGLE/Shader.cpp
  • //
    // Copyright (c) 2002-2014 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/WorkerThread.h"
    #include "libANGLE/renderer/GLImplFactory.h"
    #include "libANGLE/renderer/ShaderImpl.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";
    
            default:
                UNREACHABLE();
                return "";
        }
    }
    
    class ScopedExit final : angle::NonCopyable
    {
      public:
        ScopedExit(std::function<void()> exit) : mExit(exit) {}
        ~ScopedExit() { mExit(); }
    
      private:
        std::function<void()> mExit;
    };
    
    using CompileImplFunctor = std::function<void(const std::string &)>;
    class CompileTask : public angle::Closure
    {
      public:
        CompileTask(ShHandle handle,
                    std::string &&sourcePath,
                    std::string &&source,
                    ShCompileOptions options,
                    CompileImplFunctor &&functor)
            : mHandle(handle),
              mSourcePath(sourcePath),
              mSource(source),
              mOptions(options),
              mCompileImplFunctor(functor),
              mResult(false)
        {}
        void operator()() override
        {
            std::vector<const char *> srcStrings;
            if (!mSourcePath.empty())
            {
                srcStrings.push_back(mSourcePath.c_str());
            }
            srcStrings.push_back(mSource.c_str());
            mResult = sh::Compile(mHandle, &srcStrings[0], srcStrings.size(), mOptions);
            if (mResult)
            {
                mCompileImplFunctor(sh::GetObjectCode(mHandle));
            }
        }
        bool getResult() { return mResult; }
    
      private:
        ShHandle mHandle;
        std::string mSourcePath;
        std::string mSource;
        ShCompileOptions mOptions;
        CompileImplFunctor mCompileImplFunctor;
        bool mResult;
    };
    
    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,
                   GLuint 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;
    }
    
    GLuint 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;
    }
    
    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();
        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.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
        mBoundCompiler.set(context, context->getCompiler());
    
        // Cache the compile source and options for compilation. Must be done now, since the source
        // can change before the link call or another call that resolves the compile.
    
        std::stringstream sourceStream;
        std::string sourcePath;
        ShCompileOptions options =
            mImplementation->prepareSourceAndReturnOptions(context, &sourceStream, &sourcePath);
        options |= (SH_OBJECT_CODE | SH_VARIABLES | SH_EMULATE_GL_DRAW_ID);
        auto source = sourceStream.str();
    
        // 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 (eg 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;
        }
    
        mCurrentMaxComputeWorkGroupInvocations = context->getCaps().maxComputeWorkGroupInvocations;
    
        ASSERT(mBoundCompiler.get());
        mShCompilerInstance     = mBoundCompiler->getInstance(mState.mShaderType);
        ShHandle compilerHandle = mShCompilerInstance.getHandle();
        ASSERT(compilerHandle);
        mCompilerResourcesString = mShCompilerInstance.getBuiltinResourcesString();
    
        mWorkerPool   = context->getWorkerThreadPool();
        std::function<void(const std::string &)> compileImplFunctor;
        if (mWorkerPool->isAsync())
        {
            compileImplFunctor = [this](const std::string &source) {
                mImplementation->compileAsync(source);
            };
        }
        else
        {
            compileImplFunctor = [](const std::string &source) {};
        }
        mCompileTask =
            std::make_shared<CompileTask>(compilerHandle, std::move(sourcePath), std::move(source),
                                          options, std::move(compileImplFunctor));
        mCompileEvent = mWorkerPool->postWorkerTask(mCompileTask);
    }
    
    void Shader::resolveCompile()
    {
        if (!mState.compilePending())
        {
            return;
        }
    
        ASSERT(mCompileEvent.get());
        ASSERT(mCompileTask.get());
    
        mCompileEvent->wait();
    
        mCompileEvent.reset();
        mWorkerPool.reset();
    
        bool compiled = mCompileTask->getResult();
        mCompileTask.reset();
    
        ScopedExit exit([this]() { mBoundCompiler->putInstance(std::move(mShCompilerInstance)); });
    
        ShHandle compilerHandle = mShCompilerInstance.getHandle();
        if (!compiled)
        {
            mInfoLog = sh::GetInfoLog(compilerHandle);
            WARN() << std::endl << mInfoLog;
            mState.mCompileStatus = CompileStatus::NOT_COMPILED;
            return;
        }
    
        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 << 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));
    
        switch (mState.mShaderType)
        {
            case ShaderType::Compute:
            {
                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;
                    }
                }
                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.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));
                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;
            }
            default:
                UNREACHABLE();
        }
    
        ASSERT(!mState.mTranslatedSource.empty());
    
        bool success          = mImplementation->postTranslateCompile(&mShCompilerInstance, &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() || mCompileEvent->isReady());
    }
    
    int Shader::getShaderVersion()
    {
        resolveCompile();
        return mState.mShaderVersion;
    }
    
    const std::vector<sh::Varying> &Shader::getInputVaryings()
    {
        resolveCompile();
        return mState.getInputVaryings();
    }
    
    const std::vector<sh::Varying> &Shader::getOutputVaryings()
    {
        resolveCompile();
        return mState.getOutputVaryings();
    }
    
    const std::vector<sh::Uniform> &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::Attribute> &Shader::getActiveAttributes()
    {
        resolveCompile();
        return mState.getActiveAttributes();
    }
    
    const std::vector<sh::Attribute> &Shader::getAllAttributes()
    {
        resolveCompile();
        return mState.getAllAttributes();
    }
    
    const std::vector<sh::OutputVariable> &Shader::getActiveOutputVariables()
    {
        resolveCompile();
        return mState.getActiveOutputVariables();
    }
    
    std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfVaryingName)
    {
        // TODO(jiawei.shao@intel.com): support transform feedback on geometry shader.
        ASSERT(mState.getShaderType() == ShaderType::Vertex);
        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())
                {
                    const auto *field = FindShaderVarField(varying, tfVaryingName);
                    ASSERT(field != nullptr && !field->isStruct() && !field->isArray());
                    return varying.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;
    }
    
    const std::string &Shader::getCompilerResourcesString() const
    {
        return mCompilerResourcesString;
    }
    
    }  // namespace gl