// // 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);
¤ 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.0.38Bemerkung:
(vorverarbeitet)
¤
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.