Edit

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

Branch :

  • Show log

    Commit

  • Author : Mohan Maiya
    Date : 2021-02-17 08:07:45
    Hash : 907a3cee
    Message : Vulkan: Add support for EXT_shader_framebuffer_fetch_non_coherent EXT_shader_framebuffer_fetch_non_coherent is implemented using subpass input attachments. The extension will be enabled in a follow up change that adds required changes to the Vulkan translator. Bug: angleproject:5454 Test: FramebufferFetchNonCoherentES31.*Vulkan Change-Id: Ic73c66a476c4a21db5269431166a198841f1dc0c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2598059 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/ProgramPipeline.cpp
  • //
    // Copyright 2017 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.
    //
    
    // ProgramPipeline.cpp: Implements the gl::ProgramPipeline class.
    // Implements GL program pipeline objects and related functionality.
    // [OpenGL ES 3.1] section 7.4 page 105.
    
    #include "libANGLE/ProgramPipeline.h"
    
    #include <algorithm>
    
    #include "libANGLE/Context.h"
    #include "libANGLE/Program.h"
    #include "libANGLE/angletypes.h"
    #include "libANGLE/renderer/GLImplFactory.h"
    #include "libANGLE/renderer/ProgramPipelineImpl.h"
    
    namespace gl
    {
    
    enum SubjectIndexes : angle::SubjectIndex
    {
        kExecutableSubjectIndex = 0
    };
    
    ProgramPipelineState::ProgramPipelineState()
        : mLabel(),
          mActiveShaderProgram(nullptr),
          mValid(false),
          mExecutable(new ProgramExecutable()),
          mIsLinked(false)
    {
        for (const ShaderType shaderType : gl::AllShaderTypes())
        {
            mPrograms[shaderType] = nullptr;
        }
    }
    
    ProgramPipelineState::~ProgramPipelineState()
    {
        SafeDelete(mExecutable);
    }
    
    const std::string &ProgramPipelineState::getLabel() const
    {
        return mLabel;
    }
    
    void ProgramPipelineState::activeShaderProgram(Program *shaderProgram)
    {
        mActiveShaderProgram = shaderProgram;
    }
    
    void ProgramPipelineState::useProgramStage(const Context *context,
                                               const ShaderType shaderType,
                                               Program *shaderProgram,
                                               angle::ObserverBinding *programObserverBindings)
    {
        Program *oldProgram = mPrograms[shaderType];
        if (oldProgram)
        {
            oldProgram->release(context);
        }
    
        // If program refers to a program object with a valid shader attached for the indicated shader
        // stage, glUseProgramStages installs the executable code for that stage in the indicated
        // program pipeline object pipeline.
        if (shaderProgram && (shaderProgram->id().value != 0) &&
            shaderProgram->getExecutable().hasLinkedShaderStage(shaderType))
        {
            mPrograms[shaderType] = shaderProgram;
            shaderProgram->addRef();
        }
        else
        {
            // If program is zero, or refers to a program object with no valid shader executable for the
            // given stage, it is as if the pipeline object has no programmable stage configured for the
            // indicated shader stage.
            mPrograms[shaderType] = nullptr;
        }
    
        Program *program = mPrograms[shaderType];
        programObserverBindings->bind(program);
    }
    
    void ProgramPipelineState::useProgramStages(
        const Context *context,
        GLbitfield stages,
        Program *shaderProgram,
        std::vector<angle::ObserverBinding> *programObserverBindings)
    {
        for (size_t singleShaderBit : angle::BitSet16<16>(static_cast<uint16_t>(stages)))
        {
            // Cast back to a bit after the iterator returns an index.
            ShaderType shaderType = GetShaderTypeFromBitfield(angle::Bit<size_t>(singleShaderBit));
            if (shaderType == ShaderType::InvalidEnum)
            {
                break;
            }
            useProgramStage(context, shaderType, shaderProgram,
                            &programObserverBindings->at(static_cast<size_t>(shaderType)));
        }
    }
    
    bool ProgramPipelineState::usesShaderProgram(ShaderProgramID programId) const
    {
        for (const Program *program : mPrograms)
        {
            if (program && (program->id() == programId))
            {
                return true;
            }
        }
    
        return false;
    }
    
    void ProgramPipelineState::updateExecutableTextures()
    {
        for (const ShaderType shaderType : mExecutable->getLinkedShaderStages())
        {
            const Program *program = getShaderProgram(shaderType);
            ASSERT(program);
            mExecutable->setActiveTextureMask(program->getExecutable().getActiveSamplersMask());
            mExecutable->setActiveImagesMask(program->getExecutable().getActiveImagesMask());
            // Updates mActiveSamplerRefCounts, mActiveSamplerTypes, and mActiveSamplerFormats
            mExecutable->updateActiveSamplers(program->getState());
        }
    }
    
    rx::SpecConstUsageBits ProgramPipelineState::getSpecConstUsageBits() const
    {
        rx::SpecConstUsageBits specConstUsageBits;
        for (const ShaderType shaderType : mExecutable->getLinkedShaderStages())
        {
            const Program *program = getShaderProgram(shaderType);
            ASSERT(program);
            specConstUsageBits |= program->getState().getSpecConstUsageBits();
        }
        return specConstUsageBits;
    }
    
    ProgramPipeline::ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle)
        : RefCountObject(factory->generateSerial(), handle),
          mProgramPipelineImpl(factory->createProgramPipeline(mState)),
          mExecutableObserverBinding(this, kExecutableSubjectIndex)
    {
        ASSERT(mProgramPipelineImpl);
    
        for (const ShaderType shaderType : gl::AllShaderTypes())
        {
            mProgramObserverBindings.emplace_back(this, static_cast<angle::SubjectIndex>(shaderType));
        }
        mExecutableObserverBinding.bind(mState.mExecutable);
    }
    
    ProgramPipeline::~ProgramPipeline()
    {
        mProgramPipelineImpl.release();
    }
    
    void ProgramPipeline::onDestroy(const Context *context)
    {
        for (Program *program : mState.mPrograms)
        {
            if (program)
            {
                ASSERT(program->getRefCount());
                program->release(context);
            }
        }
    
        getImplementation()->destroy(context);
    }
    
    void ProgramPipeline::setLabel(const Context *context, const std::string &label)
    {
        mState.mLabel = label;
    }
    
    const std::string &ProgramPipeline::getLabel() const
    {
        return mState.mLabel;
    }
    
    rx::ProgramPipelineImpl *ProgramPipeline::getImplementation() const
    {
        return mProgramPipelineImpl.get();
    }
    
    void ProgramPipeline::activeShaderProgram(Program *shaderProgram)
    {
        mState.activeShaderProgram(shaderProgram);
    }
    
    void ProgramPipeline::useProgramStages(const Context *context,
                                           GLbitfield stages,
                                           Program *shaderProgram)
    {
        mState.useProgramStages(context, stages, shaderProgram, &mProgramObserverBindings);
        updateLinkedShaderStages();
        updateExecutable();
    
        mState.mIsLinked = false;
    }
    
    void ProgramPipeline::updateLinkedShaderStages()
    {
        mState.mExecutable->resetLinkedShaderStages();
    
        for (const ShaderType shaderType : gl::AllShaderTypes())
        {
            Program *program = mState.mPrograms[shaderType];
            if (program)
            {
                mState.mExecutable->setLinkedShaderStages(shaderType);
            }
        }
    
        mState.mExecutable->updateCanDrawWith();
    }
    
    void ProgramPipeline::updateExecutableAttributes()
    {
        Program *vertexProgram = getShaderProgram(gl::ShaderType::Vertex);
    
        if (!vertexProgram)
        {
            return;
        }
    
        const ProgramExecutable &vertexExecutable      = vertexProgram->getExecutable();
        mState.mExecutable->mActiveAttribLocationsMask = vertexExecutable.mActiveAttribLocationsMask;
        mState.mExecutable->mMaxActiveAttribLocation   = vertexExecutable.mMaxActiveAttribLocation;
        mState.mExecutable->mAttributesTypeMask        = vertexExecutable.mAttributesTypeMask;
        mState.mExecutable->mAttributesMask            = vertexExecutable.mAttributesMask;
    }
    
    void ProgramPipeline::updateTransformFeedbackMembers()
    {
        Program *vertexProgram = getShaderProgram(gl::ShaderType::Vertex);
    
        if (!vertexProgram)
        {
            return;
        }
    
        const ProgramExecutable &vertexExecutable     = vertexProgram->getExecutable();
        mState.mExecutable->mTransformFeedbackStrides = vertexExecutable.mTransformFeedbackStrides;
        mState.mExecutable->mLinkedTransformFeedbackVaryings =
            vertexExecutable.mLinkedTransformFeedbackVaryings;
    }
    
    void ProgramPipeline::updateShaderStorageBlocks()
    {
        mState.mExecutable->mComputeShaderStorageBlocks.clear();
        mState.mExecutable->mGraphicsShaderStorageBlocks.clear();
    
        // Only copy the storage blocks from each Program in the PPO once, since each Program could
        // contain multiple shader stages.
        ShaderBitSet handledStages;
    
        for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes)
        {
            const Program *shaderProgram = getShaderProgram(shaderType);
            if (shaderProgram && !handledStages.test(shaderType))
            {
                // Only add each Program's blocks once.
                handledStages |= shaderProgram->getExecutable().getLinkedShaderStages();
    
                for (const InterfaceBlock &block :
                     shaderProgram->getExecutable().getShaderStorageBlocks())
                {
                    mState.mExecutable->mGraphicsShaderStorageBlocks.emplace_back(block);
                }
            }
        }
    
        const Program *computeProgram = getShaderProgram(ShaderType::Compute);
        if (computeProgram)
        {
            for (const InterfaceBlock &block : computeProgram->getExecutable().getShaderStorageBlocks())
            {
                mState.mExecutable->mComputeShaderStorageBlocks.emplace_back(block);
            }
        }
    }
    
    void ProgramPipeline::updateImageBindings()
    {
        mState.mExecutable->mComputeImageBindings.clear();
        mState.mExecutable->mGraphicsImageBindings.clear();
        mState.mExecutable->mActiveImageShaderBits.fill({});
    
        // Only copy the storage blocks from each Program in the PPO once, since each Program could
        // contain multiple shader stages.
        ShaderBitSet handledStages;
    
        for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes)
        {
            const Program *shaderProgram = getShaderProgram(shaderType);
            if (shaderProgram && !handledStages.test(shaderType))
            {
                // Only add each Program's blocks once.
                handledStages |= shaderProgram->getExecutable().getLinkedShaderStages();
    
                for (const ImageBinding &imageBinding : shaderProgram->getState().getImageBindings())
                {
                    mState.mExecutable->mGraphicsImageBindings.emplace_back(imageBinding);
                }
    
                mState.mExecutable->updateActiveImages(shaderProgram->getExecutable());
            }
        }
    
        const Program *computeProgram = getShaderProgram(ShaderType::Compute);
        if (computeProgram)
        {
            for (const ImageBinding &imageBinding : computeProgram->getState().getImageBindings())
            {
                mState.mExecutable->mComputeImageBindings.emplace_back(imageBinding);
            }
    
            mState.mExecutable->setIsCompute(true);
            mState.mExecutable->updateActiveImages(computeProgram->getExecutable());
            mState.mExecutable->setIsCompute(false);
        }
    }
    
    void ProgramPipeline::updateExecutableGeometryProperties()
    {
        Program *geometryProgram = getShaderProgram(gl::ShaderType::Geometry);
    
        if (!geometryProgram)
        {
            return;
        }
    
        const ProgramExecutable &geometryExecutable = geometryProgram->getExecutable();
        mState.mExecutable->mGeometryShaderInputPrimitiveType =
            geometryExecutable.mGeometryShaderInputPrimitiveType;
        mState.mExecutable->mGeometryShaderOutputPrimitiveType =
            geometryExecutable.mGeometryShaderOutputPrimitiveType;
        mState.mExecutable->mGeometryShaderInvocations = geometryExecutable.mGeometryShaderInvocations;
        mState.mExecutable->mGeometryShaderMaxVertices = geometryExecutable.mGeometryShaderMaxVertices;
    }
    
    void ProgramPipeline::updateExecutableTessellationProperties()
    {
        Program *tessControlProgram = getShaderProgram(gl::ShaderType::TessControl);
        Program *tessEvalProgram    = getShaderProgram(gl::ShaderType::TessEvaluation);
    
        if (tessControlProgram)
        {
            const ProgramExecutable &tessControlExecutable = tessControlProgram->getExecutable();
            mState.mExecutable->mTessControlShaderVertices =
                tessControlExecutable.mTessControlShaderVertices;
        }
    
        if (tessEvalProgram)
        {
            const ProgramExecutable &tessEvalExecutable = tessEvalProgram->getExecutable();
            mState.mExecutable->mTessGenMode            = tessEvalExecutable.mTessGenMode;
            mState.mExecutable->mTessGenSpacing         = tessEvalExecutable.mTessGenSpacing;
            mState.mExecutable->mTessGenVertexOrder     = tessEvalExecutable.mTessGenVertexOrder;
            mState.mExecutable->mTessGenPointMode       = tessEvalExecutable.mTessGenPointMode;
        }
    }
    
    void ProgramPipeline::updateFragmentInoutRange()
    {
        Program *fragmentProgram = getShaderProgram(gl::ShaderType::Fragment);
    
        if (!fragmentProgram)
        {
            return;
        }
    
        const ProgramExecutable &fragmentExecutable = fragmentProgram->getExecutable();
        mState.mExecutable->mFragmentInoutRange     = fragmentExecutable.mFragmentInoutRange;
    }
    
    void ProgramPipeline::updateHasBooleans()
    {
        // Need to check all of the shader stages, not just linked, so we handle Compute correctly.
        for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes)
        {
            const Program *shaderProgram = getShaderProgram(shaderType);
            if (shaderProgram)
            {
                const ProgramExecutable &executable = shaderProgram->getExecutable();
    
                if (executable.hasUniformBuffers())
                {
                    mState.mExecutable->mPipelineHasGraphicsUniformBuffers = true;
                }
                if (executable.hasGraphicsStorageBuffers())
                {
                    mState.mExecutable->mPipelineHasGraphicsStorageBuffers = true;
                }
                if (executable.hasAtomicCounterBuffers())
                {
                    mState.mExecutable->mPipelineHasGraphicsAtomicCounterBuffers = true;
                }
                if (executable.hasDefaultUniforms())
                {
                    mState.mExecutable->mPipelineHasGraphicsDefaultUniforms = true;
                }
                if (executable.hasTextures())
                {
                    mState.mExecutable->mPipelineHasGraphicsTextures = true;
                }
                if (executable.hasGraphicsImages())
                {
                    mState.mExecutable->mPipelineHasGraphicsImages = true;
                }
            }
        }
    
        const Program *computeProgram = getShaderProgram(ShaderType::Compute);
        if (computeProgram)
        {
            const ProgramExecutable &executable = computeProgram->getExecutable();
    
            if (executable.hasUniformBuffers())
            {
                mState.mExecutable->mPipelineHasComputeUniformBuffers = true;
            }
            if (executable.hasComputeStorageBuffers())
            {
                mState.mExecutable->mPipelineHasComputeStorageBuffers = true;
            }
            if (executable.hasAtomicCounterBuffers())
            {
                mState.mExecutable->mPipelineHasComputeAtomicCounterBuffers = true;
            }
            if (executable.hasDefaultUniforms())
            {
                mState.mExecutable->mPipelineHasComputeDefaultUniforms = true;
            }
            if (executable.hasTextures())
            {
                mState.mExecutable->mPipelineHasComputeTextures = true;
            }
            if (executable.hasComputeImages())
            {
                mState.mExecutable->mPipelineHasComputeImages = true;
            }
        }
    }
    
    void ProgramPipeline::updateExecutable()
    {
        mState.mExecutable->reset();
    
        // Vertex Shader ProgramExecutable properties
        updateExecutableAttributes();
        updateTransformFeedbackMembers();
        updateShaderStorageBlocks();
        updateImageBindings();
    
        // Geometry Shader ProgramExecutable properties
        updateExecutableGeometryProperties();
    
        // Tessellation Shaders ProgramExecutable properties
        updateExecutableTessellationProperties();
    
        // Fragment Shader ProgramExecutable properties
        updateFragmentInoutRange();
    
        // All Shader ProgramExecutable properties
        mState.updateExecutableTextures();
    
        // Must be last, since it queries things updated by earlier functions
        updateHasBooleans();
    }
    
    // The attached shaders are checked for linking errors by matching up their variables.
    // Uniform, input and output variables get collected.
    // The code gets compiled into binaries.
    angle::Result ProgramPipeline::link(const Context *context)
    {
        if (mState.mIsLinked)
        {
            return angle::Result::Continue;
        }
    
        ProgramMergedVaryings mergedVaryings;
        ProgramVaryingPacking varyingPacking;
    
        if (!getExecutable().isCompute())
        {
            InfoLog &infoLog = mState.mExecutable->getInfoLog();
            infoLog.reset();
    
            if (!linkVaryings(infoLog))
            {
                return angle::Result::Stop;
            }
    
            if (!LinkValidateProgramGlobalNames(infoLog, *this))
            {
                return angle::Result::Stop;
            }
    
            mergedVaryings = GetMergedVaryingsFromShaders(*this);
            // If separable program objects are in use, the set of attributes captured is taken
            // from the program object active on the last vertex processing stage.
            Program *tfProgram = mState.mPrograms[ShaderType::Geometry];
            if (!tfProgram)
            {
                tfProgram = mState.mPrograms[ShaderType::Vertex];
            }
    
            const std::vector<std::string> &transformFeedbackVaryingNames =
                tfProgram->getState().getTransformFeedbackVaryingNames();
    
            if (!mState.mExecutable->linkMergedVaryings(context, *this, mergedVaryings,
                                                        transformFeedbackVaryingNames, false,
                                                        &varyingPacking))
            {
                return angle::Result::Stop;
            }
        }
    
        ANGLE_TRY(getImplementation()->link(context, mergedVaryings, varyingPacking));
    
        mState.mIsLinked = true;
    
        return angle::Result::Continue;
    }
    
    bool ProgramPipeline::linkVaryings(InfoLog &infoLog) const
    {
        ShaderType previousShaderType = ShaderType::InvalidEnum;
        for (ShaderType shaderType : getExecutable().getLinkedShaderStages())
        {
            Program *program = getShaderProgram(shaderType);
            ASSERT(program);
            ProgramExecutable &executable = program->getExecutable();
    
            if (previousShaderType != ShaderType::InvalidEnum)
            {
                Program *previousProgram = getShaderProgram(previousShaderType);
                ASSERT(previousProgram);
                ProgramExecutable &previousExecutable = previousProgram->getExecutable();
    
                if (!LinkValidateShaderInterfaceMatching(
                        previousExecutable.getLinkedOutputVaryings(previousShaderType),
                        executable.getLinkedInputVaryings(shaderType), previousShaderType, shaderType,
                        previousExecutable.getLinkedShaderVersion(previousShaderType),
                        executable.getLinkedShaderVersion(shaderType), true, infoLog))
                {
                    return false;
                }
            }
            previousShaderType = shaderType;
        }
    
        // TODO: http://anglebug.com/3571 and http://anglebug.com/3572
        // Need to move logic of validating builtin varyings inside the for-loop above.
        // This is because the built-in symbols `gl_ClipDistance` and `gl_CullDistance`
        // can be redeclared in Geometry or Tessellation shaders as well.
        Program *vertexProgram   = mState.mPrograms[ShaderType::Vertex];
        Program *fragmentProgram = mState.mPrograms[ShaderType::Fragment];
        if (!vertexProgram || !fragmentProgram)
        {
            return false;
        }
        ProgramExecutable &vertexExecutable   = vertexProgram->getExecutable();
        ProgramExecutable &fragmentExecutable = fragmentProgram->getExecutable();
        return LinkValidateBuiltInVaryings(
            vertexExecutable.getLinkedOutputVaryings(ShaderType::Vertex),
            fragmentExecutable.getLinkedInputVaryings(ShaderType::Fragment), ShaderType::Vertex,
            ShaderType::Fragment, vertexExecutable.getLinkedShaderVersion(ShaderType::Vertex),
            fragmentExecutable.getLinkedShaderVersion(ShaderType::Fragment), infoLog);
    }
    
    void ProgramPipeline::validate(const gl::Context *context)
    {
        const Caps &caps = context->getCaps();
        mState.mValid    = true;
        InfoLog &infoLog = mState.mExecutable->getInfoLog();
        infoLog.reset();
    
        for (const ShaderType shaderType : mState.mExecutable->getLinkedShaderStages())
        {
            Program *shaderProgram = mState.mPrograms[shaderType];
            if (shaderProgram)
            {
                shaderProgram->resolveLink(context);
                shaderProgram->validate(caps);
                std::string shaderInfoString = shaderProgram->getExecutable().getInfoLogString();
                if (shaderInfoString.length())
                {
                    mState.mValid = false;
                    infoLog << shaderInfoString << "\n";
                    return;
                }
                if (!shaderProgram->isSeparable())
                {
                    mState.mValid = false;
                    infoLog << GetShaderTypeString(shaderType) << " is not marked separable."
                            << "\n";
                    return;
                }
            }
        }
    
        if (!linkVaryings(infoLog))
        {
            mState.mValid = false;
    
            for (const ShaderType shaderType : mState.mExecutable->getLinkedShaderStages())
            {
                Program *shaderProgram = mState.mPrograms[shaderType];
                ASSERT(shaderProgram);
                shaderProgram->validate(caps);
                std::string shaderInfoString = shaderProgram->getExecutable().getInfoLogString();
                if (shaderInfoString.length())
                {
                    infoLog << shaderInfoString << "\n";
                }
            }
        }
    }
    
    bool ProgramPipeline::validateSamplers(InfoLog *infoLog, const Caps &caps)
    {
        for (const ShaderType shaderType : gl::AllShaderTypes())
        {
            Program *shaderProgram = mState.mPrograms[shaderType];
            if (shaderProgram && !shaderProgram->validateSamplers(infoLog, caps))
            {
                return false;
            }
        }
    
        return true;
    }
    
    void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
    {
        switch (message)
        {
            case angle::SubjectMessage::SubjectChanged:
                mState.mIsLinked = false;
                mState.updateExecutableTextures();
                break;
            default:
                UNREACHABLE();
                break;
        }
    }
    
    Shader *ProgramPipeline::getAttachedShader(ShaderType shaderType) const
    {
        const Program *program = mState.mPrograms[shaderType];
        return program ? program->getAttachedShader(shaderType) : nullptr;
    }
    }  // namespace gl