// // 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.
// 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++)
{ constunsigned int location = baseLocation + elementIndex;
if (outputLocations[location].used())
{
VariableLocation locationInfo(elementIndex, variableIndex);
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
reservedLocations.end())
{ returntrue;
}
}
} 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; constunsigned 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;
}
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>());
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(mOutputVariableTypes.size());
for (constauto &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()));
// 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
{ returnstatic_cast<int>(mInfoLog.getLength());
}
// 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 (!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;
}
}
// 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);
}
// From OpneGLES spec. 11.1.2.1: A program will fail to link if: // the count specified by TransformFeedbackVaryings is non-zero, but the // program object has no vertex, tessellation evaluation, or geometry shader
if (transformFeedbackVaryingNames.size() > 0 &&
!gl::ShaderTypeSupportsTransformFeedback(getLinkedTransformFeedbackStage()))
{
mInfoLog << "Linked transform feedback stage " << getLinkedTransformFeedbackStage()
<< " does not support transform feedback varying."; return false;
}
// 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;
}
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 << ").";
}
if (mActiveSamplerFormats[textureUnit] == SamplerFormat::InvalidEnum)
{
if (infoLog)
{
(*infoLog) << "Samplers of conflicting formats refer to the same texture " "image unit ("
<< textureUnit << ").";
}
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)
{ returntrue;
}
// 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 (constauto &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);
// 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;
}
}
// 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);
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));
}
// If uniform is a sampler type, insert it into the mSamplerBindings array.
for (unsigned int samplerIndex : mSamplerUniformRange)
{ constauto &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;
}
} returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.