// // 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.
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. returnfalse;
}
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>());
// 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))
{ returnfalse;
}
// 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;
} elseif (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."; returnfalse;
} if (version >= Version(3, 1))
{ if (IncludeSameArrayElement(uniqueNames, tfVaryingName))
{
mInfoLog << "Two transform feedback varyings include the same array element ("
<< tfVaryingName << ")."; returnfalse;
}
} else
{ if (uniqueNames.count(tfVaryingName) > 0)
{
mInfoLog << "Two transform feedback varyings specify the same output variable ("
<< tfVaryingName << ")."; returnfalse;
}
}
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."; returnfalse;
}
// Validate against program varyings.
size_t totalComponents = 0; for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
{
std::vector<unsignedint> 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."; returnfalse;
}
// Validate the matching variable. if (var->isStruct())
{
mInfoLog << "Struct cannot be captured directly (" << baseName << ")."; returnfalse;
}
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 << ")"; returnfalse;
}
}
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; unsignedint arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex); if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
{ continue;
} for (unsignedint 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; unsignedint 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."; returnfalse;
}
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 (unsignedint 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;
} unsignedint baseLocation = static_cast<unsignedint>(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(). unsignedint elementCount = outputVariable.getBasicTypeElementCount(); if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex))
{
mInfoLog << "Location of variable " << outputVariable.name
<< " conflicts with another variable."; returnfalse;
}
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;
}
// 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; unsignedint baseLocation = 0; unsignedint 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<unsignedint>(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; returnfalse;
}
}
// 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;
unsignedint high = static_cast<unsignedint>(mUniforms.size()); unsignedint 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 (unsignedint 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 (unsignedint samplerIndex : mSamplerUniformRange)
{ constauto &samplerUniform = mUniforms[samplerIndex];
TextureType textureType = SamplerTypeToTextureType(samplerUniform.type);
GLenum samplerType = samplerUniform.typeInfo->type; unsignedint 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);
}
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.