blob: 3cf47ba524be81982165b17855a03ba999217db8 [file] [log] [blame]
//
// Copyright 2020 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.
//
// ProgramExecutable.cpp: Collects the interfaces common to both Programs and
// ProgramPipelines in order to execute/draw with either.
#include "libANGLE/ProgramExecutable.h"
#include "common/string_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/Program.h"
#include "libANGLE/Shader.h"
namespace gl
{
namespace
{
bool IncludeSameArrayElement(const std::set<std::string> &nameSet, const std::string &name)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(name, &subscripts);
for (const std::string &nameInSet : nameSet)
{
std::vector<unsigned int> arrayIndices;
std::string arrayName = ParseResourceName(nameInSet, &arrayIndices);
if (baseName == arrayName &&
(subscripts.empty() || arrayIndices.empty() || subscripts == arrayIndices))
{
return true;
}
}
return false;
}
// Find the matching varying or field by name.
const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &varyings,
ShaderType stage,
const std::string &name)
{
const sh::ShaderVariable *var = nullptr;
for (const ProgramVaryingRef &ref : varyings)
{
if (ref.frontShaderStage != stage)
{
continue;
}
const sh::ShaderVariable *varying = ref.get(stage);
if (varying->name == name)
{
var = varying;
break;
}
GLuint fieldIndex = 0;
var = varying->findField(name, &fieldIndex);
if (var != nullptr)
{
break;
}
}
return var;
}
bool FindUsedOutputLocation(std::vector<VariableLocation> &outputLocations,
unsigned int baseLocation,
unsigned int elementCount,
const std::vector<VariableLocation> &reservedLocations,
unsigned int variableIndex)
{
if (baseLocation + elementCount > outputLocations.size())
{
elementCount = baseLocation < outputLocations.size()
? static_cast<unsigned int>(outputLocations.size() - baseLocation)
: 0;
}
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
{
const unsigned int location = baseLocation + elementIndex;
if (outputLocations[location].used())
{
VariableLocation locationInfo(elementIndex, variableIndex);
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
reservedLocations.end())
{
return true;
}
}
}
return false;
}
void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
unsigned int baseLocation,
unsigned int elementCount,
const std::vector<VariableLocation> &reservedLocations,
unsigned int variableIndex,
sh::ShaderVariable &outputVariable)
{
if (baseLocation + elementCount > outputLocations.size())
{
outputLocations.resize(baseLocation + elementCount);
}
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
{
VariableLocation locationInfo(elementIndex, variableIndex);
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
reservedLocations.end())
{
outputVariable.location = baseLocation;
const unsigned int location = baseLocation + elementIndex;
outputLocations[location] = locationInfo;
}
}
}
int GetOutputLocationForLink(const ProgramAliasedBindings &fragmentOutputLocations,
const sh::ShaderVariable &outputVariable)
{
if (outputVariable.location != -1)
{
return outputVariable.location;
}
int apiLocation = fragmentOutputLocations.getBinding(outputVariable);
if (apiLocation != -1)
{
return apiLocation;
}
return -1;
}
bool IsOutputSecondaryForLink(const ProgramAliasedBindings &fragmentOutputIndexes,
const sh::ShaderVariable &outputVariable)
{
if (outputVariable.index != -1)
{
ASSERT(outputVariable.index == 0 || outputVariable.index == 1);
return (outputVariable.index == 1);
}
int apiIndex = fragmentOutputIndexes.getBinding(outputVariable);
if (apiIndex != -1)
{
// Index layout qualifier from the shader takes precedence, so the index from the API is
// checked only if the index was not set in the shader. This is not specified in the EXT
// spec, but is specified in desktop OpenGL specs.
return (apiIndex == 1);
}
// EXT_blend_func_extended: Outputs get index 0 by default.
return false;
}
RangeUI AddUniforms(const ShaderMap<Program *> &programs,
ShaderBitSet activeShaders,
std::vector<LinkedUniform> &outputUniforms,
const std::function<RangeUI(const ProgramState &)> &getRange)
{
unsigned int startRange = static_cast<unsigned int>(outputUniforms.size());
for (ShaderType shaderType : activeShaders)
{
const ProgramState &programState = programs[shaderType]->getState();
const std::vector<LinkedUniform> &programUniforms = programState.getUniforms();
const RangeUI uniformRange = getRange(programState);
outputUniforms.insert(outputUniforms.end(), programUniforms.begin() + uniformRange.low(),
programUniforms.begin() + uniformRange.high());
}
return RangeUI(startRange, static_cast<unsigned int>(outputUniforms.size()));
}
template <typename BlockT>
void AppendActiveBlocks(ShaderType shaderType,
const std::vector<BlockT> &blocksIn,
std::vector<BlockT> &blocksOut)
{
for (const BlockT &block : blocksIn)
{
if (block.isActive(shaderType))
{
blocksOut.push_back(block);
}
}
}
} // anonymous namespace
ProgramExecutable::ProgramExecutable()
: mMaxActiveAttribLocation(0),
mAttributesTypeMask(0),
mAttributesMask(0),
mActiveSamplerRefCounts{},
mCanDrawWith(false),
mYUVOutput(false),
mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
mDefaultUniformRange(0, 0),
mSamplerUniformRange(0, 0),
mImageUniformRange(0, 0),
mAtomicCounterUniformRange(0, 0),
mFragmentInoutRange(0, 0),
mEnablesPerSampleShading(false),
// [GL_EXT_geometry_shader] Table 20.22
mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles),
mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip),
mGeometryShaderInvocations(1),
mGeometryShaderMaxVertices(0),
mTessControlShaderVertices(0),
mTessGenMode(GL_NONE),
mTessGenSpacing(GL_NONE),
mTessGenVertexOrder(GL_NONE),
mTessGenPointMode(GL_NONE)
{
reset(true);
}
ProgramExecutable::ProgramExecutable(const ProgramExecutable &other)
: mLinkedShaderStages(other.mLinkedShaderStages),
mActiveAttribLocationsMask(other.mActiveAttribLocationsMask),
mMaxActiveAttribLocation(other.mMaxActiveAttribLocation),
mAttributesTypeMask(other.mAttributesTypeMask),
mAttributesMask(other.mAttributesMask),
mActiveSamplersMask(other.mActiveSamplersMask),
mActiveSamplerRefCounts(other.mActiveSamplerRefCounts),
mActiveSamplerTypes(other.mActiveSamplerTypes),
mActiveSamplerYUV(other.mActiveSamplerYUV),
mActiveSamplerFormats(other.mActiveSamplerFormats),
mActiveSamplerShaderBits(other.mActiveSamplerShaderBits),
mActiveImagesMask(other.mActiveImagesMask),
mActiveImageShaderBits(other.mActiveImageShaderBits),
mCanDrawWith(other.mCanDrawWith),
mOutputVariables(other.mOutputVariables),
mOutputLocations(other.mOutputLocations),
mSecondaryOutputLocations(other.mSecondaryOutputLocations),
mYUVOutput(other.mYUVOutput),
mProgramInputs(other.mProgramInputs),
mLinkedTransformFeedbackVaryings(other.mLinkedTransformFeedbackVaryings),
mTransformFeedbackStrides(other.mTransformFeedbackStrides),
mTransformFeedbackBufferMode(other.mTransformFeedbackBufferMode),
mUniforms(other.mUniforms),
mDefaultUniformRange(other.mDefaultUniformRange),
mSamplerUniformRange(other.mSamplerUniformRange),
mImageUniformRange(other.mImageUniformRange),
mAtomicCounterUniformRange(other.mAtomicCounterUniformRange),
mUniformBlocks(other.mUniformBlocks),
mActiveUniformBlockBindings(other.mActiveUniformBlockBindings),
mAtomicCounterBuffers(other.mAtomicCounterBuffers),
mShaderStorageBlocks(other.mShaderStorageBlocks),
mFragmentInoutRange(other.mFragmentInoutRange),
mEnablesPerSampleShading(other.mEnablesPerSampleShading),
mAdvancedBlendEquations(other.mAdvancedBlendEquations)
{
reset(true);
}
ProgramExecutable::~ProgramExecutable() = default;
void ProgramExecutable::reset(bool clearInfoLog)
{
if (clearInfoLog)
{
resetInfoLog();
}
mActiveAttribLocationsMask.reset();
mAttributesTypeMask.reset();
mAttributesMask.reset();
mMaxActiveAttribLocation = 0;
mActiveSamplersMask.reset();
mActiveSamplerRefCounts = {};
mActiveSamplerTypes.fill(TextureType::InvalidEnum);
mActiveSamplerYUV.reset();
mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum);
mActiveImagesMask.reset();
mProgramInputs.clear();
mLinkedTransformFeedbackVaryings.clear();
mTransformFeedbackStrides.clear();
mUniforms.clear();
mUniformBlocks.clear();
mActiveUniformBlockBindings.reset();
mShaderStorageBlocks.clear();
mAtomicCounterBuffers.clear();
mOutputVariables.clear();
mOutputLocations.clear();
mActiveOutputVariablesMask.reset();
mSecondaryOutputLocations.clear();
mYUVOutput = false;
mSamplerBindings.clear();
mImageBindings.clear();
mDefaultUniformRange = RangeUI(0, 0);
mSamplerUniformRange = RangeUI(0, 0);
mImageUniformRange = RangeUI(0, 0);
mAtomicCounterUniformRange = RangeUI(0, 0);
mFragmentInoutRange = RangeUI(0, 0);
mEnablesPerSampleShading = false;
mAdvancedBlendEquations.reset();
mGeometryShaderInputPrimitiveType = PrimitiveMode::Triangles;
mGeometryShaderOutputPrimitiveType = PrimitiveMode::TriangleStrip;
mGeometryShaderInvocations = 1;
mGeometryShaderMaxVertices = 0;
mTessControlShaderVertices = 0;
mTessGenMode = GL_NONE;
mTessGenSpacing = GL_NONE;
mTessGenVertexOrder = GL_NONE;
mTessGenPointMode = GL_NONE;
mOutputVariableTypes.clear();
mDrawBufferTypeMask.reset();
}
void ProgramExecutable::load(bool isSeparable, gl::BinaryInputStream *stream)
{
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"Too many vertex attribs for mask: All bits of mAttributesTypeMask types and "
"mask fit into 32 bits each");
mAttributesTypeMask = gl::ComponentTypeMask(stream->readInt<uint32_t>());
mAttributesMask = gl::AttributesMask(stream->readInt<uint32_t>());
mActiveAttribLocationsMask = gl::AttributesMask(stream->readInt<uint32_t>());
mMaxActiveAttribLocation = stream->readInt<unsigned int>();
unsigned int fragmentInoutRangeLow = stream->readInt<uint32_t>();
unsigned int fragmentInoutRangeHigh = stream->readInt<uint32_t>();
mFragmentInoutRange = RangeUI(fragmentInoutRangeLow, fragmentInoutRangeHigh);
mEnablesPerSampleShading = stream->readBool();
static_assert(sizeof(mAdvancedBlendEquations.bits()) == sizeof(uint32_t));
mAdvancedBlendEquations = BlendEquationBitSet(stream->readInt<uint32_t>());
mLinkedShaderStages = ShaderBitSet(stream->readInt<uint8_t>());
mGeometryShaderInputPrimitiveType = stream->readEnum<PrimitiveMode>();
mGeometryShaderOutputPrimitiveType = stream->readEnum<PrimitiveMode>();
mGeometryShaderInvocations = stream->readInt<int>();
mGeometryShaderMaxVertices = stream->readInt<int>();
mTessControlShaderVertices = stream->readInt<int>();
mTessGenMode = stream->readInt<GLenum>();
mTessGenSpacing = stream->readInt<GLenum>();
mTessGenVertexOrder = stream->readInt<GLenum>();
mTessGenPointMode = stream->readInt<GLenum>();
size_t attribCount = stream->readInt<size_t>();
ASSERT(getProgramInputs().empty());
for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex)
{
sh::ShaderVariable attrib;
LoadShaderVar(stream, &attrib);
attrib.location = stream->readInt<int>();
mProgramInputs.push_back(attrib);
}
size_t uniformCount = stream->readInt<size_t>();
ASSERT(getUniforms().empty());
for (size_t uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
{
LinkedUniform uniform;
LoadShaderVar(stream, &uniform);
uniform.bufferIndex = stream->readInt<int>();
LoadBlockMemberInfo(stream, &uniform.blockInfo);
stream->readIntVector<unsigned int>(&uniform.outerArraySizes);
uniform.outerArrayOffset = stream->readInt<unsigned int>();
uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
// Active shader info
for (ShaderType shaderType : gl::AllShaderTypes())
{
uniform.setActive(shaderType, stream->readBool());
}
mUniforms.push_back(uniform);
}
size_t uniformBlockCount = stream->readInt<size_t>();
ASSERT(getUniformBlocks().empty());
for (size_t uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
{
InterfaceBlock uniformBlock;
LoadInterfaceBlock(stream, &uniformBlock);
mUniformBlocks.push_back(uniformBlock);
mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
}
size_t shaderStorageBlockCount = stream->readInt<size_t>();
ASSERT(getShaderStorageBlocks().empty());
for (size_t shaderStorageBlockIndex = 0; shaderStorageBlockIndex < shaderStorageBlockCount;
++shaderStorageBlockIndex)
{
InterfaceBlock shaderStorageBlock;
LoadInterfaceBlock(stream, &shaderStorageBlock);
mShaderStorageBlocks.push_back(shaderStorageBlock);
}
size_t atomicCounterBufferCount = stream->readInt<size_t>();
ASSERT(getAtomicCounterBuffers().empty());
for (size_t bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
{
AtomicCounterBuffer atomicCounterBuffer;
LoadShaderVariableBuffer(stream, &atomicCounterBuffer);
mAtomicCounterBuffers.push_back(atomicCounterBuffer);
}
size_t transformFeedbackVaryingCount = stream->readInt<size_t>();
ASSERT(mLinkedTransformFeedbackVaryings.empty());
for (size_t transformFeedbackVaryingIndex = 0;
transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
++transformFeedbackVaryingIndex)
{
sh::ShaderVariable varying;
stream->readIntVector<unsigned int>(&varying.arraySizes);
stream->readInt(&varying.type);
stream->readString(&varying.name);
GLuint arrayIndex = stream->readInt<GLuint>();
mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
}
mTransformFeedbackBufferMode = stream->readInt<GLint>();
size_t outputCount = stream->readInt<size_t>();
ASSERT(getOutputVariables().empty());
for (size_t outputIndex = 0; outputIndex < outputCount; ++outputIndex)
{
sh::ShaderVariable output;
LoadShaderVar(stream, &output);
output.location = stream->readInt<int>();
output.index = stream->readInt<int>();
mOutputVariables.push_back(output);
}
size_t outputVarCount = stream->readInt<size_t>();
ASSERT(getOutputLocations().empty());
for (size_t outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
{
VariableLocation locationData;
stream->readInt(&locationData.arrayIndex);
stream->readInt(&locationData.index);
stream->readBool(&locationData.ignored);
mOutputLocations.push_back(locationData);
}
mActiveOutputVariablesMask =
gl::DrawBufferMask(stream->readInt<gl::DrawBufferMask::value_type>());
size_t outputTypeCount = stream->readInt<size_t>();
for (size_t outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
{
mOutputVariableTypes.push_back(stream->readInt<GLenum>());
}
static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
"All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
"into 32 bits each");
mDrawBufferTypeMask = gl::ComponentTypeMask(stream->readInt<uint32_t>());
stream->readBool(&mYUVOutput);
size_t secondaryOutputVarCount = stream->readInt<size_t>();
ASSERT(getSecondaryOutputLocations().empty());
for (size_t outputIndex = 0; outputIndex < secondaryOutputVarCount; ++outputIndex)
{
VariableLocation locationData;
stream->readInt(&locationData.arrayIndex);
stream->readInt(&locationData.index);
stream->readBool(&locationData.ignored);
mSecondaryOutputLocations.push_back(locationData);
}
unsigned int defaultUniformRangeLow = stream->readInt<unsigned int>();
unsigned int defaultUniformRangeHigh = stream->readInt<unsigned int>();
mDefaultUniformRange = RangeUI(defaultUniformRangeLow, defaultUniformRangeHigh);
unsigned int samplerRangeLow = stream->readInt<unsigned int>();
unsigned int samplerRangeHigh = stream->readInt<unsigned int>();
mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
size_t samplerCount = stream->readInt<size_t>();
for (size_t samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
{
TextureType textureType = stream->readEnum<TextureType>();
GLenum samplerType = stream->readInt<GLenum>();
SamplerFormat format = stream->readEnum<SamplerFormat>();
size_t bindingCount = stream->readInt<size_t>();
mSamplerBindings.emplace_back(textureType, samplerType, format, bindingCount);
}
unsigned int imageRangeLow = stream->readInt<unsigned int>();
unsigned int imageRangeHigh = stream->readInt<unsigned int>();
mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
size_t imageBindingCount = stream->readInt<size_t>();
for (size_t imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
{
size_t elementCount = stream->readInt<size_t>();
TextureType textureType = static_cast<TextureType>(stream->readInt<unsigned int>());
ImageBinding imageBinding(elementCount, textureType);
for (size_t elementIndex = 0; elementIndex < elementCount; ++elementIndex)
{
imageBinding.boundImageUnits[elementIndex] = stream->readInt<unsigned int>();
}
mImageBindings.emplace_back(imageBinding);
}
unsigned int atomicCounterRangeLow = stream->readInt<unsigned int>();
unsigned int atomicCounterRangeHigh = stream->readInt<unsigned int>();
mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
// These values are currently only used by PPOs, so only load them when the program is marked
// separable to save memory.
if (isSeparable)
{
for (ShaderType shaderType : mLinkedShaderStages)
{
mLinkedOutputVaryings[shaderType].resize(stream->readInt<size_t>());
for (sh::ShaderVariable &variable : mLinkedOutputVaryings[shaderType])
{
LoadShaderVar(stream, &variable);
}
mLinkedInputVaryings[shaderType].resize(stream->readInt<size_t>());
for (sh::ShaderVariable &variable : mLinkedInputVaryings[shaderType])
{
LoadShaderVar(stream, &variable);
}
mLinkedUniforms[shaderType].resize(stream->readInt<size_t>());
for (sh::ShaderVariable &variable : mLinkedUniforms[shaderType])
{
LoadShaderVar(stream, &variable);
}
mLinkedUniformBlocks[shaderType].resize(stream->readInt<size_t>());
for (sh::InterfaceBlock &shaderStorageBlock : mLinkedUniformBlocks[shaderType])
{
LoadShInterfaceBlock(stream, &shaderStorageBlock);
}
mLinkedShaderVersions[shaderType] = stream->readInt<int>();
}
}
}
void ProgramExecutable::save(bool isSeparable, gl::BinaryOutputStream *stream) const
{
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"All bits of mAttributesTypeMask types and mask fit into 32 bits each");
stream->writeInt(static_cast<uint32_t>(mAttributesTypeMask.to_ulong()));
stream->writeInt(static_cast<uint32_t>(mAttributesMask.to_ulong()));
stream->writeInt(static_cast<uint32_t>(mActiveAttribLocationsMask.to_ulong()));
stream->writeInt(mMaxActiveAttribLocation);
stream->writeInt(mFragmentInoutRange.low());
stream->writeInt(mFragmentInoutRange.high());
stream->writeBool(mEnablesPerSampleShading);
stream->writeInt(mAdvancedBlendEquations.bits());
stream->writeInt(mLinkedShaderStages.bits());
ASSERT(mGeometryShaderInvocations >= 1 && mGeometryShaderMaxVertices >= 0);
stream->writeEnum(mGeometryShaderInputPrimitiveType);
stream->writeEnum(mGeometryShaderOutputPrimitiveType);
stream->writeInt(mGeometryShaderInvocations);
stream->writeInt(mGeometryShaderMaxVertices);
stream->writeInt(mTessControlShaderVertices);
stream->writeInt(mTessGenMode);
stream->writeInt(mTessGenSpacing);
stream->writeInt(mTessGenVertexOrder);
stream->writeInt(mTessGenPointMode);
stream->writeInt(getProgramInputs().size());
for (const sh::ShaderVariable &attrib : getProgramInputs())
{
WriteShaderVar(stream, attrib);
stream->writeInt(attrib.location);
}
stream->writeInt(getUniforms().size());
for (const LinkedUniform &uniform : getUniforms())
{
WriteShaderVar(stream, uniform);
stream->writeInt(uniform.bufferIndex);
WriteBlockMemberInfo(stream, uniform.blockInfo);
stream->writeIntVector(uniform.outerArraySizes);
stream->writeInt(uniform.outerArrayOffset);
// Active shader info
for (ShaderType shaderType : gl::AllShaderTypes())
{
stream->writeBool(uniform.isActive(shaderType));
}
}
stream->writeInt(getUniformBlocks().size());
for (const InterfaceBlock &uniformBlock : getUniformBlocks())
{
WriteInterfaceBlock(stream, uniformBlock);
}
stream->writeInt(getShaderStorageBlocks().size());
for (const InterfaceBlock &shaderStorageBlock : getShaderStorageBlocks())
{
WriteInterfaceBlock(stream, shaderStorageBlock);
}
stream->writeInt(mAtomicCounterBuffers.size());
for (const AtomicCounterBuffer &atomicCounterBuffer : getAtomicCounterBuffers())
{
WriteShaderVariableBuffer(stream, atomicCounterBuffer);
}
stream->writeInt(getLinkedTransformFeedbackVaryings().size());
for (const auto &var : getLinkedTransformFeedbackVaryings())
{
stream->writeIntVector(var.arraySizes);
stream->writeInt(var.type);
stream->writeString(var.name);
stream->writeIntOrNegOne(var.arrayIndex);
}
stream->writeInt(getTransformFeedbackBufferMode());
stream->writeInt(getOutputVariables().size());
for (const sh::ShaderVariable &output : getOutputVariables())
{
WriteShaderVar(stream, output);
stream->writeInt(output.location);
stream->writeInt(output.index);
}
stream->writeInt(getOutputLocations().size());
for (const auto &outputVar : getOutputLocations())
{
stream->writeInt(outputVar.arrayIndex);
stream->writeIntOrNegOne(outputVar.index);
stream->writeBool(outputVar.ignored);
}
stream->writeInt(static_cast<int>(mActiveOutputVariablesMask.to_ulong()));
stream->writeInt(mOutputVariableTypes.size());
for (const auto &outputVariableType : mOutputVariableTypes)
{
stream->writeInt(outputVariableType);
}
static_assert(
IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
"All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
stream->writeInt(static_cast<int>(mDrawBufferTypeMask.to_ulong()));
stream->writeBool(mYUVOutput);
stream->writeInt(getSecondaryOutputLocations().size());
for (const auto &outputVar : getSecondaryOutputLocations())
{
stream->writeInt(outputVar.arrayIndex);
stream->writeIntOrNegOne(outputVar.index);
stream->writeBool(outputVar.ignored);
}
stream->writeInt(getDefaultUniformRange().low());
stream->writeInt(getDefaultUniformRange().high());
stream->writeInt(getSamplerUniformRange().low());
stream->writeInt(getSamplerUniformRange().high());
stream->writeInt(getSamplerBindings().size());
for (const auto &samplerBinding : getSamplerBindings())
{
stream->writeEnum(samplerBinding.textureType);
stream->writeInt(samplerBinding.samplerType);
stream->writeEnum(samplerBinding.format);
stream->writeInt(samplerBinding.boundTextureUnits.size());
}
stream->writeInt(getImageUniformRange().low());
stream->writeInt(getImageUniformRange().high());
stream->writeInt(getImageBindings().size());
for (const auto &imageBinding : getImageBindings())
{
stream->writeInt(imageBinding.boundImageUnits.size());
stream->writeInt(static_cast<unsigned int>(imageBinding.textureType));
for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
{
stream->writeInt(imageBinding.boundImageUnits[i]);
}
}
stream->writeInt(getAtomicCounterUniformRange().low());
stream->writeInt(getAtomicCounterUniformRange().high());
// These values are currently only used by PPOs, so only save them when the program is marked
// separable to save memory.
if (isSeparable)
{
for (ShaderType shaderType : mLinkedShaderStages)
{
stream->writeInt(mLinkedOutputVaryings[shaderType].size());
for (const sh::ShaderVariable &shaderVariable : mLinkedOutputVaryings[shaderType])
{
WriteShaderVar(stream, shaderVariable);
}
stream->writeInt(mLinkedInputVaryings[shaderType].size());
for (const sh::ShaderVariable &shaderVariable : mLinkedInputVaryings[shaderType])
{
WriteShaderVar(stream, shaderVariable);
}
stream->writeInt(mLinkedUniforms[shaderType].size());
for (const sh::ShaderVariable &shaderVariable : mLinkedUniforms[shaderType])
{
WriteShaderVar(stream, shaderVariable);
}
stream->writeInt(mLinkedUniformBlocks[shaderType].size());
for (const sh::InterfaceBlock &shaderStorageBlock : mLinkedUniformBlocks[shaderType])
{
WriteShInterfaceBlock(stream, shaderStorageBlock);
}
stream->writeInt(mLinkedShaderVersions[shaderType]);
}
}
}
int ProgramExecutable::getInfoLogLength() const
{
return static_cast<int>(mInfoLog.getLength());
}
void ProgramExecutable::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const
{
return mInfoLog.getLog(bufSize, length, infoLog);
}
std::string ProgramExecutable::getInfoLogString() const
{
return mInfoLog.str();
}
bool ProgramExecutable::isAttribLocationActive(size_t attribLocation) const
{
ASSERT(attribLocation < mActiveAttribLocationsMask.size());
return mActiveAttribLocationsMask[attribLocation];
}
AttributesMask ProgramExecutable::getAttributesMask() const
{
return mAttributesMask;
}
bool ProgramExecutable::hasDefaultUniforms() const
{
return !getDefaultUniformRange().empty();
}
bool ProgramExecutable::hasTextures() const
{
return !getSamplerBindings().empty();
}
bool ProgramExecutable::hasUniformBuffers() const
{
return !mUniformBlocks.empty();
}
bool ProgramExecutable::hasStorageBuffers() const
{
return !mShaderStorageBlocks.empty();
}
bool ProgramExecutable::hasAtomicCounterBuffers() const
{
return !mAtomicCounterBuffers.empty();
}
bool ProgramExecutable::hasImages() const
{
return !mImageBindings.empty();
}
bool ProgramExecutable::usesFramebufferFetch() const
{
return (mFragmentInoutRange.length() > 0);
}
GLuint ProgramExecutable::getUniformIndexFromImageIndex(GLuint imageIndex) const
{
ASSERT(imageIndex < mImageUniformRange.length());
return imageIndex + mImageUniformRange.low();
}
GLuint ProgramExecutable::getUniformIndexFromSamplerIndex(GLuint samplerIndex) const
{
ASSERT(samplerIndex < mSamplerUniformRange.length());
return samplerIndex + mSamplerUniformRange.low();
}
void ProgramExecutable::setActive(size_t textureUnit,
const SamplerBinding &samplerBinding,
const gl::LinkedUniform &samplerUniform)
{
mActiveSamplersMask.set(textureUnit);
mActiveSamplerTypes[textureUnit] = samplerBinding.textureType;
mActiveSamplerYUV[textureUnit] = IsSamplerYUVType(samplerBinding.samplerType);
mActiveSamplerFormats[textureUnit] = samplerBinding.format;
mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders();
}
void ProgramExecutable::setInactive(size_t textureUnit)
{
mActiveSamplersMask.reset(textureUnit);
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
mActiveSamplerYUV.reset(textureUnit);
mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
mActiveSamplerShaderBits[textureUnit].reset();
}
void ProgramExecutable::hasSamplerTypeConflict(size_t textureUnit)
{
// Conflicts are marked with InvalidEnum
mActiveSamplerYUV.reset(textureUnit);
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
}
void ProgramExecutable::hasSamplerFormatConflict(size_t textureUnit)
{
// Conflicts are marked with InvalidEnum
mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
}
void ProgramExecutable::updateActiveSamplers(const ProgramState &programState)
{
const std::vector<SamplerBinding> &samplerBindings = programState.getSamplerBindings();
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
for (GLint textureUnit : samplerBinding.boundTextureUnits)
{
if (++mActiveSamplerRefCounts[textureUnit] == 1)
{
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(samplerIndex);
setActive(textureUnit, samplerBinding, programState.getUniforms()[uniformIndex]);
}
else
{
if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType ||
mActiveSamplerYUV.test(textureUnit) !=
IsSamplerYUVType(samplerBinding.samplerType))
{
hasSamplerTypeConflict(textureUnit);
}
if (mActiveSamplerFormats[textureUnit] != samplerBinding.format)
{
hasSamplerFormatConflict(textureUnit);
}
}
mActiveSamplersMask.set(textureUnit);
}
}
// Invalidate the validation cache.
resetCachedValidateSamplersResult();
}
void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable)
{
const std::vector<ImageBinding> &imageBindings = executable.getImageBindings();
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings.at(imageIndex);
uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = executable.getUniforms()[uniformIndex];
const ShaderBitSet shaderBits = imageUniform.activeShaders();
for (GLint imageUnit : imageBinding.boundImageUnits)
{
mActiveImagesMask.set(imageUnit);
mActiveImageShaderBits[imageUnit] |= shaderBits;
}
}
}
void ProgramExecutable::setSamplerUniformTextureTypeAndFormat(
size_t textureUnitIndex,
std::vector<SamplerBinding> &samplerBindings)
{
bool foundBinding = false;
TextureType foundType = TextureType::InvalidEnum;
bool foundYUV = false;
SamplerFormat foundFormat = SamplerFormat::InvalidEnum;
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const SamplerBinding &binding = samplerBindings[samplerIndex];
// A conflict exists if samplers of different types are sourced by the same texture unit.
// We need to check all bound textures to detect this error case.
for (GLuint textureUnit : binding.boundTextureUnits)
{
if (textureUnit != textureUnitIndex)
{
continue;
}
if (!foundBinding)
{
foundBinding = true;
foundType = binding.textureType;
foundYUV = IsSamplerYUVType(binding.samplerType);
foundFormat = binding.format;
uint32_t uniformIndex = getUniformIndexFromSamplerIndex(samplerIndex);
setActive(textureUnit, binding, mUniforms[uniformIndex]);
}
else
{
if (foundType != binding.textureType ||
foundYUV != IsSamplerYUVType(binding.samplerType))
{
hasSamplerTypeConflict(textureUnit);
}
if (foundFormat != binding.format)
{
hasSamplerFormatConflict(textureUnit);
}
}
}
}
}
void ProgramExecutable::updateCanDrawWith()
{
mCanDrawWith = hasLinkedShaderStage(ShaderType::Vertex);
}
void ProgramExecutable::saveLinkedStateInfo(const ProgramState &state)
{
for (ShaderType shaderType : getLinkedShaderStages())
{
Shader *shader = state.getAttachedShader(shaderType);
ASSERT(shader);
mLinkedOutputVaryings[shaderType] = shader->getOutputVaryings();
mLinkedInputVaryings[shaderType] = shader->getInputVaryings();
mLinkedShaderVersions[shaderType] = shader->getShaderVersion();
mLinkedUniforms[shaderType] = shader->getUniforms();
mLinkedUniformBlocks[shaderType] = shader->getUniformBlocks();
}
}
bool ProgramExecutable::isYUVOutput() const
{
return mYUVOutput;
}
ShaderType ProgramExecutable::getLinkedTransformFeedbackStage() const
{
return GetLastPreFragmentStage(mLinkedShaderStages);
}
bool ProgramExecutable::linkMergedVaryings(
const Context *context,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &transformFeedbackVaryingNames,
const LinkingVariables &linkingVariables,
bool isSeparable,
ProgramVaryingPacking *varyingPacking)
{
ShaderType tfStage = GetLastPreFragmentStage(linkingVariables.isShaderStageUsedBitset);
if (!linkValidateTransformFeedback(context, mergedVaryings, tfStage,
transformFeedbackVaryingNames))
{
return false;
}
// Map the varyings to the register file
// In WebGL, we use a slightly different handling for packing variables.
gl::PackMode packMode = PackMode::ANGLE_RELAXED;
if (context->getLimitations().noFlexibleVaryingPacking)
{
// D3D9 pack mode is strictly more strict than WebGL, so takes priority.
packMode = PackMode::ANGLE_NON_CONFORMANT_D3D9;
}
else if (context->isWebGL())
{
packMode = PackMode::WEBGL_STRICT;
}
// Build active shader stage map.
ShaderBitSet activeShadersMask;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
// - Check for attached shaders to handle the case of a Program linking the currently
// attached shaders.
// - Check for linked shaders to handle the case of a PPO linking separable programs before
// drawing.
if (linkingVariables.isShaderStageUsedBitset[shaderType] ||
getLinkedShaderStages().test(shaderType))
{
activeShadersMask[shaderType] = true;
}
}
if (!varyingPacking->collectAndPackUserVaryings(mInfoLog, context->getCaps(), packMode,
activeShadersMask, mergedVaryings,
transformFeedbackVaryingNames, isSeparable))
{
return false;
}
gatherTransformFeedbackVaryings(mergedVaryings, tfStage, transformFeedbackVaryingNames);
updateTransformFeedbackStrides();
return true;
}
bool ProgramExecutable::linkValidateTransformFeedback(
const Context *context,
const ProgramMergedVaryings &varyings,
ShaderType stage,
const std::vector<std::string> &transformFeedbackVaryingNames)
{
const Version &version = context->getClientVersion();
// Validate the tf names regardless of the actual program varyings.
std::set<std::string> uniqueNames;
for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
{
if (version < Version(3, 1) && tfVaryingName.find('[') != std::string::npos)
{
mInfoLog << "Capture of array elements is undefined and not supported.";
return false;
}
if (version >= Version(3, 1))
{
if (IncludeSameArrayElement(uniqueNames, tfVaryingName))
{
mInfoLog << "Two transform feedback varyings include the same array element ("
<< tfVaryingName << ").";
return false;
}
}
else
{
if (uniqueNames.count(tfVaryingName) > 0)
{
mInfoLog << "Two transform feedback varyings specify the same output variable ("
<< tfVaryingName << ").";
return false;
}
}
uniqueNames.insert(tfVaryingName);
}
// Validate against program varyings.
size_t totalComponents = 0;
for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
const sh::ShaderVariable *var = FindOutputVaryingOrField(varyings, stage, baseName);
if (var == nullptr)
{
mInfoLog << "Transform feedback varying " << tfVaryingName
<< " does not exist in the vertex shader.";
return false;
}
// Validate the matching variable.
if (var->isStruct())
{
mInfoLog << "Struct cannot be captured directly (" << baseName << ").";
return false;
}
size_t elementCount = 0;
size_t componentCount = 0;
if (var->isArray())
{
if (version < Version(3, 1))
{
mInfoLog << "Capture of arrays is undefined and not supported.";
return false;
}
// GLSL ES 3.10 section 4.3.6: A vertex output can't be an array of arrays.
ASSERT(!var->isArrayOfArrays());
if (!subscripts.empty() && subscripts[0] >= var->getOutermostArraySize())
{
mInfoLog << "Cannot capture outbound array element '" << tfVaryingName << "'.";
return false;
}
elementCount = (subscripts.empty() ? var->getOutermostArraySize() : 1);
}
else
{
if (!subscripts.empty())
{
mInfoLog << "Varying '" << baseName
<< "' is not an array to be captured by element.";
return false;
}
elementCount = 1;
}
const Caps &caps = context->getCaps();
// TODO(jmadill): Investigate implementation limits on D3D11
componentCount = VariableComponentCount(var->type) * elementCount;
if (mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS &&
componentCount > static_cast<GLuint>(caps.maxTransformFeedbackSeparateComponents))
{
mInfoLog << "Transform feedback varying " << tfVaryingName << " components ("
<< componentCount << ") exceed the maximum separate components ("
<< caps.maxTransformFeedbackSeparateComponents << ").";
return false;
}
totalComponents += componentCount;
if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS &&
totalComponents > static_cast<GLuint>(caps.maxTransformFeedbackInterleavedComponents))
{
mInfoLog << "Transform feedback varying total components (" << totalComponents
<< ") exceed the maximum interleaved components ("
<< caps.maxTransformFeedbackInterleavedComponents << ").";
return false;
}
}
return true;
}
void ProgramExecutable::gatherTransformFeedbackVaryings(
const ProgramMergedVaryings &varyings,
ShaderType stage,
const std::vector<std::string> &transformFeedbackVaryingNames)
{
// Gather the linked varyings that are used for transform feedback, they should all exist.
mLinkedTransformFeedbackVaryings.clear();
for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
size_t subscript = GL_INVALID_INDEX;
if (!subscripts.empty())
{
subscript = subscripts.back();
}
for (const ProgramVaryingRef &ref : varyings)
{
if (ref.frontShaderStage != stage)
{
continue;
}
const sh::ShaderVariable *varying = ref.get(stage);
if (baseName == varying->name)
{
mLinkedTransformFeedbackVaryings.emplace_back(*varying,
static_cast<GLuint>(subscript));
break;
}
else if (varying->isStruct())
{
GLuint fieldIndex = 0;
const auto *field = varying->findField(tfVaryingName, &fieldIndex);
if (field != nullptr)
{
mLinkedTransformFeedbackVaryings.emplace_back(*field, *varying);
break;
}
}
}
}
}
void ProgramExecutable::updateTransformFeedbackStrides()
{
if (mLinkedTransformFeedbackVaryings.empty())
{
return;
}
if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS)
{
mTransformFeedbackStrides.resize(1);
size_t totalSize = 0;
for (const TransformFeedbackVarying &varying : mLinkedTransformFeedbackVaryings)
{
totalSize += varying.size() * VariableExternalSize(varying.type);
}
mTransformFeedbackStrides[0] = static_cast<GLsizei>(totalSize);
}
else
{
mTransformFeedbackStrides.resize(mLinkedTransformFeedbackVaryings.size());
for (size_t i = 0; i < mLinkedTransformFeedbackVaryings.size(); i++)
{
TransformFeedbackVarying &varying = mLinkedTransformFeedbackVaryings[i];
mTransformFeedbackStrides[i] =
static_cast<GLsizei>(varying.size() * VariableExternalSize(varying.type));
}
}
}
bool ProgramExecutable::validateSamplersImpl(InfoLog *infoLog, const Caps &caps) const
{
// if any two active samplers in a program are of different types, but refer to the same
// texture image unit, and this is the current program, then ValidateProgram will fail, and
// DrawArrays and DrawElements will issue the INVALID_OPERATION error.
for (size_t textureUnit : mActiveSamplersMask)
{
if (mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum)
{
if (infoLog)
{
(*infoLog) << "Samplers of conflicting types refer to the same texture "
"image unit ("
<< textureUnit << ").";
}
mCachedValidateSamplersResult = false;
return false;
}
if (mActiveSamplerFormats[textureUnit] == SamplerFormat::InvalidEnum)
{
if (infoLog)
{
(*infoLog) << "Samplers of conflicting formats refer to the same texture "
"image unit ("
<< textureUnit << ").";
}
mCachedValidateSamplersResult = false;
return false;
}
}
mCachedValidateSamplersResult = true;
return true;
}
bool ProgramExecutable::linkValidateOutputVariables(
const Caps &caps,
const Extensions &extensions,
const Version &version,
GLuint combinedImageUniformsCount,
GLuint combinedShaderStorageBlocksCount,
const std::vector<sh::ShaderVariable> &outputVariables,
int fragmentShaderVersion,
const ProgramAliasedBindings &fragmentOutputLocations,
const ProgramAliasedBindings &fragmentOutputIndices)
{
ASSERT(mOutputVariableTypes.empty());
ASSERT(mActiveOutputVariablesMask.none());
ASSERT(mDrawBufferTypeMask.none());
ASSERT(!mYUVOutput);
// Gather output variable types
for (const sh::ShaderVariable &outputVariable : outputVariables)
{
if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" &&
outputVariable.name != "gl_FragData")
{
continue;
}
unsigned int baseLocation =
(outputVariable.location == -1 ? 0u
: static_cast<unsigned int>(outputVariable.location));
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
// structures, so we may use getBasicTypeElementCount().
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
{
const unsigned int location = baseLocation + elementIndex;
if (location >= mOutputVariableTypes.size())
{
mOutputVariableTypes.resize(location + 1, GL_NONE);
}
ASSERT(location < mActiveOutputVariablesMask.size());
mActiveOutputVariablesMask.set(location);
mOutputVariableTypes[location] = VariableComponentType(outputVariable.type);
ComponentType componentType = GLenumToComponentType(mOutputVariableTypes[location]);
SetComponentTypeMask(componentType, location, &mDrawBufferTypeMask);
}
if (outputVariable.yuv)
{
ASSERT(outputVariables.size() == 1);
mYUVOutput = true;
}
}
if (version >= ES_3_1)
{
// [OpenGL ES 3.1] Chapter 8.22 Page 203:
// A link error will be generated if the sum of the number of active image uniforms used in
// all shaders, the number of active shader storage blocks, and the number of active
// fragment shader outputs exceeds the implementation-dependent value of
// MAX_COMBINED_SHADER_OUTPUT_RESOURCES.
if (combinedImageUniformsCount + combinedShaderStorageBlocksCount +
mActiveOutputVariablesMask.count() >
static_cast<GLuint>(caps.maxCombinedShaderOutputResources))
{
mInfoLog
<< "The sum of the number of active image uniforms, active shader storage blocks "
"and active fragment shader outputs exceeds "
"MAX_COMBINED_SHADER_OUTPUT_RESOURCES ("
<< caps.maxCombinedShaderOutputResources << ")";
return false;
}
}
mOutputVariables = outputVariables;
if (fragmentShaderVersion == 100)
{
return true;
}
// EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
// output array in explicit terms.
//
// Assuming fragData is an output array, you can defend the position that:
// P1) you must support binding "fragData" because it's specified
// P2) you must support querying "fragData[x]" because it's specified
// P3) you must support binding "fragData[0]" because it's a frequently used pattern
//
// Then you can make the leap of faith:
// P4) you must support binding "fragData[x]" because you support "fragData[0]"
// P5) you must support binding "fragData[x]" because you support querying "fragData[x]"
//
// The spec brings in the "world of arrays" when it mentions binding the arrays and the
// automatic binding. Thus it must be interpreted that the thing is not undefined, rather you
// must infer the only possible interpretation (?). Note again: this need of interpretation
// might be completely off of what GL spec logic is.
//
// The other complexity is that unless you implement this feature, it's hard to understand what
// should happen when the client invokes the feature. You cannot add an additional error as it
// is not specified. One can ignore it, but obviously it creates the discrepancies...
std::vector<VariableLocation> reservedLocations;
// Process any output API bindings for arrays that don't alias to the first element.
for (const auto &bindingPair : fragmentOutputLocations)
{
const std::string &name = bindingPair.first;
const ProgramBinding &binding = bindingPair.second;
size_t nameLengthWithoutArrayIndex;
unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
{
continue;
}
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
outputVariableIndex++)
{
const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex];
// Check that the binding corresponds to an output array and its array index fits.
if (outputVariable.isBuiltIn() || !outputVariable.isArray() ||
!angle::BeginsWith(outputVariable.name, name, nameLengthWithoutArrayIndex) ||
arrayIndex >= outputVariable.getOutermostArraySize())
{
continue;
}
// Get the API index that corresponds to this exact binding.
// This index may differ from the index used for the array's base.
std::vector<VariableLocation> &outputLocations =
fragmentOutputIndices.getBindingByName(name) == 1 ? mSecondaryOutputLocations
: mOutputLocations;
unsigned int location = binding.location;
VariableLocation locationInfo(arrayIndex, outputVariableIndex);
if (location >= outputLocations.size())
{
outputLocations.resize(location + 1);
}
if (outputLocations[location].used())
{
mInfoLog << "Location of variable " << outputVariable.name
<< " conflicts with another variable.";
return false;
}
outputLocations[location] = locationInfo;
// Note the array binding location so that it can be skipped later.
reservedLocations.push_back(locationInfo);
}
}
// Reserve locations for output variables whose location is fixed in the shader or through the
// API. Otherwise, the remaining unallocated outputs will be processed later.
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
outputVariableIndex++)
{
const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex];
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
if (outputVariable.isBuiltIn())
continue;
int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable);
if (fixedLocation == -1)
{
// Here we're only reserving locations for variables whose location is fixed.
continue;
}
unsigned int baseLocation = static_cast<unsigned int>(fixedLocation);
std::vector<VariableLocation> &outputLocations =
IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable)
? mSecondaryOutputLocations
: mOutputLocations;
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
// structures, so we may use getBasicTypeElementCount().
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex))
{
mInfoLog << "Location of variable " << outputVariable.name
<< " conflicts with another variable.";
return false;
}
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex, mOutputVariables[outputVariableIndex]);
}
// Here we assign locations for the output variables that don't yet have them. Note that we're
// not necessarily able to fit the variables optimally, since then we might have to try
// different arrangements of output arrays. Now we just assign the locations in the order that
// we got the output variables. The spec isn't clear on what kind of algorithm is required for
// finding locations for the output variables, so this should be acceptable at least for now.
GLuint maxLocation = static_cast<GLuint>(caps.maxDrawBuffers);
if (!mSecondaryOutputLocations.empty())
{
// EXT_blend_func_extended: Program outputs will be validated against
// MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT if there's even one output with index one.
maxLocation = caps.maxDualSourceDrawBuffers;
}
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
outputVariableIndex++)
{
const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex];
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
if (outputVariable.isBuiltIn())
continue;
int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable);
std::vector<VariableLocation> &outputLocations =
IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable)
? mSecondaryOutputLocations
: mOutputLocations;
unsigned int baseLocation = 0;
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
if (fixedLocation != -1)
{
// Secondary inputs might have caused the max location to drop below what has already
// been explicitly assigned locations. Check for any fixed locations above the max
// that should cause linking to fail.
baseLocation = static_cast<unsigned int>(fixedLocation);
}
else
{
// No fixed location, so try to fit the output in unassigned locations.
// Try baseLocations starting from 0 one at a time and see if the variable fits.
while (FindUsedOutputLocation(outputLocations, baseLocation, elementCount,
reservedLocations, outputVariableIndex))
{
baseLocation++;
}
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex, mOutputVariables[outputVariableIndex]);
}
// Check for any elements assigned above the max location that are actually used.
if (baseLocation + elementCount > maxLocation &&
(baseLocation >= maxLocation ||
FindUsedOutputLocation(outputLocations, maxLocation,
baseLocation + elementCount - maxLocation, reservedLocations,
outputVariableIndex)))
{
// EXT_blend_func_extended: Linking can fail:
// "if the explicit binding assignments do not leave enough space for the linker to
// automatically assign a location for a varying out array, which requires multiple
// contiguous locations."
mInfoLog << "Could not fit output variable into available locations: "
<< outputVariable.name;
return false;
}
}
return true;
}
bool ProgramExecutable::linkUniforms(
const Context *context,
const ShaderMap<std::vector<sh::ShaderVariable>> &shaderUniforms,
InfoLog &infoLog,
const ProgramAliasedBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCountOut,
std::vector<UnusedUniform> *unusedUniformsOutOrNull,
std::vector<VariableLocation> *uniformLocationsOutOrNull)
{
UniformLinker linker(mLinkedShaderStages, shaderUniforms);
if (!linker.link(context->getCaps(), infoLog, uniformLocationBindings))
{
return false;
}
linker.getResults(&mUniforms, unusedUniformsOutOrNull, uniformLocationsOutOrNull);
linkSamplerAndImageBindings(combinedImageUniformsCountOut);
if (!linkAtomicCounterBuffers(context, infoLog))
{
return false;
}
return true;
}
void ProgramExecutable::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
{
ASSERT(combinedImageUniforms);
// Iterate over mExecutable->mUniforms from the back, and find the range of subpass inputs,
// atomic counters, images and samplers in that order.
auto highIter = mUniforms.rbegin();
auto lowIter = highIter;
unsigned int high = static_cast<unsigned int>(mUniforms.size());
unsigned int low = high;
// Note that uniform block uniforms are not yet appended to this list.
ASSERT(mUniforms.empty() || highIter->isAtomicCounter() || highIter->isImage() ||
highIter->isSampler() || highIter->isInDefaultBlock() || highIter->isFragmentInOut);
for (; lowIter != mUniforms.rend() && lowIter->isFragmentInOut; ++lowIter)
{
--low;
}
mFragmentInoutRange = RangeUI(low, high);
highIter = lowIter;
high = low;
for (; lowIter != mUniforms.rend() && lowIter->isAtomicCounter(); ++lowIter)
{
--low;
}
mAtomicCounterUniformRange = RangeUI(low, high);
highIter = lowIter;
high = low;
for (; lowIter != mUniforms.rend() && lowIter->isImage(); ++lowIter)
{
--low;
}
mImageUniformRange = RangeUI(low, high);
*combinedImageUniforms = 0u;
// If uniform is a image type, insert it into the mImageBindings array.
for (unsigned int imageIndex : mImageUniformRange)
{
// ES3.1 (section 7.6.1) and GLSL ES3.1 (section 4.4.5), Uniform*i{v} commands
// cannot load values into a uniform defined as an image. if declare without a
// binding qualifier, any uniform image variable (include all elements of
// unbound image array) should be bound to unit zero.
auto &imageUniform = mUniforms[imageIndex];
TextureType textureType = ImageTypeToTextureType(imageUniform.type);
const GLuint arraySize = imageUniform.isArray() ? imageUniform.arraySizes[0] : 1u;
if (imageUniform.binding == -1)
{
mImageBindings.emplace_back(
ImageBinding(imageUniform.getBasicTypeElementCount(), textureType));
}
else
{
// The arrays of arrays are flattened to arrays, it needs to record the array offset for
// the correct binding image unit.
mImageBindings.emplace_back(
ImageBinding(imageUniform.binding + imageUniform.parentArrayIndex() * arraySize,
imageUniform.getBasicTypeElementCount(), textureType));
}
*combinedImageUniforms += imageUniform.activeShaderCount() * arraySize;
}
highIter = lowIter;
high = low;
for (; lowIter != mUniforms.rend() && lowIter->isSampler(); ++lowIter)
{
--low;
}
mSamplerUniformRange = RangeUI(low, high);
// If uniform is a sampler type, insert it into the mSamplerBindings array.
for (unsigned int samplerIndex : mSamplerUniformRange)
{
const auto &samplerUniform = mUniforms[samplerIndex];
TextureType textureType = SamplerTypeToTextureType(samplerUniform.type);
GLenum samplerType = samplerUniform.typeInfo->type;
unsigned int elementCount = samplerUniform.getBasicTypeElementCount();
SamplerFormat format = samplerUniform.typeInfo->samplerFormat;
mSamplerBindings.emplace_back(textureType, samplerType, format, elementCount);
}
// Whatever is left constitutes the default uniforms.
mDefaultUniformRange = RangeUI(0, low);
}
bool ProgramExecutable::linkAtomicCounterBuffers(const Context *context, InfoLog &infoLog)
{
for (unsigned int index : mAtomicCounterUniformRange)
{
auto &uniform = mUniforms[index];
uniform.blockInfo.offset = uniform.offset;
uniform.blockInfo.arrayStride = (uniform.isArray() ? 4 : 0);
uniform.blockInfo.matrixStride = 0;
uniform.blockInfo.isRowMajorMatrix = false;
bool found = false;
for (unsigned int bufferIndex = 0; bufferIndex < getActiveAtomicCounterBufferCount();
++bufferIndex)
{
auto &buffer = mAtomicCounterBuffers[bufferIndex];
if (buffer.binding == uniform.binding)
{
buffer.memberIndexes.push_back(index);
uniform.bufferIndex = bufferIndex;
found = true;
buffer.unionReferencesWith(uniform);
break;
}
}
if (!found)
{
AtomicCounterBuffer atomicCounterBuffer;
atomicCounterBuffer.binding = uniform.binding;
atomicCounterBuffer.memberIndexes.push_back(index);
atomicCounterBuffer.unionReferencesWith(uniform);
mAtomicCounterBuffers.push_back(atomicCounterBuffer);
uniform.bufferIndex = static_cast<int>(getActiveAtomicCounterBufferCount() - 1);
}
}
// Count each atomic counter buffer to validate against
// per-stage and combined gl_Max*AtomicCounterBuffers.
GLint combinedShaderACBCount = 0;
gl::ShaderMap<GLint> perShaderACBCount = {};
for (unsigned int bufferIndex = 0; bufferIndex < getActiveAtomicCounterBufferCount();
++bufferIndex)
{
AtomicCounterBuffer &acb = mAtomicCounterBuffers[bufferIndex];
const ShaderBitSet shaderStages = acb.activeShaders();
for (gl::ShaderType shaderType : shaderStages)
{
++perShaderACBCount[shaderType];
}
++combinedShaderACBCount;
}
const Caps &caps = context->getCaps();
if (combinedShaderACBCount > caps.maxCombinedAtomicCounterBuffers)
{
infoLog << " combined AtomicCounterBuffers count exceeds limit";
return false;
}
for (gl::ShaderType stage : gl::AllShaderTypes())
{
if (perShaderACBCount[stage] > caps.maxShaderAtomicCounterBuffers[stage])
{
infoLog << GetShaderTypeString(stage)
<< " shader AtomicCounterBuffers count exceeds limit";
return false;
}
}
return true;
}
void ProgramExecutable::copyInputsFromProgram(const ProgramState &programState)
{
mProgramInputs = programState.getProgramInputs();
}
void ProgramExecutable::copyShaderBuffersFromProgram(const ProgramState &programState,
ShaderType shaderType)
{
AppendActiveBlocks(shaderType, programState.getUniformBlocks(), mUniformBlocks);
AppendActiveBlocks(shaderType, programState.getShaderStorageBlocks(), mShaderStorageBlocks);
AppendActiveBlocks(shaderType, programState.getAtomicCounterBuffers(), mAtomicCounterBuffers);
}
void ProgramExecutable::clearSamplerBindings()
{
mSamplerBindings.clear();
}
void ProgramExecutable::copySamplerBindingsFromProgram(const ProgramState &programState)
{
const std::vector<SamplerBinding> &bindings = programState.getSamplerBindings();
mSamplerBindings.insert(mSamplerBindings.end(), bindings.begin(), bindings.end());
}
void ProgramExecutable::copyImageBindingsFromProgram(const ProgramState &programState)
{
const std::vector<ImageBinding> &bindings = programState.getImageBindings();
mImageBindings.insert(mImageBindings.end(), bindings.begin(), bindings.end());
}
void ProgramExecutable::copyOutputsFromProgram(const ProgramState &programState)
{
mOutputVariables = programState.getOutputVariables();
mOutputLocations = programState.getOutputLocations();
mSecondaryOutputLocations = programState.getSecondaryOutputLocations();
}
void ProgramExecutable::copyUniformsFromProgramMap(const ShaderMap<Program *> &programs)
{
// Merge default uniforms.
auto getDefaultRange = [](const ProgramState &state) { return state.getDefaultUniformRange(); };
mDefaultUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getDefaultRange);
// Merge sampler uniforms.
auto getSamplerRange = [](const ProgramState &state) { return state.getSamplerUniformRange(); };
mSamplerUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getSamplerRange);
// Merge image uniforms.
auto getImageRange = [](const ProgramState &state) { return state.getImageUniformRange(); };
mImageUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getImageRange);
// Merge atomic counter uniforms.
auto getAtomicRange = [](const ProgramState &state) {
return state.getAtomicCounterUniformRange();
};
mAtomicCounterUniformRange =
AddUniforms(programs, mLinkedShaderStages, mUniforms, getAtomicRange);
// Merge fragment in/out uniforms.
auto getInoutRange = [](const ProgramState &state) { return state.getFragmentInoutRange(); };
mFragmentInoutRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getInoutRange);
}
} // namespace gl