blob: c29a36ee615c845db0816c7d6960ea87ba2f6e3b [file] [log] [blame]
//
// 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);
}
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 (!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