// // Copyright 2002 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. //
// Program.cpp: Implements the gl::Program class. Implements GL program objects // and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28.
// This simplified cast function doesn't need to worry about advanced concepts like // depth range values, or casting to bool. template <typename DestT, typename SrcT>
DestT UniformStateQueryCast(SrcT value);
// Array output variables may be bound out of order, so we need to ensure we only pick the // first element if given the base name. if ((variable.name == name) && (variableLocation.arrayIndex == 0))
{ returnstatic_cast<GLint>(location);
} if (variable.isArray() && variableLocation.arrayIndex == arrayIndex &&
angle::BeginsWith(variable.name, name, nameLengthWithoutArrayIndex))
{ returnstatic_cast<GLint>(location);
}
}
// Array output variables may be bound out of order, so we need to ensure we only pick the // first element if given the base name. Uniforms don't allow this behavior and some code // seemingly depends on the opposite behavior, so only enable it for output variables. if (angle::BeginsWith(variable.name, name) && (variableLocation.arrayIndex == 0))
{ if (name.length() == variable.name.length())
{
ASSERT(name == variable.name); // GLES 3.1 November 2016 page 87. // The string exactly matches the name of the active variable. returnstatic_cast<GLint>(location);
} if (name.length() + 3u == variable.name.length() && variable.isArray())
{
ASSERT(name + "[0]" == variable.name); // The string identifies the base name of an active array, where the string would // exactly match the name of the variable if the suffix "[0]" were appended to the // string. returnstatic_cast<GLint>(location);
}
} if (variable.isArray() && variableLocation.arrayIndex == arrayIndex &&
nameLengthWithoutArrayIndex + 3u == variable.name.length() &&
angle::BeginsWith(variable.name, name, nameLengthWithoutArrayIndex))
{
ASSERT(name.substr(0u, nameLengthWithoutArrayIndex) + "[0]" == variable.name); // The string identifies an active element of the array, where the string ends with the // concatenation of the "[" character, an integer (with no "+" sign, extra leading // zeroes, or whitespace) identifying an array element, and the "]" character, the // integer is less than the number of active elements of the array variable, and where // the string would exactly match the enumerated name of the array if the decimal // integer were replaced with zero. returnstatic_cast<GLint>(location);
}
}
constchar *GetLinkMismatchErrorString(LinkMismatchError linkError)
{ switch (linkError)
{ case LinkMismatchError::TYPE_MISMATCH: return"Type"; case LinkMismatchError::ARRAYNESS_MISMATCH: return"Array-ness"; case LinkMismatchError::ARRAY_SIZE_MISMATCH: return"Array size"; case LinkMismatchError::PRECISION_MISMATCH: return"Precision"; case LinkMismatchError::STRUCT_NAME_MISMATCH: return"Structure name"; case LinkMismatchError::FIELD_NUMBER_MISMATCH: return"Field number"; case LinkMismatchError::FIELD_NAME_MISMATCH: return"Field name";
case LinkMismatchError::INTERPOLATION_TYPE_MISMATCH: return"Interpolation type"; case LinkMismatchError::INVARIANCE_MISMATCH: return"Invariance";
case LinkMismatchError::BINDING_MISMATCH: return"Binding layout qualifier"; case LinkMismatchError::LOCATION_MISMATCH: return"Location layout qualifier"; case LinkMismatchError::OFFSET_MISMATCH: return"Offset layout qualifier"; case LinkMismatchError::INSTANCE_NAME_MISMATCH: return"Instance name qualifier"; case LinkMismatchError::FORMAT_MISMATCH: return"Format qualifier";
case LinkMismatchError::LAYOUT_QUALIFIER_MISMATCH: return"Layout qualifier"; case LinkMismatchError::MATRIX_PACKING_MISMATCH: return"Matrix Packing";
case LinkMismatchError::FIELD_LOCATION_MISMATCH: return"Field location"; case LinkMismatchError::FIELD_STRUCT_NAME_MISMATCH: return"Field structure name"; default:
UNREACHABLE(); return"";
}
}
// Saves the linking context for later use in resolveLink(). struct Program::LinkingState
{
std::shared_ptr<ProgramExecutable> linkedExecutable;
ProgramLinkedResources resources;
egl::BlobCache::Key programHash;
std::unique_ptr<rx::LinkEvent> linkEvent; bool linkingFromBinary;
};
if (bufSize > 0)
{ const std::string logString(str());
if (!logString.empty())
{
index = std::min(static_cast<size_t>(bufSize) - 1, logString.length());
memcpy(infoLog, logString.c_str(), index);
}
infoLog[index] = '\0';
}
if (length)
{
*length = static_cast<GLsizei>(index);
}
}
// append a sanitized message to the program info log. // The D3D compiler includes a fake file path in some of the warning or error // messages, so lets remove all occurrences of this fake file path from the log. void InfoLog::appendSanitized(constchar *message)
{
ensureInitialized();
std::string msg(message);
size_t found; do
{
found = msg.find(g_fakepath); if (found != std::string::npos)
{
msg.erase(found, strlen(g_fakepath));
}
} while (found != std::string::npos);
if (!msg.empty())
{
*mLazyStream << message << std::endl;
}
}
void InfoLog::reset()
{ if (mLazyStream)
{
mLazyStream.reset(nullptr);
}
}
bool InfoLog::empty() const
{ if (!mLazyStream)
{ returntrue;
}
if (!mismatchedStructOrBlockFieldName.empty())
{
stream << "' member '" << variableName << "." << mismatchedStructOrBlockFieldName;
}
stream << "' differ between " << GetShaderTypeString(shaderType1) << " and "
<< GetShaderTypeString(shaderType2) << " shaders.";
infoLog << stream.str();
}
bool IsActiveInterfaceBlock(const sh::InterfaceBlock &interfaceBlock)
{ // Only 'packed' blocks are allowed to be considered inactive. return interfaceBlock.active || interfaceBlock.layout != sh::BLOCKLAYOUT_PACKED;
}
// EXT_blend_func_extended spec: "If it specifies the base name of an array, // it identifies the resources associated with the first element of the array." // // Normalize array bindings so that "name" and "name[0]" map to the same entry. // If this binding is of the form "name[0]", then mark the "name" binding as // aliased but do not update it yet in case "name" is not actually an array.
size_t nameLengthWithoutArrayIndex; unsignedint arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex); if (arrayIndex == 0)
{
std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex); auto iter = mBindings.find(baseName); if (iter != mBindings.end())
{
iter->second.aliased = true;
}
}
}
int ProgramAliasedBindings::getBindingByName(const std::string &name) const
{ auto iter = mBindings.find(name); return (iter != mBindings.end()) ? iter->second.location : -1;
}
int ProgramAliasedBindings::getBindingByLocation(GLuint location) const
{ for (constauto &iter : mBindings)
{ if (iter.second.location == location)
{ return iter.second.location;
}
} return -1;
}
// Check with the normalized array name if applicable. if (variable.isArray())
{
size_t nameLengthWithoutArrayIndex; unsignedint arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex); if (arrayIndex == 0)
{
std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex); auto iter = mBindings.find(baseName); // If "name" exists and is not aliased, that means it was modified more // recently than its "name[0]" form and should be used instead of that. if (iter != mBindings.end() && !iter->second.aliased)
{ return iter->second.location;
}
} elseif (arrayIndex == GL_INVALID_INDEX)
{ auto iter = mBindings.find(variable.name); // If "name" exists and is not aliased, that means it was modified more // recently than its "name[0]" form and should be used instead of that. if (iter != mBindings.end() && !iter->second.aliased)
{ return iter->second.location;
} // The base name was aliased, so use the name with the array notation. return getBindingByName(name + "[0]");
}
}
angle::Result Program::link(const Context *context)
{
angle::Result result = linkImpl(context);
// Avoid having two ProgramExecutables if the link failed and the Program had successfully // linked previously. if (mLinkingState && mLinkingState->linkedExecutable)
{
mState.mExecutable = mLinkingState->linkedExecutable;
}
return result;
}
// The attached shaders are checked for linking errors by matching up their variables. // Uniform, input and output variables get collected. // The code gets compiled into binaries.
angle::Result Program::linkImpl(const Context *context)
{
ASSERT(!mLinkingState); // Don't make any local variables pointing to anything within the ProgramExecutable, since // unlink() could make a new ProgramExecutable making any references/pointers invalid. auto *platform = ANGLEPlatformCurrent(); double startTime = platform->currentTime(platform);
// Unlink the program, but do not clear the validation-related caching yet, since we can still // use the previously linked program if linking the shaders fails.
mLinked = false;
mState.mExecutable->resetInfoLog();
// Validate we have properly attached shaders before checking the cache. if (!linkValidateShaders(context, mState.mExecutable->getInfoLog()))
{ return angle::Result::Continue;
}
// TODO: http://anglebug.com/4530: Enable program caching for separable programs if (cache && !isSeparable())
{
std::lock_guard<std::mutex> cacheLock(context->getProgramCacheMutex());
angle::Result cacheResult = cache->getProgram(context, this, &programHash);
ANGLE_TRY(cacheResult);
// Check explicitly for Continue, Incomplete means a cache miss if (cacheResult == angle::Result::Continue)
{
std::scoped_lock lock(mHistogramMutex); // Succeeded in loading the binaries in the front-end, back end may still be loading // asynchronously double delta = platform->currentTime(platform) - startTime; int us = static_cast<int>(delta * 1000000.0);
ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramCacheHitTimeUS", us); return angle::Result::Continue;
}
}
// Cache load failed, fall through to normal linking.
unlink();
InfoLog &infoLog = mState.mExecutable->getInfoLog();
// Re-link shaders after the unlink call. bool result = linkValidateShaders(context, infoLog);
ASSERT(result);
// [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 (combinedImageUniforms + combinedShaderStorageBlocks > static_cast<GLuint>(context->getCaps().maxCombinedShaderOutputResources))
{
infoLog
<< "The sum of the number of active image uniforms, active shader storage blocks " "and active fragment shader outputs exceeds " "MAX_COMBINED_SHADER_OUTPUT_RESOURCES ("
<< context->getCaps().maxCombinedShaderOutputResources << ")"; return angle::Result::Continue;
}
} else
{ if (!linkAttributes(context, infoLog))
{ return angle::Result::Continue;
}
if (!linkVaryings(context, infoLog))
{ return angle::Result::Continue;
}
// Must be after mProgram->link() to avoid misleading the linker about output variables.
mState.updateProgramInterfaceInputs(context);
mState.updateProgramInterfaceOutputs(context);
if (mState.mSeparable)
{
mLinkingState->linkedExecutable = mState.mExecutable;
}
angle::Result result = mLinkingState->linkEvent->wait(context);
mLinked = result == angle::Result::Continue;
std::unique_ptr<LinkingState> linkingState = std::move(mLinkingState); if (!mLinked)
{
mState.mExecutable->reset(false); return;
}
if (linkingState->linkingFromBinary)
{ // All internal Program state is already loaded from the binary. return;
}
initInterfaceBlockBindings();
// According to GLES 3.0/3.1 spec for LinkProgram and UseProgram, // Only successfully linked program can replace the executables.
ASSERT(mLinked);
// Mark implementation-specific unreferenced uniforms as ignored.
std::vector<ImageBinding> *imageBindings = getExecutable().getImageBindings();
mProgram->markUnusedUniformLocations(&mState.mUniformLocations,
&mState.mExecutable->mSamplerBindings, imageBindings);
// Must be called after markUnusedUniformLocations.
postResolveLink(context);
// Save to the program cache.
std::lock_guard<std::mutex> cacheLock(context->getProgramCacheMutex());
MemoryProgramCache *cache = context->getMemoryProgramCache(); // TODO: http://anglebug.com/4530: Enable program caching for separable programs if (cache && !isSeparable() &&
(mState.mExecutable->mLinkedTransformFeedbackVaryings.empty() ||
!context->getFrontendFeatures().disableProgramCachingForTransformFeedback.enabled))
{ if (cache->putProgram(linkingState->programHash, context, this) == angle::Result::Stop)
{ // Don't fail linking if putting the program binary into the cache fails, the program is // still usable.
ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, "Failed to save linked program to memory program cache.");
}
}
}
// Copy over each input varying, since the Shader could go away if (shader->getType() == ShaderType::Compute)
{ for (const sh::ShaderVariable &attribute : shader->getAllAttributes(context))
{ // Compute Shaders have the following built-in input variables. // // in uvec3 gl_NumWorkGroups; // in uvec3 gl_WorkGroupID; // in uvec3 gl_LocalInvocationID; // in uvec3 gl_GlobalInvocationID; // in uint gl_LocalInvocationIndex; // They are all vecs or uints, so no special handling is required.
mExecutable->mProgramInputs.emplace_back(attribute);
}
} else
{ for (const sh::ShaderVariable &varying : shader->getInputVaryings(context))
{
UpdateInterfaceVariable(&mExecutable->mProgramInputs, varying);
}
}
}
if (lastAttachedShaderType == ShaderType::Fragment)
{ // Fragment outputs are already what we need, so nothing to do return;
} if (lastAttachedShaderType == ShaderType::Compute)
{ // If the program only contains a Compute Shader, then there are no user-defined outputs. return;
}
// Copy over each output varying, since the Shader could go away for (const sh::ShaderVariable &varying : shader->getOutputVaryings(context))
{
UpdateInterfaceVariable(&mExecutable->mOutputVariables, varying);
}
}
// Returns the program object to an unlinked state, before re-linking, or at destruction void Program::unlink()
{ if (mLinkingState && mLinkingState->linkedExecutable)
{ // The new ProgramExecutable that we'll attempt to link with needs to start from a copy of // the last successfully linked ProgramExecutable, so we don't lose any state information.
mState.mExecutable.reset(new ProgramExecutable(*mLinkingState->linkedExecutable));
}
mState.mExecutable->reset(true);
if (!angle::GetANGLEHasBinaryLoading())
{ return angle::Result::Incomplete;
}
ASSERT(binaryFormat == GL_PROGRAM_BINARY_ANGLE); if (binaryFormat != GL_PROGRAM_BINARY_ANGLE)
{
infoLog << "Invalid program binary format."; return angle::Result::Incomplete;
}
BinaryInputStream stream(binary, length);
ANGLE_TRY(deserialize(context, stream, infoLog)); // Currently we require the full shader text to compute the program hash. // We could also store the binary in the internal program cache.
// The rx::LinkEvent returned from ProgramImpl::load is a base class with multiple // implementations. In some implementations, a background thread is used to compile the // shaders. Any calls to the LinkEvent object, therefore, are racy and may interfere with // the operation.
// We do not want to call LinkEvent::wait because that will cause the background thread // to finish its task before returning, thus defeating the purpose of background compilation. // We need to defer waiting on background compilation until the very last minute when we // absolutely need the results, such as when the developer binds the program or queries // for the completion status.
// If load returns nullptr, we know for sure that the binary is not compatible with the backend. // The loaded binary could have been read from the on-disk shader cache and be corrupted or // serialized with different revision and subsystem id than the currently loaded backend. // Returning 'Incomplete' to the caller results in link happening using the original shader // sources.
angle::Result result;
std::unique_ptr<LinkingState> linkingState;
std::unique_ptr<rx::LinkEvent> linkEvent = mProgram->load(context, &stream, infoLog); if (linkEvent)
{
linkingState = std::make_unique<LinkingState>();
linkingState->linkingFromBinary = true;
linkingState->linkEvent = std::move(linkEvent);
result = angle::Result::Continue;
} else
{
result = angle::Result::Incomplete;
}
mLinkingState = std::move(linkingState);
if (streamLength > bufSize)
{ if (length)
{
*length = 0;
}
// TODO: This should be moved to the validation layer but computing the size of the binary // before saving it causes the save to happen twice. It may be possible to write the binary // to a separate buffer, validate sizes and then copy it.
ANGLE_CHECK(context, false, "Insufficient buffer size", GL_INVALID_OPERATION);
}
if (binary)
{ char *ptr = reinterpret_cast<char *>(binary);
bool isComputeShaderAttached = shaders[ShaderType::Compute] != nullptr; bool isGraphicsShaderAttached = shaders[ShaderType::Vertex] ||
shaders[ShaderType::TessControl] ||
shaders[ShaderType::TessEvaluation] ||
shaders[ShaderType::Geometry] || shaders[ShaderType::Fragment]; // Check whether we both have a compute and non-compute shaders attached. // If there are of both types attached, then linking should fail. // OpenGL ES 3.10, 7.3 Program Objects, under LinkProgram if (isComputeShaderAttached && isGraphicsShaderAttached)
{
infoLog << "Both compute and graphics shaders are attached to the same program."; returnfalse;
}
if (!shader->isCompiled(context))
{
infoLog << ShaderTypeToString(shaderType) << " shader is not compiled."; returnfalse;
}
if (!version.valid())
{
version = shader->getShaderVersion(context);
} elseif (version != shader->getShaderVersion(context))
{
infoLog << ShaderTypeToString(shaderType)
<< " shader version does not match other shader versions."; returnfalse;
}
}
if (isComputeShaderAttached)
{
ASSERT(shaders[ShaderType::Compute]->getType() == ShaderType::Compute);
// GLSL ES 3.10, 4.4.1.1 Compute Shader Inputs // If the work group size is not specified, a link time error should occur. if (!mState.mComputeShaderLocalSize.isDeclared())
{
infoLog << "Work group size is not specified."; returnfalse;
}
} else
{ if (!isGraphicsShaderAttached)
{
infoLog << "No compiled shaders."; returnfalse;
}
bool hasVertex = shaders[ShaderType::Vertex] != nullptr; bool hasFragment = shaders[ShaderType::Fragment] != nullptr; if (!isSeparable() && (!hasVertex || !hasFragment))
{
infoLog
<< "The program must contain objects to form both a vertex and fragment shader."; returnfalse;
}
bool hasTessControl = shaders[ShaderType::TessControl] != nullptr; bool hasTessEvaluation = shaders[ShaderType::TessEvaluation] != nullptr; if (!isSeparable() && (hasTessControl != hasTessEvaluation))
{
infoLog << "Tessellation control and evaluation shaders must be specified together."; returnfalse;
}
Shader *geometryShader = shaders[ShaderType::Geometry]; if (shaders[ShaderType::Geometry])
{ // [GL_EXT_geometry_shader] Chapter 7 // Linking can fail for a variety of reasons as specified in the OpenGL ES Shading // Language Specification, as well as any of the following reasons: // * One or more of the shader objects attached to <program> are not compiled // successfully. // * The shaders do not use the same shader language version. // * <program> contains objects to form a geometry shader, and // - <program> is not separable and contains no objects to form a vertex shader; or // - the input primitive type, output primitive type, or maximum output vertex count // is not specified in the compiled geometry shader object.
ASSERT(geometryShader->getType() == ShaderType::Geometry);
Optional<PrimitiveMode> inputPrimitive =
geometryShader->getGeometryShaderInputPrimitiveType(context); if (!inputPrimitive.valid())
{
infoLog << "Input primitive type is not specified in the geometry shader."; returnfalse;
}
Optional<PrimitiveMode> outputPrimitive =
geometryShader->getGeometryShaderOutputPrimitiveType(context); if (!outputPrimitive.valid())
{
infoLog << "Output primitive type is not specified in the geometry shader."; returnfalse;
}
Optional<GLint> maxVertices = geometryShader->getGeometryShaderMaxVertices(context); if (!maxVertices.valid())
{
infoLog << "'max_vertices' is not specified in the geometry shader."; returnfalse;
}
Shader *tessControlShader = shaders[ShaderType::TessControl]; if (tessControlShader)
{ int tcsShaderVertices = tessControlShader->getTessControlShaderVertices(context); if (tcsShaderVertices == 0)
{ // In tessellation control shader, output vertices should be specified at least // once. // > GLSL ES Version 3.20.6 spec: // > 4.4.2. Output Layout Qualifiers // > Tessellation Control Outputs // > ... // > There must be at least one layout qualifier specifying an output patch vertex // > count in any program containing a tessellation control shader.
infoLog << "In Tessellation Control Shader, at least one layout qualifier " "specifying an output patch vertex count must exist."; returnfalse;
}
Shader *tessEvaluationShader = shaders[ShaderType::TessEvaluation]; if (tessEvaluationShader)
{
GLenum tesPrimitiveMode = tessEvaluationShader->getTessGenMode(context); if (tesPrimitiveMode == 0)
{ // In tessellation evaluation shader, a primitive mode should be specified at least // once. // > GLSL ES Version 3.20.6 spec: // > 4.4.1. Input Layout Qualifiers // > Tessellation Evaluation Inputs // > ... // > The tessellation evaluation shader object in a program must declare a primitive // > mode in its input layout. Declaring vertex spacing, ordering, or point mode // > identifiers is optional.
infoLog << "The Tessellation Evaluation Shader object in a program must declare a " "primitive mode in its input layout."; returnfalse;
}
// TODO: http://anglebug.com/3571 and http://anglebug.com/3572 // Need to move logic of validating builtin varyings inside the for-loop above. // This is because the built-in symbols `gl_ClipDistance` and `gl_CullDistance` // can be redeclared in Geometry or Tessellation shaders as well.
Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment]; if (vertexShader && fragmentShader &&
!LinkValidateBuiltInVaryings(vertexShader->getOutputVaryings(context),
fragmentShader->getInputVaryings(context),
vertexShader->getType(), fragmentShader->getType(),
vertexShader->getShaderVersion(context),
fragmentShader->getShaderVersion(context), infoLog))
{ returnfalse;
}
if (!vertexShader)
{ // No vertex shader, so no attributes, so nothing to do returntrue;
}
shaderVersion = vertexShader->getShaderVersion(context); if (shaderVersion >= 300)
{ // In GLSL ES 3.00.6, aliasing checks should be done with all declared attributes - // see GLSL ES 3.00.6 section 12.46. Inactive attributes will be pruned after // aliasing checks.
mState.mExecutable->mProgramInputs = vertexShader->getAllAttributes(context);
} else
{ // In GLSL ES 1.00.17 we only do aliasing checks for active attributes.
mState.mExecutable->mProgramInputs = vertexShader->getActiveAttributes(context);
}
// Assign locations to attributes that have a binding location and check for attribute aliasing. for (sh::ShaderVariable &attribute : mState.mExecutable->mProgramInputs)
{ // GLSL ES 3.10 January 2016 section 4.3.4: Vertex shader inputs can't be arrays or // structures, so we don't need to worry about adjusting their names or generating entries // for each member/element (unlike uniforms for example).
ASSERT(!attribute.isArray() && !attribute.isStruct());
int bindingLocation = mAttributeBindings.getBinding(attribute); if (attribute.location == -1 && bindingLocation != -1)
{
attribute.location = bindingLocation;
}
if (attribute.location != -1)
{ // Location is set by glBindAttribLocation or by location layout qualifier constint regs = VariableRegisterCount(attribute.type);
if (static_cast<GLuint>(regs + attribute.location) > maxAttribs)
{
infoLog << "Attribute (" << attribute.name << ") at location " << attribute.location
<< " is too big to fit";
// In GLSL ES 3.00.6 and in WebGL, attribute aliasing produces a link error. // In non-WebGL GLSL ES 1.00.17, attribute aliasing is allowed with some // restrictions - see GLSL ES 1.00.17 section 2.10.4, but ANGLE currently has a bug. // In D3D 9 and 11, aliasing is not supported, so check a limitation. if (linkedAttribute)
{ if (shaderVersion >= 300 || webglCompatibility ||
limitations.noVertexAttributeAliasing)
{
infoLog << "Attribute '" << attribute.name << "' aliases attribute '"
<< linkedAttribute->name << "' at location " << regLocation; returnfalse;
}
} else
{
usedAttribMap[regLocation] = &attribute;
}
usedLocations |= 1 << regLocation;
}
}
}
// Assign locations to attributes that don't have a binding location. for (sh::ShaderVariable &attribute : mState.mExecutable->mProgramInputs)
{ // Not set by glBindAttribLocation or by location layout qualifier if (attribute.location == -1)
{ int regs = VariableRegisterCount(attribute.type); int availableIndex = AllocateFirstFreeBits(&usedLocations, regs, maxAttribs);
// Prune inactive attributes. This step is only needed on shaderVersion >= 300 since on earlier // shader versions we're only processing active attributes to begin with. if (shaderVersion >= 300)
{ for (auto attributeIter = mState.mExecutable->getProgramInputs().begin();
attributeIter != mState.mExecutable->getProgramInputs().end();)
{ if (attributeIter->active)
{
++attributeIter;
} else
{
attributeIter = mState.mExecutable->mProgramInputs.erase(attributeIter);
}
}
}
unsignedint location = static_cast<unsignedint>(attribute.location); for (unsignedint r = 0; r < regs; r++)
{ // Built-in active program inputs don't have a bound attribute. if (!attribute.isBuiltIn())
{
mState.mExecutable->mActiveAttribLocationsMask.set(location);
mState.mExecutable->mMaxActiveAttribLocation =
std::max(mState.mExecutable->mMaxActiveAttribLocation, location + 1);
// Here we pass nullptr to avoid a large chain of calls that need a non-const Context. // We know it's safe not to notify the Context because this is only called after link.
setUniform1iv(nullptr, location, static_cast<GLsizei>(boundTextureUnits.size()),
boundTextureUnits.data());
}
}
}
void Program::initInterfaceBlockBindings()
{ // Set initial bindings from shader. for (unsignedint blockIndex = 0; blockIndex < mState.mExecutable->getActiveUniformBlockCount();
blockIndex++)
{
InterfaceBlock &uniformBlock = mState.mExecutable->mUniformBlocks[blockIndex];
bindUniformBlock({blockIndex}, uniformBlock.binding);
}
}
if (newSamplerFormat != samplerBinding.format)
{
mState.mExecutable->hasSamplerFormatConflict(newTextureUnit);
}
}
// Unset previously active sampler. if (oldRefCount == 0)
{
mState.mExecutable->setInactive(oldTextureUnit);
} else
{ if (oldSamplerType == TextureType::InvalidEnum ||
oldSamplerFormat == SamplerFormat::InvalidEnum)
{ // Previous conflict. Check if this new change fixed the conflict.
mState.setSamplerUniformTextureTypeAndFormat(oldTextureUnit);
}
}
// Update the observing PPO's executable, if any. // Do this before any of the Context work, since that uses the current ProgramExecutable, // which will be the PPO's if this Program is bound to it, rather than this Program's. if (isSeparable())
{
onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged);
}
// Notify context. if (context)
{
context->onSamplerUniformChange(newTextureUnit);
context->onSamplerUniformChange(oldTextureUnit);
}
}
// Invalidate the validation cache.
getExecutable().resetCachedValidateSamplersResult(); // Inform any PPOs this Program may be bound to.
onStateChange(angle::SubjectMessage::SamplerUniformsUpdated);
}
// OpenGL ES 3.0.4 spec pg 67: "Values for any array element that exceeds the highest array // element index used, as reported by GetActiveUniform, will be ignored by the GL." unsignedint remainingElements =
linkedUniform.getBasicTypeElementCount() - locationInfo.arrayIndex;
GLsizei maxElementCount = static_cast<GLsizei>(remainingElements * linkedUniform.getElementComponents());
// OpenGL ES 3.0.4 spec pg 67: "Values for any array element that exceeds the highest array // element index used, as reported by GetActiveUniform, will be ignored by the GL." unsignedint remainingElements =
linkedUniform.getBasicTypeElementCount() - locationInfo.arrayIndex; return std::min(count, static_cast<GLsizei>(remainingElements));
}
// Driver differences mean that doing the uniform value cast ourselves gives consistent results. // EG: on NVIDIA drivers, it was observed that getUniformi for MAX_INT+1 returned MIN_INT. template <typename DestT> void Program::getUniformInternal(const Context *context,
DestT *dataOut,
UniformLocation location,
GLenum nativeType, int components) const
{ switch (nativeType)
{ case GL_BOOL:
{
GLint tempValue[16] = {0};
mProgram->getUniformiv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLboolean>(
dataOut, reinterpret_cast<const uint8_t *>(tempValue), components); break;
} case GL_INT:
{
GLint tempValue[16] = {0};
mProgram->getUniformiv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue),
components); break;
} case GL_UNSIGNED_INT:
{
GLuint tempValue[16] = {0};
mProgram->getUniformuiv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLuint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue),
components); break;
} case GL_FLOAT:
{
GLfloat tempValue[16] = {0};
mProgram->getUniformfv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLfloat>(
dataOut, reinterpret_cast<const uint8_t *>(tempValue), components); break;
} default:
UNREACHABLE(); break;
}
}
// Warn the app layer if saving a binary with unsupported transform feedback. if (!mState.getLinkedTransformFeedbackVaryings().empty() &&
context->getFrontendFeatures().disableProgramCachingForTransformFeedback.enabled)
{
ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, "Saving program binary with transform feedback, which is not supported " "on this driver.");
}
if (context->getShareGroup()->getFrameCaptureShared()->enabled())
{ // Serialize the source for each stage for re-use during capture for (ShaderType shaderType : mState.mExecutable->getLinkedShaderStages())
{
gl::Shader *shader = getAttachedShader(shaderType); if (shader)
{
stream.writeString(shader->getSourceString());
} else
{ // If we don't have an attached shader, which would occur if this program was // created via glProgramBinary, pull from our cached copy const angle::ProgramSources &cachedLinkedSources =
context->getShareGroup()->getFrameCaptureShared()->getProgramSources(id()); const std::string &cachedSourceString = cachedLinkedSources[shaderType];
ASSERT(!cachedSourceString.empty());
stream.writeString(cachedSourceString.c_str());
}
}
}
mProgram->save(context, &stream);
ASSERT(binaryOut); if (!binaryOut->resize(stream.length()))
{
std::stringstream sstream;
sstream << "Failed to allocate enough memory to serialize a program. (" << stream.length()
<< " bytes )";
ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
sstream.str().c_str()); return angle::Result::Incomplete;
}
memcpy(binaryOut->data(), stream.data(), stream.length()); return angle::Result::Continue;
}
int majorVersion = stream.readInt<int>(); int minorVersion = stream.readInt<int>(); if (majorVersion != context->getClientMajorVersion() ||
minorVersion != context->getClientMinorVersion())
{
infoLog << "Cannot load program binaries across different ES context versions."; return angle::Result::Stop;
}
// Must be before mExecutable->load(), since it uses the value.
mState.mSeparable = stream.readBool();
static_assert(static_cast<unsignedlong>(ShaderType::EnumCount) <= sizeof(unsignedlong) * 8, "Too many shader types");
// Reject programs that use transform feedback varyings if the hardware cannot support them. if (mState.mExecutable->getLinkedTransformFeedbackVaryings().size() > 0 &&
context->getFrontendFeatures().disableProgramCachingForTransformFeedback.enabled)
{
infoLog << "Current driver does not support transform feedback in binary programs."; return angle::Result::Stop;
}
if (!mState.mAttachedShaders[ShaderType::Compute])
{
mState.mExecutable->updateTransformFeedbackStrides();
}
if (context->getShareGroup()->getFrameCaptureShared()->enabled())
{ // Extract the source for each stage from the program binary
angle::ProgramSources sources;
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.55Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-28)
¤
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.