summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/ProgramPipeline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/ProgramPipeline.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/ProgramPipeline.cpp721
1 files changed, 721 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/ProgramPipeline.cpp b/gfx/angle/checkout/src/libANGLE/ProgramPipeline.cpp
new file mode 100644
index 0000000000..1675b06705
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/ProgramPipeline.cpp
@@ -0,0 +1,721 @@
+//
+// 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,
+ const gl::ShaderBitSet &shaderTypes,
+ Program *shaderProgram,
+ std::vector<angle::ObserverBinding> *programObserverBindings)
+{
+ for (ShaderType shaderType : shaderTypes)
+ {
+ 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(mExecutable->getActiveSamplersMask() |
+ program->getExecutable().getActiveSamplersMask());
+ mExecutable->setActiveImagesMask(mExecutable->getActiveImagesMask() |
+ 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.reset(nullptr);
+}
+
+void ProgramPipeline::onDestroy(const Context *context)
+{
+ for (Program *program : mState.mPrograms)
+ {
+ if (program)
+ {
+ ASSERT(program->getRefCount());
+ program->release(context);
+ }
+ }
+
+ getImplementation()->destroy(context);
+}
+
+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)
+{
+ bool needToUpdatePipelineState = false;
+ gl::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());
+
+ for (ShaderType shaderType : shaderTypes)
+ {
+ if (mState.getShaderProgram(shaderType) != shaderProgram ||
+ (shaderProgram && shaderProgram->hasAnyDirtyBit()))
+ {
+ needToUpdatePipelineState = true;
+ break;
+ }
+ }
+
+ if (!needToUpdatePipelineState)
+ {
+ return angle::Result::Continue;
+ }
+
+ mState.useProgramStages(context, shaderTypes, shaderProgram, &mProgramObserverBindings);
+ updateLinkedShaderStages();
+
+ mState.mIsLinked = false;
+ onStateChange(angle::SubjectMessage::SubjectChanged);
+
+ return angle::Result::Continue;
+}
+
+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;
+ mState.mExecutable->mProgramInputs = vertexExecutable.mProgramInputs;
+}
+
+void ProgramPipeline::updateTransformFeedbackMembers()
+{
+ ShaderType lastVertexProcessingStage =
+ gl::GetLastPreFragmentStage(getExecutable().getLinkedShaderStages());
+ if (lastVertexProcessingStage == ShaderType::InvalidEnum)
+ {
+ return;
+ }
+
+ Program *shaderProgram = getShaderProgram(lastVertexProcessingStage);
+ ASSERT(shaderProgram);
+
+ const ProgramExecutable &lastPreFragmentExecutable = shaderProgram->getExecutable();
+ 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 gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ 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->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 gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ 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->mImageBindings.emplace_back(imageBinding);
+ }
+
+ mState.mExecutable->updateActiveImages(shaderProgram->getExecutable());
+ }
+ }
+}
+
+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::updateFragmentInoutRangeAndEnablesPerSampleShading()
+{
+ Program *fragmentProgram = getShaderProgram(gl::ShaderType::Fragment);
+
+ if (!fragmentProgram)
+ {
+ return;
+ }
+
+ const ProgramExecutable &fragmentExecutable = fragmentProgram->getExecutable();
+ mState.mExecutable->mFragmentInoutRange = fragmentExecutable.mFragmentInoutRange;
+ mState.mExecutable->mHasDiscard = fragmentExecutable.mHasDiscard;
+ mState.mExecutable->mEnablesPerSampleShading = fragmentExecutable.mEnablesPerSampleShading;
+}
+
+void ProgramPipeline::updateLinkedVaryings()
+{
+ // 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 && shaderProgram->isLinked())
+ {
+ const ProgramExecutable &executable = shaderProgram->getExecutable();
+ mState.mExecutable->mLinkedOutputVaryings[shaderType] =
+ executable.getLinkedOutputVaryings(shaderType);
+ mState.mExecutable->mLinkedInputVaryings[shaderType] =
+ executable.getLinkedInputVaryings(shaderType);
+ }
+ }
+
+ const Program *computeProgram = getShaderProgram(ShaderType::Compute);
+ if (computeProgram && computeProgram->isLinked())
+ {
+ const ProgramExecutable &executable = computeProgram->getExecutable();
+ mState.mExecutable->mLinkedOutputVaryings[ShaderType::Compute] =
+ executable.getLinkedOutputVaryings(ShaderType::Compute);
+ mState.mExecutable->mLinkedInputVaryings[ShaderType::Compute] =
+ executable.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();
+ updateLinkedVaryings();
+}
+
+// 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)
+{
+ ASSERT(!mState.mIsLinked);
+
+ ProgramMergedVaryings mergedVaryings;
+ ProgramVaryingPacking varyingPacking;
+ LinkingVariables linkingVariables(mState);
+
+ mState.mExecutable->reset(true);
+
+ InfoLog &infoLog = mState.mExecutable->getInfoLog();
+ infoLog.reset();
+
+ // Build shader variable uniforms map for gl::UniformLinker.
+ ShaderMap<std::vector<sh::ShaderVariable>> shaderUniforms;
+ for (ShaderType shaderType : mState.mExecutable->mLinkedShaderStages)
+ {
+ for (const LinkedUniform &uniform : mState.mPrograms[shaderType]->getUniforms())
+ {
+ shaderUniforms[shaderType].push_back(uniform);
+ }
+ }
+
+ if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::Vertex))
+ {
+ if (!linkVaryings(infoLog))
+ {
+ return angle::Result::Stop;
+ }
+
+ if (!LinkValidateProgramGlobalNames(infoLog, getExecutable(), linkingVariables))
+ {
+ return angle::Result::Stop;
+ }
+
+ Program *fragmentShaderProgram = getShaderProgram(ShaderType::Fragment);
+ if (fragmentShaderProgram)
+ {
+ // We should also be validating SSBO and image uniform counts.
+ const GLuint combinedImageUniforms = 0;
+ const GLuint combinedShaderStorageBlocks = 0;
+ const ProgramExecutable &fragmentExecutable = fragmentShaderProgram->getExecutable();
+ if (!mState.mExecutable->linkValidateOutputVariables(
+ context->getCaps(), context->getExtensions(), context->getClientVersion(),
+ combinedImageUniforms, combinedShaderStorageBlocks,
+ fragmentExecutable.getOutputVariables(),
+ 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 =
+ gl::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;
+ }
+
+ Program *tfProgram = getShaderProgram(lastVertexProcessingStage);
+ ASSERT(tfProgram);
+
+ if (!tfProgram)
+ {
+ tfProgram = mState.mPrograms[ShaderType::Vertex];
+ }
+
+ const std::vector<std::string> &transformFeedbackVaryingNames =
+ tfProgram->getState().getTransformFeedbackVaryingNames();
+
+ if (!mState.mExecutable->linkMergedVaryings(context, mergedVaryings,
+ transformFeedbackVaryingNames, linkingVariables,
+ false, &varyingPacking))
+ {
+ return angle::Result::Stop;
+ }
+ }
+
+ // Merge uniforms.
+ mState.mExecutable->copyUniformsFromProgramMap(mState.mPrograms);
+
+ if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::Vertex))
+ {
+ const ProgramState &programState = mState.mPrograms[gl::ShaderType::Vertex]->getState();
+ mState.mExecutable->copyInputsFromProgram(programState);
+ }
+
+ // 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 ProgramState &programState = mState.mPrograms[shaderType]->getState();
+ mState.mExecutable->copyShaderBuffersFromProgram(programState, shaderType);
+ mState.mExecutable->copySamplerBindingsFromProgram(programState);
+ mState.mExecutable->copyImageBindingsFromProgram(programState);
+ }
+
+ if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::Fragment))
+ {
+ const ProgramState &programState = mState.mPrograms[gl::ShaderType::Fragment]->getState();
+ mState.mExecutable->copyOutputsFromProgram(programState);
+ }
+
+ if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::Vertex) ||
+ mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::Compute))
+ {
+ ANGLE_TRY(getImplementation()->link(context, mergedVaryings, varyingPacking));
+ }
+
+ mState.mExecutable->mActiveSamplerRefCounts.fill(0);
+ updateExecutable();
+
+ mState.mIsLinked = true;
+ onStateChange(angle::SubjectMessage::SubjectChanged);
+
+ return angle::Result::Continue;
+}
+
+bool ProgramPipeline::linkVaryings(InfoLog &infoLog) const
+{
+ ShaderType previousShaderType = ShaderType::InvalidEnum;
+ for (ShaderType shaderType : kAllGraphicsShaderTypes)
+ {
+ Program *program = getShaderProgram(shaderType);
+ if (!program)
+ {
+ continue;
+ }
+ ProgramExecutable &executable = program->getExecutable();
+
+ if (previousShaderType != ShaderType::InvalidEnum)
+ {
+ Program *previousProgram = getShaderProgram(previousShaderType);
+ ASSERT(previousProgram);
+ const 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 true;
+ }
+ 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;
+ }
+ }
+ }
+
+ intptr_t programPipelineError = context->getStateCache().getProgramPipelineError(context);
+ if (programPipelineError)
+ {
+ mState.mValid = false;
+ const char *errorMessage = reinterpret_cast<const char *>(programPipelineError);
+ infoLog << errorMessage << "\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";
+ }
+ }
+ }
+}
+
+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::ProgramRelinked:
+ mState.mIsLinked = false;
+ onStateChange(angle::SubjectMessage::ProgramRelinked);
+ break;
+ case angle::SubjectMessage::SamplerUniformsUpdated:
+ mState.mExecutable->clearSamplerBindings();
+ for (ShaderType shaderType : mState.mExecutable->getLinkedShaderStages())
+ {
+ const ProgramState &programState = mState.mPrograms[shaderType]->getState();
+ mState.mExecutable->copySamplerBindingsFromProgram(programState);
+ }
+ mState.mExecutable->mActiveSamplerRefCounts.fill(0);
+ mState.updateExecutableTextures();
+ break;
+ case angle::SubjectMessage::ProgramUniformUpdated:
+ mProgramPipelineImpl->onProgramUniformUpdate(static_cast<ShaderType>(index));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+} // namespace gl