| // | |
| // 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/ContextImpl.h" | |
| #include "libANGLE/renderer/ProgramPipelineImpl.h" | |
| namespace gl | |
| { | |
| ProgramPipelineState::ProgramPipelineState(rx::GLImplFactory *factory) | |
| : mLabel(), | |
| mActiveShaderProgram(nullptr), | |
| mValid(false), | |
| mExecutable(makeNewExecutable(factory, {})), | |
| mIsLinked(false) | |
| { | |
| for (const ShaderType shaderType : AllShaderTypes()) | |
| { | |
| mPrograms[shaderType] = nullptr; | |
| } | |
| } | |
| ProgramPipelineState::~ProgramPipelineState() {} | |
| const std::string &ProgramPipelineState::getLabel() const | |
| { | |
| return mLabel; | |
| } | |
| void ProgramPipelineState::activeShaderProgram(Program *shaderProgram) | |
| { | |
| mActiveShaderProgram = shaderProgram; | |
| } | |
| SharedProgramExecutable ProgramPipelineState::makeNewExecutable( | |
| rx::GLImplFactory *factory, | |
| ShaderMap<SharedProgramExecutable> &&ppoProgramExecutables) | |
| { | |
| SharedProgramExecutable newExecutable = std::make_shared<ProgramExecutable>(factory, &mInfoLog); | |
| newExecutable->mIsPPO = true; | |
| newExecutable->mPPOProgramExecutables = std::move(ppoProgramExecutables); | |
| return newExecutable; | |
| } | |
| void ProgramPipelineState::useProgramStage(const Context *context, | |
| const ShaderType shaderType, | |
| Program *shaderProgram, | |
| angle::ObserverBinding *programObserverBinding, | |
| angle::ObserverBinding *programExecutableObserverBinding) | |
| { | |
| 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; | |
| // Install the program executable, if not already | |
| if (shaderProgram->getSharedExecutable().get() != | |
| mExecutable->mPPOProgramExecutables[shaderType].get()) | |
| { | |
| InstallExecutable(context, shaderProgram->getSharedExecutable(), | |
| &mExecutable->mPPOProgramExecutables[shaderType]); | |
| } | |
| 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; | |
| UninstallExecutable(context, &mExecutable->mPPOProgramExecutables[shaderType]); | |
| } | |
| programObserverBinding->bind(mPrograms[shaderType]); | |
| programExecutableObserverBinding->bind(mExecutable->mPPOProgramExecutables[shaderType].get()); | |
| } | |
| void ProgramPipelineState::useProgramStages( | |
| const Context *context, | |
| const ShaderBitSet &shaderTypes, | |
| Program *shaderProgram, | |
| std::vector<angle::ObserverBinding> *programObserverBindings, | |
| std::vector<angle::ObserverBinding> *programExecutableObserverBindings) | |
| { | |
| for (ShaderType shaderType : shaderTypes) | |
| { | |
| useProgramStage(context, shaderType, shaderProgram, | |
| &programObserverBindings->at(static_cast<size_t>(shaderType)), | |
| &programExecutableObserverBindings->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 SharedProgramExecutable &programExecutable = getShaderProgramExecutable(shaderType); | |
| ASSERT(programExecutable); | |
| mExecutable->setActiveTextureMask(mExecutable->getActiveSamplersMask() | | |
| programExecutable->getActiveSamplersMask()); | |
| mExecutable->setActiveImagesMask(mExecutable->getActiveImagesMask() | | |
| programExecutable->getActiveImagesMask()); | |
| // Updates mActiveSamplerRefCounts, mActiveSamplerTypes, and mActiveSamplerFormats | |
| mExecutable->updateActiveSamplers(*programExecutable); | |
| } | |
| } | |
| void ProgramPipelineState::updateExecutableSpecConstUsageBits() | |
| { | |
| rx::SpecConstUsageBits specConstUsageBits; | |
| for (const ShaderType shaderType : mExecutable->getLinkedShaderStages()) | |
| { | |
| const SharedProgramExecutable &programExecutable = getShaderProgramExecutable(shaderType); | |
| ASSERT(programExecutable); | |
| specConstUsageBits |= programExecutable->getSpecConstUsageBits(); | |
| } | |
| mExecutable->mPod.specConstUsageBits = specConstUsageBits; | |
| } | |
| ProgramPipeline::ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle) | |
| : RefCountObject(factory->generateSerial(), handle), | |
| mProgramPipelineImpl(factory->createProgramPipeline(mState)), | |
| mState(factory) | |
| { | |
| ASSERT(mProgramPipelineImpl); | |
| for (const ShaderType shaderType : AllShaderTypes()) | |
| { | |
| mProgramObserverBindings.emplace_back(this, static_cast<angle::SubjectIndex>(shaderType)); | |
| mProgramExecutableObserverBindings.emplace_back( | |
| this, static_cast<angle::SubjectIndex>(shaderType)); | |
| } | |
| } | |
| ProgramPipeline::~ProgramPipeline() | |
| { | |
| mProgramPipelineImpl.reset(nullptr); | |
| } | |
| void ProgramPipeline::onDestroy(const Context *context) | |
| { | |
| for (Program *program : mState.mPrograms) | |
| { | |
| if (program) | |
| { | |
| ASSERT(program->getRefCount()); | |
| program->release(context); | |
| } | |
| } | |
| getImplementation()->destroy(context); | |
| UninstallExecutable(context, &mState.mExecutable); | |
| mState.destroyDiscardedExecutables(context); | |
| } | |
| void ProgramPipelineState::destroyDiscardedExecutables(const Context *context) | |
| { | |
| for (SharedProgramExecutable &executable : mProgramExecutablesToDiscard) | |
| { | |
| UninstallExecutable(context, &executable); | |
| } | |
| mProgramExecutablesToDiscard.clear(); | |
| } | |
| angle::Result ProgramPipeline::setLabel(const Context *context, const std::string &label) | |
| { | |
| mState.mLabel = label; | |
| if (mProgramPipelineImpl) | |
| { | |
| return mProgramPipelineImpl->onLabelUpdate(context); | |
| } | |
| return angle::Result::Continue; | |
| } | |
| 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); | |
| } | |
| angle::Result ProgramPipeline::useProgramStages(const Context *context, | |
| GLbitfield stages, | |
| Program *shaderProgram) | |
| { | |
| ShaderBitSet shaderTypes; | |
| if (stages != GL_ALL_SHADER_BITS) | |
| { | |
| ASSERT(stages < 256u); | |
| for (size_t singleShaderBit : angle::BitSet<8>(stages)) | |
| { | |
| // Cast back to a bit after the iterator returns an index. | |
| ShaderType shaderType = GetShaderTypeFromBitfield(angle::Bit<size_t>(singleShaderBit)); | |
| ASSERT(shaderType != ShaderType::InvalidEnum); | |
| shaderTypes.set(shaderType); | |
| } | |
| } | |
| else | |
| { | |
| shaderTypes.set(); | |
| } | |
| ASSERT(shaderTypes.any()); | |
| bool needToUpdatePipelineState = false; | |
| for (ShaderType shaderType : shaderTypes) | |
| { | |
| if (mState.getShaderProgram(shaderType) != shaderProgram || | |
| (shaderProgram && | |
| getShaderProgramExecutable(shaderType) != shaderProgram->getSharedExecutable())) | |
| { | |
| needToUpdatePipelineState = true; | |
| break; | |
| } | |
| } | |
| if (!needToUpdatePipelineState) | |
| { | |
| return angle::Result::Continue; | |
| } | |
| mState.useProgramStages(context, shaderTypes, shaderProgram, &mProgramObserverBindings, | |
| &mProgramExecutableObserverBindings); | |
| mState.mIsLinked = false; | |
| onStateChange(angle::SubjectMessage::ProgramUnlinked); | |
| return angle::Result::Continue; | |
| } | |
| void ProgramPipeline::updateLinkedShaderStages() | |
| { | |
| mState.mExecutable->resetLinkedShaderStages(); | |
| for (const ShaderType shaderType : AllShaderTypes()) | |
| { | |
| if (getShaderProgramExecutable(shaderType)) | |
| { | |
| mState.mExecutable->setLinkedShaderStages(shaderType); | |
| } | |
| } | |
| mState.mExecutable->updateCanDrawWith(); | |
| } | |
| void ProgramPipeline::updateExecutableAttributes() | |
| { | |
| const SharedProgramExecutable &vertexExecutable = | |
| getShaderProgramExecutable(ShaderType::Vertex); | |
| if (!vertexExecutable) | |
| { | |
| return; | |
| } | |
| mState.mExecutable->mPod.activeAttribLocationsMask = | |
| vertexExecutable->mPod.activeAttribLocationsMask; | |
| mState.mExecutable->mPod.maxActiveAttribLocation = | |
| vertexExecutable->mPod.maxActiveAttribLocation; | |
| mState.mExecutable->mPod.attributesTypeMask = vertexExecutable->mPod.attributesTypeMask; | |
| mState.mExecutable->mPod.attributesMask = vertexExecutable->mPod.attributesMask; | |
| mState.mExecutable->mProgramInputs = vertexExecutable->mProgramInputs; | |
| mState.mExecutable->mPod.numViews = vertexExecutable->mPod.numViews; | |
| mState.mExecutable->mPod.drawIDLocation = vertexExecutable->mPod.drawIDLocation; | |
| mState.mExecutable->mPod.baseVertexLocation = vertexExecutable->mPod.baseVertexLocation; | |
| mState.mExecutable->mPod.baseInstanceLocation = vertexExecutable->mPod.baseInstanceLocation; | |
| } | |
| void ProgramPipeline::updateTransformFeedbackMembers() | |
| { | |
| ShaderType lastVertexProcessingStage = | |
| GetLastPreFragmentStage(getExecutable().getLinkedShaderStages()); | |
| if (lastVertexProcessingStage == ShaderType::InvalidEnum) | |
| { | |
| return; | |
| } | |
| const SharedProgramExecutable &lastPreFragmentExecutable = | |
| getShaderProgramExecutable(lastVertexProcessingStage); | |
| ASSERT(lastPreFragmentExecutable); | |
| mState.mExecutable->mTransformFeedbackStrides = | |
| lastPreFragmentExecutable->mTransformFeedbackStrides; | |
| mState.mExecutable->mLinkedTransformFeedbackVaryings = | |
| lastPreFragmentExecutable->mLinkedTransformFeedbackVaryings; | |
| } | |
| void ProgramPipeline::updateShaderStorageBlocks() | |
| { | |
| mState.mExecutable->mShaderStorageBlocks.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 ShaderType shaderType : AllShaderTypes()) | |
| { | |
| const SharedProgramExecutable &programExecutable = getShaderProgramExecutable(shaderType); | |
| if (programExecutable && !handledStages.test(shaderType)) | |
| { | |
| // Only add each Program's blocks once. | |
| handledStages |= programExecutable->getLinkedShaderStages(); | |
| for (const InterfaceBlock &block : programExecutable->getShaderStorageBlocks()) | |
| { | |
| mState.mExecutable->mShaderStorageBlocks.emplace_back(block); | |
| } | |
| } | |
| } | |
| } | |
| void ProgramPipeline::updateImageBindings() | |
| { | |
| mState.mExecutable->mImageBindings.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 ShaderType shaderType : AllShaderTypes()) | |
| { | |
| const SharedProgramExecutable &programExecutable = getShaderProgramExecutable(shaderType); | |
| if (programExecutable && !handledStages.test(shaderType)) | |
| { | |
| // Only add each Program's blocks once. | |
| handledStages |= programExecutable->getLinkedShaderStages(); | |
| for (const ImageBinding &imageBinding : *programExecutable->getImageBindings()) | |
| { | |
| mState.mExecutable->mImageBindings.emplace_back(imageBinding); | |
| } | |
| mState.mExecutable->updateActiveImages(*programExecutable); | |
| } | |
| } | |
| } | |
| void ProgramPipeline::updateExecutableGeometryProperties() | |
| { | |
| const SharedProgramExecutable &geometryExecutable = | |
| getShaderProgramExecutable(ShaderType::Geometry); | |
| if (!geometryExecutable) | |
| { | |
| return; | |
| } | |
| mState.mExecutable->mPod.geometryShaderInputPrimitiveType = | |
| geometryExecutable->mPod.geometryShaderInputPrimitiveType; | |
| mState.mExecutable->mPod.geometryShaderOutputPrimitiveType = | |
| geometryExecutable->mPod.geometryShaderOutputPrimitiveType; | |
| mState.mExecutable->mPod.geometryShaderInvocations = | |
| geometryExecutable->mPod.geometryShaderInvocations; | |
| mState.mExecutable->mPod.geometryShaderMaxVertices = | |
| geometryExecutable->mPod.geometryShaderMaxVertices; | |
| } | |
| void ProgramPipeline::updateExecutableTessellationProperties() | |
| { | |
| const SharedProgramExecutable &tessControlExecutable = | |
| getShaderProgramExecutable(ShaderType::TessControl); | |
| const SharedProgramExecutable &tessEvalExecutable = | |
| getShaderProgramExecutable(ShaderType::TessEvaluation); | |
| if (tessControlExecutable) | |
| { | |
| mState.mExecutable->mPod.tessControlShaderVertices = | |
| tessControlExecutable->mPod.tessControlShaderVertices; | |
| } | |
| if (tessEvalExecutable) | |
| { | |
| mState.mExecutable->mPod.tessGenMode = tessEvalExecutable->mPod.tessGenMode; | |
| mState.mExecutable->mPod.tessGenSpacing = tessEvalExecutable->mPod.tessGenSpacing; | |
| mState.mExecutable->mPod.tessGenVertexOrder = tessEvalExecutable->mPod.tessGenVertexOrder; | |
| mState.mExecutable->mPod.tessGenPointMode = tessEvalExecutable->mPod.tessGenPointMode; | |
| } | |
| } | |
| void ProgramPipeline::updateFragmentInoutRangeAndEnablesPerSampleShading() | |
| { | |
| const SharedProgramExecutable &fragmentExecutable = | |
| getShaderProgramExecutable(ShaderType::Fragment); | |
| if (!fragmentExecutable) | |
| { | |
| return; | |
| } | |
| mState.mExecutable->mPod.fragmentInoutIndices = fragmentExecutable->mPod.fragmentInoutIndices; | |
| mState.mExecutable->mPod.hasDiscard = fragmentExecutable->mPod.hasDiscard; | |
| mState.mExecutable->mPod.enablesPerSampleShading = | |
| fragmentExecutable->mPod.enablesPerSampleShading; | |
| mState.mExecutable->mPod.hasDepthInputAttachment = | |
| fragmentExecutable->mPod.hasDepthInputAttachment; | |
| mState.mExecutable->mPod.hasStencilInputAttachment = | |
| fragmentExecutable->mPod.hasStencilInputAttachment; | |
| } | |
| void ProgramPipeline::updateLinkedVaryings() | |
| { | |
| // Need to check all of the shader stages, not just linked, so we handle Compute correctly. | |
| for (const ShaderType shaderType : kAllGraphicsShaderTypes) | |
| { | |
| const SharedProgramExecutable &programExecutable = getShaderProgramExecutable(shaderType); | |
| if (programExecutable) | |
| { | |
| mState.mExecutable->mLinkedOutputVaryings[shaderType] = | |
| programExecutable->getLinkedOutputVaryings(shaderType); | |
| mState.mExecutable->mLinkedInputVaryings[shaderType] = | |
| programExecutable->getLinkedInputVaryings(shaderType); | |
| } | |
| } | |
| const SharedProgramExecutable &computeExecutable = | |
| getShaderProgramExecutable(ShaderType::Compute); | |
| if (computeExecutable) | |
| { | |
| mState.mExecutable->mLinkedOutputVaryings[ShaderType::Compute] = | |
| computeExecutable->getLinkedOutputVaryings(ShaderType::Compute); | |
| mState.mExecutable->mLinkedInputVaryings[ShaderType::Compute] = | |
| computeExecutable->getLinkedInputVaryings(ShaderType::Compute); | |
| } | |
| } | |
| void ProgramPipeline::updateExecutable() | |
| { | |
| // Vertex Shader ProgramExecutable properties | |
| updateExecutableAttributes(); | |
| updateTransformFeedbackMembers(); | |
| updateShaderStorageBlocks(); | |
| updateImageBindings(); | |
| // Geometry Shader ProgramExecutable properties | |
| updateExecutableGeometryProperties(); | |
| // Tessellation Shaders ProgramExecutable properties | |
| updateExecutableTessellationProperties(); | |
| // Fragment Shader ProgramExecutable properties | |
| updateFragmentInoutRangeAndEnablesPerSampleShading(); | |
| // All Shader ProgramExecutable properties | |
| mState.updateExecutableTextures(); | |
| mState.updateExecutableSpecConstUsageBits(); | |
| updateLinkedVaryings(); | |
| } | |
| void ProgramPipeline::resolveAttachedPrograms(const Context *context) | |
| { | |
| // Wait for attached programs to finish linking | |
| for (Program *program : mState.mPrograms) | |
| { | |
| if (program != nullptr) | |
| { | |
| program->resolveLink(context); | |
| } | |
| } | |
| } | |
| // 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) | |
| { | |
| mState.destroyDiscardedExecutables(context); | |
| // Make a new executable to hold the result of the link. | |
| SharedProgramExecutable newExecutable = mState.makeNewExecutable( | |
| context->getImplementation(), std::move(mState.mExecutable->mPPOProgramExecutables)); | |
| InstallExecutable(context, newExecutable, &mState.mExecutable); | |
| onStateChange(angle::SubjectMessage::ProgramUnlinked); | |
| updateLinkedShaderStages(); | |
| ASSERT(!mState.mIsLinked); | |
| ProgramMergedVaryings mergedVaryings; | |
| ProgramVaryingPacking varyingPacking; | |
| LinkingVariables linkingVariables; | |
| mState.mInfoLog.reset(); | |
| linkingVariables.initForProgramPipeline(mState); | |
| const Caps &caps = context->getCaps(); | |
| const Limitations &limitations = context->getLimitations(); | |
| const Version &clientVersion = context->getClientVersion(); | |
| const bool isWebGL = context->isWebGL(); | |
| if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::TessControl) != | |
| mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::TessEvaluation)) | |
| { | |
| return angle::Result::Stop; | |
| } | |
| if (mState.mExecutable->hasLinkedShaderStage(ShaderType::Vertex)) | |
| { | |
| if (!linkVaryings()) | |
| { | |
| return angle::Result::Stop; | |
| } | |
| if (!LinkValidateProgramGlobalNames(mState.mInfoLog, getExecutable(), linkingVariables)) | |
| { | |
| return angle::Result::Stop; | |
| } | |
| const SharedProgramExecutable &fragmentExecutable = | |
| getShaderProgramExecutable(ShaderType::Fragment); | |
| if (fragmentExecutable) | |
| { | |
| // We should also be validating SSBO and image uniform counts. | |
| const GLuint combinedImageUniforms = 0; | |
| const GLuint combinedShaderStorageBlocks = 0; | |
| ASSERT(mState.mExecutable->mOutputVariables.empty()); | |
| mState.mExecutable->mOutputVariables = fragmentExecutable->getOutputVariables(); | |
| if (!mState.mExecutable->linkValidateOutputVariables( | |
| caps, clientVersion, combinedImageUniforms, combinedShaderStorageBlocks, | |
| fragmentExecutable->getLinkedShaderVersion(ShaderType::Fragment), | |
| ProgramAliasedBindings(), ProgramAliasedBindings())) | |
| { | |
| return angle::Result::Continue; | |
| } | |
| } | |
| mergedVaryings = GetMergedVaryingsFromLinkingVariables(linkingVariables); | |
| // 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. | |
| ShaderType lastVertexProcessingStage = | |
| GetLastPreFragmentStage(getExecutable().getLinkedShaderStages()); | |
| if (lastVertexProcessingStage == ShaderType::InvalidEnum) | |
| { | |
| // If there is no active program for the vertex or fragment shader stages, the results | |
| // of vertex and fragment shader execution will respectively be undefined. However, | |
| // this is not an error. | |
| return angle::Result::Continue; | |
| } | |
| const SharedProgramExecutable *tfExecutable = | |
| &getShaderProgramExecutable(lastVertexProcessingStage); | |
| ASSERT(*tfExecutable); | |
| if (!*tfExecutable) | |
| { | |
| tfExecutable = &getShaderProgramExecutable(ShaderType::Vertex); | |
| } | |
| mState.mExecutable->mTransformFeedbackVaryingNames = | |
| (*tfExecutable)->mTransformFeedbackVaryingNames; | |
| mState.mExecutable->mPod.transformFeedbackBufferMode = | |
| (*tfExecutable)->mPod.transformFeedbackBufferMode; | |
| if (!mState.mExecutable->linkMergedVaryings(caps, limitations, clientVersion, isWebGL, | |
| mergedVaryings, linkingVariables, | |
| &varyingPacking)) | |
| { | |
| return angle::Result::Stop; | |
| } | |
| } | |
| // Merge uniforms. | |
| mState.mExecutable->copyUniformsFromProgramMap(mState.mExecutable->mPPOProgramExecutables); | |
| if (mState.mExecutable->hasLinkedShaderStage(ShaderType::Vertex)) | |
| { | |
| const SharedProgramExecutable &executable = getShaderProgramExecutable(ShaderType::Vertex); | |
| mState.mExecutable->copyInputsFromProgram(*executable); | |
| } | |
| // Merge shader buffers (UBOs, SSBOs, and atomic counter buffers) into the executable. | |
| // Also copy over image and sampler bindings. | |
| for (ShaderType shaderType : mState.mExecutable->getLinkedShaderStages()) | |
| { | |
| const SharedProgramExecutable &executable = getShaderProgramExecutable(shaderType); | |
| mState.mExecutable->copyUniformBuffersFromProgram(*executable, shaderType, | |
| &mState.mUniformBlockMap[shaderType]); | |
| mState.mExecutable->copyStorageBuffersFromProgram(*executable, shaderType); | |
| mState.mExecutable->copySamplerBindingsFromProgram(*executable); | |
| mState.mExecutable->copyImageBindingsFromProgram(*executable); | |
| } | |
| // Update active uniform and storage buffer block indices mask | |
| mState.mExecutable->updateActiveUniformBufferBlocks(); | |
| mState.mExecutable->updateActiveStorageBufferBlocks(); | |
| if (mState.mExecutable->hasLinkedShaderStage(ShaderType::Fragment)) | |
| { | |
| const SharedProgramExecutable &executable = | |
| getShaderProgramExecutable(ShaderType::Fragment); | |
| mState.mExecutable->copyOutputsFromProgram(*executable); | |
| } | |
| mState.mExecutable->mActiveSamplerRefCounts.fill(0); | |
| updateExecutable(); | |
| if (mState.mExecutable->hasLinkedShaderStage(ShaderType::Vertex) || | |
| mState.mExecutable->hasLinkedShaderStage(ShaderType::Compute)) | |
| { | |
| ANGLE_TRY(getImplementation()->link(context, mergedVaryings, varyingPacking)); | |
| } | |
| mState.mIsLinked = true; | |
| onStateChange(angle::SubjectMessage::ProgramRelinked); | |
| return angle::Result::Continue; | |
| } | |
| int ProgramPipeline::getInfoLogLength() const | |
| { | |
| return static_cast<int>(mState.mInfoLog.getLength()); | |
| } | |
| void ProgramPipeline::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const | |
| { | |
| return mState.mInfoLog.getLog(bufSize, length, infoLog); | |
| } | |
| bool ProgramPipeline::linkVaryings() | |
| { | |
| ShaderType previousShaderType = ShaderType::InvalidEnum; | |
| for (ShaderType shaderType : kAllGraphicsShaderTypes) | |
| { | |
| const SharedProgramExecutable &programExecutable = getShaderProgramExecutable(shaderType); | |
| if (!programExecutable) | |
| { | |
| continue; | |
| } | |
| if (previousShaderType != ShaderType::InvalidEnum) | |
| { | |
| const SharedProgramExecutable &previousExecutable = | |
| getShaderProgramExecutable(previousShaderType); | |
| ASSERT(previousExecutable); | |
| if (previousExecutable == programExecutable) | |
| { | |
| continue; | |
| } | |
| if (!LinkValidateInOutNumberMatching( | |
| previousExecutable->getLinkedOutputVaryings(previousShaderType), | |
| programExecutable->getLinkedInputVaryings(shaderType), previousShaderType, | |
| shaderType, previousExecutable->getLinkedShaderVersion(previousShaderType), | |
| programExecutable->getLinkedShaderVersion(shaderType), mState.mInfoLog)) | |
| { | |
| return false; | |
| } | |
| if (!LinkValidateShaderInterfaceMatching( | |
| previousExecutable->getLinkedOutputVaryings(previousShaderType), | |
| programExecutable->getLinkedInputVaryings(shaderType), previousShaderType, | |
| shaderType, previousExecutable->getLinkedShaderVersion(previousShaderType), | |
| programExecutable->getLinkedShaderVersion(shaderType), true, mState.mInfoLog)) | |
| { | |
| return false; | |
| } | |
| } | |
| previousShaderType = shaderType; | |
| } | |
| // TODO: http://anglebug.com/42262233 and http://anglebug.com/42262234 | |
| // 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. | |
| const SharedProgramExecutable &vertexExecutable = | |
| getShaderProgramExecutable(ShaderType::Vertex); | |
| const SharedProgramExecutable &fragmentExecutable = | |
| getShaderProgramExecutable(ShaderType::Fragment); | |
| if (!vertexExecutable || !fragmentExecutable) | |
| { | |
| return true; | |
| } | |
| return LinkValidateBuiltInVaryings( | |
| vertexExecutable->getLinkedOutputVaryings(ShaderType::Vertex), | |
| fragmentExecutable->getLinkedInputVaryings(ShaderType::Fragment), ShaderType::Vertex, | |
| ShaderType::Fragment, vertexExecutable->getLinkedShaderVersion(ShaderType::Vertex), | |
| fragmentExecutable->getLinkedShaderVersion(ShaderType::Fragment), mState.mInfoLog); | |
| } | |
| void ProgramPipeline::validate(const Context *context) | |
| { | |
| updateLinkedShaderStages(); | |
| const Caps &caps = context->getCaps(); | |
| mState.mValid = true; | |
| mState.mInfoLog.reset(); | |
| bool noActiveStage = true; | |
| if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::TessControl) != | |
| mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::TessEvaluation)) | |
| { | |
| mState.mValid = false; | |
| mState.mInfoLog << "Program pipeline must have both a Tessellation Control and Evaluation " | |
| "shader or neither\n"; | |
| return; | |
| } | |
| for (const ShaderType shaderType : mState.mExecutable->getLinkedShaderStages()) | |
| { | |
| Program *shaderProgram = mState.mPrograms[shaderType]; | |
| if (shaderProgram) | |
| { | |
| noActiveStage = false; | |
| shaderProgram->resolveLink(context); | |
| shaderProgram->validate(caps); | |
| std::string shaderInfoString = shaderProgram->getExecutable().getInfoLogString(); | |
| if (shaderInfoString.length()) | |
| { | |
| mState.mValid = false; | |
| mState.mInfoLog << shaderInfoString << "\n"; | |
| return; | |
| } | |
| if (!shaderProgram->isSeparable()) | |
| { | |
| mState.mValid = false; | |
| mState.mInfoLog << GetShaderTypeString(shaderType) << " is not marked separable." | |
| << "\n"; | |
| return; | |
| } | |
| } | |
| } | |
| if (noActiveStage) | |
| { | |
| mState.mValid = false; | |
| mState.mInfoLog << "Program pipeline has no active stage yet.\n"; | |
| return; | |
| } | |
| intptr_t programPipelineError = context->getStateCache().getProgramPipelineError(context); | |
| if (programPipelineError) | |
| { | |
| mState.mValid = false; | |
| const char *errorMessage = reinterpret_cast<const char *>(programPipelineError); | |
| mState.mInfoLog << errorMessage << "\n"; | |
| return; | |
| } | |
| if (!linkVaryings()) | |
| { | |
| 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()) | |
| { | |
| mState.mInfoLog << shaderInfoString << "\n"; | |
| } | |
| } | |
| } | |
| } | |
| void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) | |
| { | |
| switch (message) | |
| { | |
| case angle::SubjectMessage::ProgramTextureOrImageBindingChanged: | |
| mState.mExecutable->mActiveSamplerRefCounts.fill(0); | |
| mState.updateExecutableTextures(); | |
| break; | |
| case angle::SubjectMessage::ProgramUnlinked: | |
| // A used program is being relinked. Ensure next usage of the PPO resolve the program | |
| // link and relinks the PPO. | |
| mState.mIsLinked = false; | |
| onStateChange(angle::SubjectMessage::ProgramUnlinked); | |
| break; | |
| case angle::SubjectMessage::ProgramRelinked: | |
| { | |
| ShaderType shaderType = static_cast<ShaderType>(index); | |
| ASSERT(mState.mPrograms[shaderType] != nullptr); | |
| ASSERT(mState.mExecutable->mPPOProgramExecutables[shaderType]); | |
| mState.mIsLinked = false; | |
| mState.mProgramExecutablesToDiscard.emplace_back( | |
| std::move(mState.mExecutable->mPPOProgramExecutables[shaderType])); | |
| mState.mExecutable->mPPOProgramExecutables[shaderType] = | |
| mState.mPrograms[shaderType]->getSharedExecutable(); | |
| break; | |
| } | |
| case angle::SubjectMessage::SamplerUniformsUpdated: | |
| mState.mExecutable->clearSamplerBindings(); | |
| for (ShaderType shaderType : mState.mExecutable->getLinkedShaderStages()) | |
| { | |
| const SharedProgramExecutable &executable = getShaderProgramExecutable(shaderType); | |
| mState.mExecutable->copySamplerBindingsFromProgram(*executable); | |
| } | |
| mState.mExecutable->mActiveSamplerRefCounts.fill(0); | |
| mState.updateExecutableTextures(); | |
| break; | |
| default: | |
| if (angle::IsProgramUniformBlockBindingUpdatedMessage(message)) | |
| { | |
| if (mState.mIsLinked) | |
| { | |
| ShaderType shaderType = static_cast<ShaderType>(index); | |
| const SharedProgramExecutable &executable = | |
| getShaderProgramExecutable(shaderType); | |
| const GLuint blockIndex = | |
| angle::ProgramUniformBlockBindingUpdatedMessageToIndex(message); | |
| if (executable->getUniformBlocks()[blockIndex].isActive(shaderType)) | |
| { | |
| const uint32_t blockIndexInPPO = | |
| mState.mUniformBlockMap[shaderType][blockIndex]; | |
| ASSERT(blockIndexInPPO < mState.mExecutable->mUniformBlocks.size()); | |
| // Set the block buffer binding in the PPO to the same binding as the | |
| // program's. | |
| mState.mExecutable->remapUniformBlockBinding( | |
| {blockIndexInPPO}, executable->getUniformBlockBinding(blockIndex)); | |
| // Notify the contexts that the block binding has changed. | |
| onStateChange(angle::ProgramUniformBlockBindingUpdatedMessageFromIndex( | |
| blockIndexInPPO)); | |
| } | |
| } | |
| break; | |
| } | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| } // namespace gl |