blob: 6c6ce121d11ef68626676d22aa1b4f8b8efb0fc4 [file] [log] [blame]
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ProgramExecutable.cpp: Collects the interfaces common to both Programs and
// ProgramPipelines in order to execute/draw with either.
#include "libANGLE/ProgramExecutable.h"
#include "common/string_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/Program.h"
#include "libANGLE/Shader.h"
#include "libANGLE/queryconversions.h"
#include "libANGLE/renderer/GLImplFactory.h"
#include "libANGLE/renderer/ProgramExecutableImpl.h"
#include "libANGLE/renderer/ProgramImpl.h"
namespace gl
{
namespace
{
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
// A placeholder struct just to ensure sh::BlockMemberInfo is tightly packed since vulkan backend
// uses it and memcpy the entire vector which requires it tightly packed to make msan happy.
struct BlockMemberInfoPaddingTest
{
sh::BlockMemberInfo blockMemberInfo;
};
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
bool IncludeSameArrayElement(const std::set<std::string> &nameSet, const std::string &name)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(name, &subscripts);
for (const std::string &nameInSet : nameSet)
{
std::vector<unsigned int> arrayIndices;
std::string arrayName = ParseResourceName(nameInSet, &arrayIndices);
if (baseName == arrayName &&
(subscripts.empty() || arrayIndices.empty() || subscripts == arrayIndices))
{
return true;
}
}
return false;
}
// Find the matching varying or field by name.
const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &varyings,
ShaderType stage,
const std::string &name)
{
const sh::ShaderVariable *var = nullptr;
for (const ProgramVaryingRef &ref : varyings)
{
if (ref.frontShaderStage != stage)
{
continue;
}
const sh::ShaderVariable *varying = ref.get(stage);
if (varying->name == name)
{
var = varying;
break;
}
GLuint fieldIndex = 0;
var = varying->findField(name, &fieldIndex);
if (var != nullptr)
{
break;
}
}
return var;
}
bool FindUsedOutputLocation(std::vector<VariableLocation> &outputLocations,
unsigned int baseLocation,
unsigned int elementCount,
const std::vector<VariableLocation> &reservedLocations,
unsigned int variableIndex)
{
if (baseLocation + elementCount > outputLocations.size())
{
elementCount = baseLocation < outputLocations.size()
? static_cast<unsigned int>(outputLocations.size() - baseLocation)
: 0;
}
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
{
const unsigned int location = baseLocation + elementIndex;
if (outputLocations[location].used())
{
VariableLocation locationInfo(elementIndex, variableIndex);
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
reservedLocations.end())
{
return true;
}
}
}
return false;
}
void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
unsigned int baseLocation,
unsigned int elementCount,
const std::vector<VariableLocation> &reservedLocations,
unsigned int variableIndex,
ProgramOutput &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.pod.location = baseLocation;
const unsigned int location = baseLocation + elementIndex;
outputLocations[location] = locationInfo;
}
}
}
int GetOutputLocationForLink(const ProgramAliasedBindings &fragmentOutputLocations,
const ProgramOutput &outputVariable)
{
if (outputVariable.pod.location != -1)
{
return outputVariable.pod.location;
}
int apiLocation = fragmentOutputLocations.getBinding(outputVariable);
if (apiLocation != -1)
{
return apiLocation;
}
return -1;
}
bool IsOutputSecondaryForLink(const ProgramAliasedBindings &fragmentOutputIndexes,
const ProgramOutput &outputVariable)
{
if (outputVariable.pod.index != -1)
{
ASSERT(outputVariable.pod.index == 0 || outputVariable.pod.index == 1);
return (outputVariable.pod.index == 1);
}
int apiIndex = fragmentOutputIndexes.getBinding(outputVariable);
if (apiIndex != -1)
{
// Index layout qualifier from the shader takes precedence, so the index from the API is
// checked only if the index was not set in the shader. This is not specified in the EXT
// spec, but is specified in desktop OpenGL specs.
return (apiIndex == 1);
}
// EXT_blend_func_extended: Outputs get index 0 by default.
return false;
}
RangeUI AddUniforms(const ShaderMap<SharedProgramExecutable> &executables,
ShaderBitSet activeShaders,
std::vector<LinkedUniform> *outputUniforms,
std::vector<std::string> *outputUniformNames,
std::vector<std::string> *outputUniformMappedNames,
const std::function<RangeUI(const ProgramExecutable &)> &getRange)
{
unsigned int startRange = static_cast<unsigned int>(outputUniforms->size());
for (ShaderType shaderType : activeShaders)
{
const ProgramExecutable &executable = *executables[shaderType];
const RangeUI uniformRange = getRange(executable);
const std::vector<LinkedUniform> &programUniforms = executable.getUniforms();
outputUniforms->insert(outputUniforms->end(), programUniforms.begin() + uniformRange.low(),
programUniforms.begin() + uniformRange.high());
const std::vector<std::string> &uniformNames = executable.getUniformNames();
outputUniformNames->insert(outputUniformNames->end(),
uniformNames.begin() + uniformRange.low(),
uniformNames.begin() + uniformRange.high());
const std::vector<std::string> &uniformMappedNames = executable.getUniformMappedNames();
outputUniformMappedNames->insert(outputUniformMappedNames->end(),
uniformMappedNames.begin() + uniformRange.low(),
uniformMappedNames.begin() + uniformRange.high());
}
return RangeUI(startRange, static_cast<unsigned int>(outputUniforms->size()));
}
template <typename BlockT>
void AppendActiveBlocks(ShaderType shaderType,
const std::vector<BlockT> &blocksIn,
std::vector<BlockT> &blocksOut,
ProgramUniformBlockArray<GLuint> *ppoBlockMap)
{
for (size_t index = 0; index < blocksIn.size(); ++index)
{
const BlockT &block = blocksIn[index];
if (block.isActive(shaderType))
{
// Have a way for the PPO to know how to map the program's UBO index into its own UBO
// array. This is used to propagate changes to the program's UBOs to the PPO's UBO
// list.
if (ppoBlockMap != nullptr)
{
(*ppoBlockMap)[static_cast<uint32_t>(index)] =
static_cast<uint32_t>(blocksOut.size());
}
blocksOut.push_back(block);
}
}
}
void SaveProgramInputs(BinaryOutputStream *stream, const std::vector<ProgramInput> &programInputs)
{
stream->writeInt(programInputs.size());
for (const ProgramInput &attrib : programInputs)
{
stream->writeString(attrib.name);
stream->writeString(attrib.mappedName);
stream->writeStruct(attrib.pod);
}
}
void LoadProgramInputs(BinaryInputStream *stream, std::vector<ProgramInput> *programInputs)
{
size_t attribCount = stream->readInt<size_t>();
ASSERT(programInputs->empty());
if (attribCount > 0)
{
programInputs->resize(attribCount);
for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex)
{
ProgramInput &attrib = (*programInputs)[attribIndex];
stream->readString(&attrib.name);
stream->readString(&attrib.mappedName);
stream->readStruct(&attrib.pod);
}
}
}
void SaveUniforms(BinaryOutputStream *stream,
const std::vector<LinkedUniform> &uniforms,
const std::vector<std::string> &uniformNames,
const std::vector<std::string> &uniformMappedNames,
const std::vector<VariableLocation> &uniformLocations)
{
stream->writeVector(uniforms);
ASSERT(uniforms.size() == uniformNames.size());
ASSERT(uniforms.size() == uniformMappedNames.size());
for (const std::string &name : uniformNames)
{
stream->writeString(name);
}
for (const std::string &name : uniformMappedNames)
{
stream->writeString(name);
}
stream->writeVector(uniformLocations);
}
void LoadUniforms(BinaryInputStream *stream,
std::vector<LinkedUniform> *uniforms,
std::vector<std::string> *uniformNames,
std::vector<std::string> *uniformMappedNames,
std::vector<VariableLocation> *uniformLocations)
{
stream->readVector(uniforms);
if (!uniforms->empty())
{
uniformNames->resize(uniforms->size());
for (size_t uniformIndex = 0; uniformIndex < uniforms->size(); ++uniformIndex)
{
stream->readString(&(*uniformNames)[uniformIndex]);
}
uniformMappedNames->resize(uniforms->size());
for (size_t uniformIndex = 0; uniformIndex < uniforms->size(); ++uniformIndex)
{
stream->readString(&(*uniformMappedNames)[uniformIndex]);
}
}
stream->readVector(uniformLocations);
}
void SaveSamplerBindings(BinaryOutputStream *stream,
const std::vector<SamplerBinding> &samplerBindings,
const std::vector<GLuint> &samplerBoundTextureUnits)
{
stream->writeVector(samplerBindings);
stream->writeInt(samplerBoundTextureUnits.size());
}
void LoadSamplerBindings(BinaryInputStream *stream,
std::vector<SamplerBinding> *samplerBindings,
std::vector<GLuint> *samplerBoundTextureUnits)
{
stream->readVector(samplerBindings);
ASSERT(samplerBoundTextureUnits->empty());
size_t boundTextureUnitsCount = stream->readInt<size_t>();
samplerBoundTextureUnits->resize(boundTextureUnitsCount, 0);
}
void WriteBufferVariable(BinaryOutputStream *stream, const BufferVariable &var)
{
stream->writeString(var.name);
stream->writeString(var.mappedName);
stream->writeStruct(var.pod);
}
void LoadBufferVariable(BinaryInputStream *stream, BufferVariable *var)
{
var->name = stream->readString();
var->mappedName = stream->readString();
stream->readStruct(&var->pod);
}
void WriteAtomicCounterBuffer(BinaryOutputStream *stream, const AtomicCounterBuffer &var)
{
stream->writeVector(var.memberIndexes);
stream->writeStruct(var.pod);
}
void LoadAtomicCounterBuffer(BinaryInputStream *stream, AtomicCounterBuffer *var)
{
stream->readVector(&var->memberIndexes);
stream->readStruct(&var->pod);
}
void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
{
stream->writeString(block.name);
stream->writeString(block.mappedName);
stream->writeVector(block.memberIndexes);
stream->writeStruct(block.pod);
}
void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
{
block->name = stream->readString();
block->mappedName = stream->readString();
stream->readVector(&block->memberIndexes);
stream->readStruct(&block->pod);
}
void CopyStringToBuffer(GLchar *buffer,
const std::string &string,
GLsizei bufSize,
GLsizei *lengthOut)
{
ASSERT(bufSize > 0);
size_t length = std::min<size_t>(bufSize - 1, string.length());
memcpy(buffer, string.c_str(), length);
buffer[length] = '\0';
if (lengthOut)
{
*lengthOut = static_cast<GLsizei>(length);
}
}
template <typename T>
GLuint GetResourceMaxNameSize(const T &resource, GLint max)
{
if (resource.isArray())
{
return std::max(max, clampCast<GLint>((resource.name + "[0]").size()));
}
else
{
return std::max(max, clampCast<GLint>((resource.name).size()));
}
}
template <typename T>
GLuint GetResourceLocation(const GLchar *name, const T &variable, GLint location)
{
if (variable.isBuiltIn())
{
return GL_INVALID_INDEX;
}
if (variable.isArray())
{
size_t nameLengthWithoutArrayIndexOut;
size_t arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndexOut);
// The 'name' string may not contain the array notation "[0]"
if (arrayIndex != GL_INVALID_INDEX)
{
location += arrayIndex;
}
}
return location;
}
template <typename T>
const std::string GetResourceName(const T &resource)
{
std::string resourceName = resource.name;
if (resource.isArray())
{
resourceName += "[0]";
}
return resourceName;
}
GLint GetVariableLocation(const std::vector<gl::ProgramOutput> &list,
const std::vector<VariableLocation> &locationList,
const std::string &name)
{
size_t nameLengthWithoutArrayIndex;
unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
for (size_t location = 0u; location < locationList.size(); ++location)
{
const VariableLocation &variableLocation = locationList[location];
if (!variableLocation.used())
{
continue;
}
const gl::ProgramOutput &variable = list[variableLocation.index];
// 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))
{
return static_cast<GLint>(location);
}
if (variable.isArray() && variableLocation.arrayIndex == arrayIndex &&
angle::BeginsWith(variable.name, name, nameLengthWithoutArrayIndex))
{
return static_cast<GLint>(location);
}
}
return -1;
}
template <typename VarT>
GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string &name)
{
std::string nameAsArrayName = name + "[0]";
for (size_t index = 0; index < list.size(); index++)
{
const VarT &resource = list[index];
if (resource.name == name || (resource.isArray() && resource.name == nameAsArrayName))
{
return static_cast<GLuint>(index);
}
}
return GL_INVALID_INDEX;
}
GLuint GetUniformIndexFromName(const std::vector<LinkedUniform> &uniformList,
const std::vector<std::string> &nameList,
const std::string &name)
{
std::string nameAsArrayName = name + "[0]";
for (size_t index = 0; index < nameList.size(); index++)
{
const std::string &uniformName = nameList[index];
if (uniformName == name || (uniformList[index].isArray() && uniformName == nameAsArrayName))
{
return static_cast<GLuint>(index);
}
}
return GL_INVALID_INDEX;
}
GLint GetUniformLocation(const std::vector<LinkedUniform> &uniformList,
const std::vector<std::string> &nameList,
const std::vector<VariableLocation> &locationList,
const std::string &name)
{
size_t nameLengthWithoutArrayIndex;
unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
for (size_t location = 0u; location < locationList.size(); ++location)
{
const VariableLocation &variableLocation = locationList[location];
if (!variableLocation.used())
{
continue;
}
const LinkedUniform &variable = uniformList[variableLocation.index];
const std::string &uniformName = nameList[variableLocation.index];
// 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(uniformName, name) && (variableLocation.arrayIndex == 0))
{
if (name.length() == uniformName.length())
{
ASSERT(name == uniformName);
// GLES 3.1 November 2016 page 87.
// The string exactly matches the name of the active variable.
return static_cast<GLint>(location);
}
if (name.length() + 3u == uniformName.length() && variable.isArray())
{
ASSERT(name + "[0]" == uniformName);
// 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.
return static_cast<GLint>(location);
}
}
if (variable.isArray() && variableLocation.arrayIndex == arrayIndex &&
nameLengthWithoutArrayIndex + 3u == uniformName.length() &&
angle::BeginsWith(uniformName, name, nameLengthWithoutArrayIndex))
{
ASSERT(name.substr(0u, nameLengthWithoutArrayIndex) + "[0]" == uniformName);
// 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.
return static_cast<GLint>(location);
}
}
return -1;
}
GLuint GetInterfaceBlockIndex(const std::vector<InterfaceBlock> &list, const std::string &name)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(name, &subscripts);
unsigned int numBlocks = static_cast<unsigned int>(list.size());
for (unsigned int blockIndex = 0; blockIndex < numBlocks; blockIndex++)
{
const auto &block = list[blockIndex];
if (block.name == baseName)
{
const bool arrayElementZero =
(subscripts.empty() && (!block.pod.isArray || block.pod.arrayElement == 0));
const bool arrayElementMatches =
(subscripts.size() == 1 && subscripts[0] == block.pod.arrayElement);
if (arrayElementMatches || arrayElementZero)
{
return blockIndex;
}
}
}
return GL_INVALID_INDEX;
}
void GetInterfaceBlockName(const UniformBlockIndex index,
const std::vector<InterfaceBlock> &list,
GLsizei bufSize,
GLsizei *length,
GLchar *name)
{
ASSERT(index.value < list.size());
const auto &block = list[index.value];
if (bufSize > 0)
{
std::string blockName = block.name;
if (block.pod.isArray)
{
blockName += ArrayString(block.pod.arrayElement);
}
CopyStringToBuffer(name, blockName, bufSize, length);
}
}
template <typename T>
GLint GetActiveInterfaceBlockMaxNameLength(const std::vector<T> &resources)
{
int maxLength = 0;
for (const T &resource : resources)
{
if (!resource.name.empty())
{
int length = static_cast<int>(resource.nameWithArrayIndex().length());
maxLength = std::max(length + 1, maxLength);
}
}
return maxLength;
}
// 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);
// From-Float-To-Integer Casts
template <>
GLint UniformStateQueryCast(GLfloat value)
{
return clampCast<GLint>(roundf(value));
}
template <>
GLuint UniformStateQueryCast(GLfloat value)
{
return clampCast<GLuint>(roundf(value));
}
// From-Integer-to-Integer Casts
template <>
GLint UniformStateQueryCast(GLuint value)
{
return clampCast<GLint>(value);
}
template <>
GLuint UniformStateQueryCast(GLint value)
{
return clampCast<GLuint>(value);
}
// From-Boolean-to-Anything Casts
template <>
GLfloat UniformStateQueryCast(GLboolean value)
{
return (ConvertToBool(value) ? 1.0f : 0.0f);
}
template <>
GLint UniformStateQueryCast(GLboolean value)
{
return (ConvertToBool(value) ? 1 : 0);
}
template <>
GLuint UniformStateQueryCast(GLboolean value)
{
return (ConvertToBool(value) ? 1u : 0u);
}
// Default to static_cast
template <typename DestT, typename SrcT>
DestT UniformStateQueryCast(SrcT value)
{
return static_cast<DestT>(value);
}
template <typename SrcT, typename DestT>
void UniformStateQueryCastLoop(DestT *dataOut, const uint8_t *srcPointer, int components)
{
for (int comp = 0; comp < components; ++comp)
{
// We only work with strides of 4 bytes for uniform components. (GLfloat/GLint)
// Don't use SrcT stride directly since GLboolean has a stride of 1 byte.
size_t offset = comp * 4;
const SrcT *typedSrcPointer = reinterpret_cast<const SrcT *>(&srcPointer[offset]);
dataOut[comp] = UniformStateQueryCast<DestT>(*typedSrcPointer);
}
}
} // anonymous namespace
// ImageBinding implementation.
ImageBinding::ImageBinding(GLuint imageUnit, size_t count, TextureType textureTypeIn)
: textureType(textureTypeIn)
{
for (size_t index = 0; index < count; ++index)
{
boundImageUnits.push_back(imageUnit + static_cast<GLuint>(index));
}
}
// ProgramInput implementation.
ProgramInput::ProgramInput(const sh::ShaderVariable &var)
{
ASSERT(!var.isStruct());
name = var.name;
mappedName = var.mappedName;
SetBitField(pod.type, var.type);
pod.location = var.hasImplicitLocation ? -1 : var.location;
SetBitField(pod.interpolation, var.interpolation);
pod.flagBitsAsUByte = 0;
pod.flagBits.active = var.active;
pod.flagBits.isPatch = var.isPatch;
pod.flagBits.hasImplicitLocation = var.hasImplicitLocation;
pod.flagBits.isArray = var.isArray();
pod.flagBits.isBuiltIn = IsBuiltInName(var.name);
SetBitField(pod.basicTypeElementCount, var.getBasicTypeElementCount());
pod.id = var.id;
SetBitField(pod.arraySizeProduct, var.getArraySizeProduct());
}
// ProgramOutput implementation.
ProgramOutput::ProgramOutput(const sh::ShaderVariable &var)
{
name = var.name;
mappedName = var.mappedName;
pod.type = var.type;
pod.location = var.location;
pod.index = var.index;
pod.id = var.id;
SetBitField(pod.outermostArraySize, var.getOutermostArraySize());
SetBitField(pod.basicTypeElementCount, var.getBasicTypeElementCount());
SetBitField(pod.isPatch, var.isPatch);
SetBitField(pod.yuv, var.yuv);
SetBitField(pod.isBuiltIn, IsBuiltInName(var.name));
SetBitField(pod.isArray, var.isArray());
SetBitField(pod.hasImplicitLocation, var.hasImplicitLocation);
SetBitField(pod.pad, 0);
}
// ProgramExecutable implementation.
ProgramExecutable::ProgramExecutable(rx::GLImplFactory *factory, InfoLog *infoLog)
: mImplementation(factory->createProgramExecutable(this)),
mInfoLog(infoLog),
mCachedBaseVertex(0),
mCachedBaseInstance(0)
{
memset(&mPod, 0, sizeof(mPod));
reset();
}
ProgramExecutable::~ProgramExecutable()
{
ASSERT(mPostLinkSubTasks.empty());
ASSERT(mPostLinkSubTaskWaitableEvents.empty());
ASSERT(mImplementation == nullptr);
}
void ProgramExecutable::destroy(const Context *context)
{
ASSERT(mImplementation != nullptr);
mImplementation->destroy(context);
SafeDelete(mImplementation);
}
void ProgramExecutable::reset()
{
mPod.activeAttribLocationsMask.reset();
mPod.attributesTypeMask.reset();
mPod.attributesMask.reset();
mPod.maxActiveAttribLocation = 0;
mPod.activeOutputVariablesMask.reset();
mPod.activeSecondaryOutputVariablesMask.reset();
mPod.defaultUniformRange = RangeUI(0, 0);
mPod.samplerUniformRange = RangeUI(0, 0);
mPod.imageUniformRange = RangeUI(0, 0);
mPod.atomicCounterUniformRange = RangeUI(0, 0);
mPod.fragmentInoutIndices.reset();
mPod.hasClipDistance = false;
mPod.hasDiscard = false;
mPod.enablesPerSampleShading = false;
mPod.hasYUVOutput = false;
mPod.advancedBlendEquations.reset();
mPod.geometryShaderInputPrimitiveType = PrimitiveMode::Triangles;
mPod.geometryShaderOutputPrimitiveType = PrimitiveMode::TriangleStrip;
mPod.geometryShaderInvocations = 1;
mPod.geometryShaderMaxVertices = 0;
mPod.transformFeedbackBufferMode = GL_INTERLEAVED_ATTRIBS;
mPod.numViews = -1;
mPod.drawIDLocation = -1;
mPod.baseVertexLocation = -1;
mPod.baseInstanceLocation = -1;
mPod.tessControlShaderVertices = 0;
mPod.tessGenMode = GL_NONE;
mPod.tessGenSpacing = GL_NONE;
mPod.tessGenVertexOrder = GL_NONE;
mPod.tessGenPointMode = GL_NONE;
mPod.drawBufferTypeMask.reset();
mPod.computeShaderLocalSize.fill(1);
mPod.specConstUsageBits.reset();
mActiveSamplersMask.reset();
mActiveSamplerRefCounts = {};
mActiveSamplerTypes.fill(TextureType::InvalidEnum);
mActiveSamplerYUV.reset();
mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum);
mActiveImagesMask.reset();
mUniformBlockIndexToBufferBinding = {};
mProgramInputs.clear();
mLinkedTransformFeedbackVaryings.clear();
mTransformFeedbackStrides.clear();
mUniforms.clear();
mUniformNames.clear();
mUniformMappedNames.clear();
mUniformBlocks.clear();
mUniformLocations.clear();
mShaderStorageBlocks.clear();
mAtomicCounterBuffers.clear();
mBufferVariables.clear();
mOutputVariables.clear();
mOutputLocations.clear();
mSecondaryOutputLocations.clear();
mSamplerBindings.clear();
mSamplerBoundTextureUnits.clear();
mImageBindings.clear();
mPostLinkSubTasks.clear();
mPostLinkSubTaskWaitableEvents.clear();
}
void ProgramExecutable::load(gl::BinaryInputStream *stream)
{
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"Too many vertex attribs for mask: All bits of mAttributesTypeMask types and "
"mask fit into 32 bits each");
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");
stream->readStruct(&mPod);
LoadProgramInputs(stream, &mProgramInputs);
LoadUniforms(stream, &mUniforms, &mUniformNames, &mUniformMappedNames, &mUniformLocations);
size_t uniformBlockCount = stream->readInt<size_t>();
ASSERT(getUniformBlocks().empty());
mUniformBlocks.resize(uniformBlockCount);
for (size_t uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
{
InterfaceBlock &uniformBlock = mUniformBlocks[uniformBlockIndex];
LoadInterfaceBlock(stream, &uniformBlock);
}
size_t shaderStorageBlockCount = stream->readInt<size_t>();
ASSERT(getShaderStorageBlocks().empty());
mShaderStorageBlocks.resize(shaderStorageBlockCount);
for (size_t shaderStorageBlockIndex = 0; shaderStorageBlockIndex < shaderStorageBlockCount;
++shaderStorageBlockIndex)
{
InterfaceBlock &shaderStorageBlock = mShaderStorageBlocks[shaderStorageBlockIndex];
LoadInterfaceBlock(stream, &shaderStorageBlock);
}
size_t atomicCounterBufferCount = stream->readInt<size_t>();
ASSERT(getAtomicCounterBuffers().empty());
mAtomicCounterBuffers.resize(atomicCounterBufferCount);
for (size_t bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
{
AtomicCounterBuffer &atomicCounterBuffer = mAtomicCounterBuffers[bufferIndex];
LoadAtomicCounterBuffer(stream, &atomicCounterBuffer);
}
size_t bufferVariableCount = stream->readInt<size_t>();
ASSERT(getBufferVariables().empty());
mBufferVariables.resize(bufferVariableCount);
for (size_t bufferVarIndex = 0; bufferVarIndex < bufferVariableCount; ++bufferVarIndex)
{
LoadBufferVariable(stream, &mBufferVariables[bufferVarIndex]);
}
size_t transformFeedbackVaryingCount = stream->readInt<size_t>();
ASSERT(mLinkedTransformFeedbackVaryings.empty());
mLinkedTransformFeedbackVaryings.resize(transformFeedbackVaryingCount);
for (size_t transformFeedbackVaryingIndex = 0;
transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
++transformFeedbackVaryingIndex)
{
TransformFeedbackVarying &varying =
mLinkedTransformFeedbackVaryings[transformFeedbackVaryingIndex];
stream->readVector(&varying.arraySizes);
stream->readInt(&varying.type);
stream->readString(&varying.name);
varying.arrayIndex = stream->readInt<GLuint>();
}
size_t outputCount = stream->readInt<size_t>();
ASSERT(getOutputVariables().empty());
mOutputVariables.resize(outputCount);
for (size_t outputIndex = 0; outputIndex < outputCount; ++outputIndex)
{
ProgramOutput &output = mOutputVariables[outputIndex];
stream->readString(&output.name);
stream->readString(&output.mappedName);
stream->readStruct(&output.pod);
}
stream->readVector(&mOutputLocations);
stream->readVector(&mSecondaryOutputLocations);
LoadSamplerBindings(stream, &mSamplerBindings, &mSamplerBoundTextureUnits);
size_t imageBindingCount = stream->readInt<size_t>();
ASSERT(mImageBindings.empty());
mImageBindings.resize(imageBindingCount);
for (size_t imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
{
ImageBinding &imageBinding = mImageBindings[imageIndex];
size_t elementCount = stream->readInt<size_t>();
imageBinding.textureType = static_cast<TextureType>(stream->readInt<unsigned int>());
imageBinding.boundImageUnits.resize(elementCount);
for (size_t elementIndex = 0; elementIndex < elementCount; ++elementIndex)
{
imageBinding.boundImageUnits[elementIndex] = stream->readInt<unsigned int>();
}
}
// These values are currently only used by PPOs, so only load them when the program is marked
// separable to save memory.
if (mPod.isSeparable)
{
for (ShaderType shaderType : getLinkedShaderStages())
{
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);
}
}
}
}
void ProgramExecutable::save(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");
static_assert(
IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
"All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
ASSERT(mPod.geometryShaderInvocations >= 1 && mPod.geometryShaderMaxVertices >= 0);
stream->writeStruct(mPod);
SaveProgramInputs(stream, mProgramInputs);
SaveUniforms(stream, mUniforms, mUniformNames, mUniformMappedNames, mUniformLocations);
stream->writeInt(getUniformBlocks().size());
for (const InterfaceBlock &uniformBlock : getUniformBlocks())
{
WriteInterfaceBlock(stream, uniformBlock);
}
stream->writeInt(getShaderStorageBlocks().size());
for (const InterfaceBlock &shaderStorageBlock : getShaderStorageBlocks())
{
WriteInterfaceBlock(stream, shaderStorageBlock);
}
stream->writeInt(mAtomicCounterBuffers.size());
for (const AtomicCounterBuffer &atomicCounterBuffer : getAtomicCounterBuffers())
{
WriteAtomicCounterBuffer(stream, atomicCounterBuffer);
}
stream->writeInt(getBufferVariables().size());
for (const BufferVariable &bufferVariable : getBufferVariables())
{
WriteBufferVariable(stream, bufferVariable);
}
stream->writeInt(getLinkedTransformFeedbackVaryings().size());
for (const auto &var : getLinkedTransformFeedbackVaryings())
{
stream->writeVector(var.arraySizes);
stream->writeInt(var.type);
stream->writeString(var.name);
stream->writeIntOrNegOne(var.arrayIndex);
}
stream->writeInt(getOutputVariables().size());
for (const ProgramOutput &output : getOutputVariables())
{
stream->writeString(output.name);
stream->writeString(output.mappedName);
stream->writeStruct(output.pod);
}
stream->writeVector(mOutputLocations);
stream->writeVector(mSecondaryOutputLocations);
SaveSamplerBindings(stream, mSamplerBindings, mSamplerBoundTextureUnits);
stream->writeInt(getImageBindings().size());
for (const auto &imageBinding : getImageBindings())
{
stream->writeInt(imageBinding.boundImageUnits.size());
stream->writeInt(static_cast<unsigned int>(imageBinding.textureType));
for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
{
stream->writeInt(imageBinding.boundImageUnits[i]);
}
}
// These values are currently only used by PPOs, so only save them when the program is marked
// separable to save memory.
if (mPod.isSeparable)
{
for (ShaderType shaderType : getLinkedShaderStages())
{
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);
}
}
}
}
std::string ProgramExecutable::getInfoLogString() const
{
return mInfoLog->str();
}
ShaderType ProgramExecutable::getFirstLinkedShaderStageType() const
{
const ShaderBitSet linkedStages = mPod.linkedShaderStages;
if (linkedStages.none())
{
return ShaderType::InvalidEnum;
}
return linkedStages.first();
}
ShaderType ProgramExecutable::getLastLinkedShaderStageType() const
{
const ShaderBitSet linkedStages = mPod.linkedShaderStages;
if (linkedStages.none())
{
return ShaderType::InvalidEnum;
}
return linkedStages.last();
}
void ProgramExecutable::setActive(size_t textureUnit,
const SamplerBinding &samplerBinding,
const gl::LinkedUniform &samplerUniform)
{
mActiveSamplersMask.set(textureUnit);
mActiveSamplerTypes[textureUnit] = samplerBinding.textureType;
mActiveSamplerYUV[textureUnit] = IsSamplerYUVType(samplerBinding.samplerType);
mActiveSamplerFormats[textureUnit] = samplerBinding.format;
mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders();
}
void ProgramExecutable::setInactive(size_t textureUnit)
{
mActiveSamplersMask.reset(textureUnit);
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
mActiveSamplerYUV.reset(textureUnit);
mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
mActiveSamplerShaderBits[textureUnit].reset();
}
void ProgramExecutable::hasSamplerTypeConflict(size_t textureUnit)
{
// Conflicts are marked with InvalidEnum
mActiveSamplerYUV.reset(textureUnit);
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
}
void ProgramExecutable::hasSamplerFormatConflict(size_t textureUnit)
{
// Conflicts are marked with InvalidEnum
mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
}
void ProgramExecutable::updateActiveSamplers(const ProgramExecutable &executable)
{
const std::vector<SamplerBinding> &samplerBindings = executable.getSamplerBindings();
const std::vector<GLuint> &boundTextureUnits = executable.getSamplerBoundTextureUnits();
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
for (uint16_t index = 0; index < samplerBinding.textureUnitsCount; index++)
{
GLint textureUnit = samplerBinding.getTextureUnit(boundTextureUnits, index);
if (++mActiveSamplerRefCounts[textureUnit] == 1)
{
uint32_t uniformIndex = executable.getUniformIndexFromSamplerIndex(samplerIndex);
setActive(textureUnit, samplerBinding, executable.getUniforms()[uniformIndex]);
}
else
{
if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType ||
mActiveSamplerYUV.test(textureUnit) !=
IsSamplerYUVType(samplerBinding.samplerType))
{
hasSamplerTypeConflict(textureUnit);
}
if (mActiveSamplerFormats[textureUnit] != samplerBinding.format)
{
hasSamplerFormatConflict(textureUnit);
}
}
mActiveSamplersMask.set(textureUnit);
}
}
// Invalidate the validation cache.
resetCachedValidateSamplersResult();
}
void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable)
{
const std::vector<ImageBinding> &imageBindings = executable.getImageBindings();
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings.at(imageIndex);
uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = executable.getUniforms()[uniformIndex];
const ShaderBitSet shaderBits = imageUniform.activeShaders();
for (GLint imageUnit : imageBinding.boundImageUnits)
{
mActiveImagesMask.set(imageUnit);
mActiveImageShaderBits[imageUnit] |= shaderBits;
}
}
}
void ProgramExecutable::setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex)
{
bool foundBinding = false;
TextureType foundType = TextureType::InvalidEnum;
bool foundYUV = false;
SamplerFormat foundFormat = SamplerFormat::InvalidEnum;
for (uint32_t samplerIndex = 0; samplerIndex < mSamplerBindings.size(); ++samplerIndex)
{
const SamplerBinding &binding = mSamplerBindings[samplerIndex];
// A conflict exists if samplers of different types are sourced by the same texture unit.
// We need to check all bound textures to detect this error case.
for (uint16_t index = 0; index < binding.textureUnitsCount; index++)
{
GLuint textureUnit = binding.getTextureUnit(mSamplerBoundTextureUnits, index);
if (textureUnit != textureUnitIndex)
{
continue;
}
if (!foundBinding)
{
foundBinding = true;
foundType = binding.textureType;
foundYUV = IsSamplerYUVType(binding.samplerType);
foundFormat = binding.format;
uint32_t uniformIndex = getUniformIndexFromSamplerIndex(samplerIndex);
setActive(textureUnit, binding, mUniforms[uniformIndex]);
}
else
{
if (foundType != binding.textureType ||
foundYUV != IsSamplerYUVType(binding.samplerType))
{
hasSamplerTypeConflict(textureUnit);
}
if (foundFormat != binding.format)
{
hasSamplerFormatConflict(textureUnit);
}
}
}
}
}
void ProgramExecutable::saveLinkedStateInfo(const ProgramState &state)
{
for (ShaderType shaderType : getLinkedShaderStages())
{
const SharedCompiledShaderState &shader = state.getAttachedShader(shaderType);
ASSERT(shader);
mPod.linkedShaderVersions[shaderType] = shader->shaderVersion;
mLinkedOutputVaryings[shaderType] = shader->outputVaryings;
mLinkedInputVaryings[shaderType] = shader->inputVaryings;
mLinkedUniforms[shaderType] = shader->uniforms;
mLinkedUniformBlocks[shaderType] = shader->uniformBlocks;
}
}
bool ProgramExecutable::linkMergedVaryings(const Caps &caps,
const Limitations &limitations,
const Version &clientVersion,
bool webglCompatibility,
const ProgramMergedVaryings &mergedVaryings,
const LinkingVariables &linkingVariables,
ProgramVaryingPacking *varyingPacking)
{
ShaderType tfStage = GetLastPreFragmentStage(linkingVariables.isShaderStageUsedBitset);
if (!linkValidateTransformFeedback(caps, clientVersion, mergedVaryings, tfStage))
{
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 (limitations.noFlexibleVaryingPacking)
{
// D3D9 pack mode is strictly more strict than WebGL, so takes priority.
packMode = PackMode::ANGLE_NON_CONFORMANT_D3D9;
}
else if (webglCompatibility)
{
packMode = PackMode::WEBGL_STRICT;
}
// Build active shader stage map.
ShaderBitSet activeShadersMask;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
// - Check for attached shaders to handle the case of a Program linking the currently
// attached shaders.
// - Check for linked shaders to handle the case of a PPO linking separable programs before
// drawing.
if (linkingVariables.isShaderStageUsedBitset[shaderType] ||
getLinkedShaderStages().test(shaderType))
{
activeShadersMask[shaderType] = true;
}
}
if (!varyingPacking->collectAndPackUserVaryings(*mInfoLog, caps, packMode, activeShadersMask,
mergedVaryings, mTransformFeedbackVaryingNames,
mPod.isSeparable))
{
return false;
}
gatherTransformFeedbackVaryings(mergedVaryings, tfStage);
updateTransformFeedbackStrides();
return true;
}
bool ProgramExecutable::linkValidateTransformFeedback(const Caps &caps,
const Version &clientVersion,
const ProgramMergedVaryings &varyings,
ShaderType stage)
{
// Validate the tf names regardless of the actual program varyings.
std::set<std::string> uniqueNames;
for (const std::string &tfVaryingName : mTransformFeedbackVaryingNames)
{
if (clientVersion < Version(3, 1) && tfVaryingName.find('[') != std::string::npos)
{
*mInfoLog << "Capture of array elements is undefined and not supported.";
return false;
}
if (clientVersion >= 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 (mTransformFeedbackVaryingNames.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 : mTransformFeedbackVaryingNames)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
const sh::ShaderVariable *var = FindOutputVaryingOrField(varyings, stage, baseName);
if (var == nullptr)
{
*mInfoLog << "Transform feedback varying " << tfVaryingName
<< " does not exist in the vertex shader.";
return false;
}
// Validate the matching variable.
if (var->isStruct())
{
*mInfoLog << "Struct cannot be captured directly (" << baseName << ").";
return false;
}
size_t elementCount = 0;
size_t componentCount = 0;
if (var->isArray())
{
if (clientVersion < Version(3, 1))
{
*mInfoLog << "Capture of arrays is undefined and not supported.";
return false;
}
// GLSL ES 3.10 section 4.3.6: A vertex output can't be an array of arrays.
ASSERT(!var->isArrayOfArrays());
if (!subscripts.empty() && subscripts[0] >= var->getOutermostArraySize())
{
*mInfoLog << "Cannot capture outbound array element '" << tfVaryingName << "'.";
return false;
}
elementCount = (subscripts.empty() ? var->getOutermostArraySize() : 1);
}
else
{
if (!subscripts.empty())
{
*mInfoLog << "Varying '" << baseName
<< "' is not an array to be captured by element.";
return false;
}
elementCount = 1;
}
componentCount = VariableComponentCount(var->type) * elementCount;
if (mPod.transformFeedbackBufferMode == GL_SEPARATE_ATTRIBS &&
componentCount > static_cast<GLuint>(caps.maxTransformFeedbackSeparateComponents))
{
*mInfoLog << "Transform feedback varying " << tfVaryingName << " components ("
<< componentCount << ") exceed the maximum separate components ("
<< caps.maxTransformFeedbackSeparateComponents << ").";
return false;
}
totalComponents += componentCount;
if (mPod.transformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS &&
totalComponents > static_cast<GLuint>(caps.maxTransformFeedbackInterleavedComponents))
{
*mInfoLog << "Transform feedback varying total components (" << totalComponents
<< ") exceed the maximum interleaved components ("
<< caps.maxTransformFeedbackInterleavedComponents << ").";
return false;
}
}
return true;
}
void ProgramExecutable::gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings,
ShaderType stage)
{
// Gather the linked varyings that are used for transform feedback, they should all exist.
mLinkedTransformFeedbackVaryings.clear();
for (const std::string &tfVaryingName : mTransformFeedbackVaryingNames)
{
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
size_t subscript = GL_INVALID_INDEX;
if (!subscripts.empty())
{
subscript = subscripts.back();
}
for (const ProgramVaryingRef &ref : varyings)
{
if (ref.frontShaderStage != stage)
{
continue;
}
const sh::ShaderVariable *varying = ref.get(stage);
if (baseName == varying->name)
{
mLinkedTransformFeedbackVaryings.emplace_back(*varying,
static_cast<GLuint>(subscript));
break;
}
else if (varying->isStruct())
{
GLuint fieldIndex = 0;
const auto *field = varying->findField(tfVaryingName, &fieldIndex);
if (field != nullptr)
{
mLinkedTransformFeedbackVaryings.emplace_back(*field, *varying);
break;
}
}
}
}
}
void ProgramExecutable::updateTransformFeedbackStrides()
{
if (mLinkedTransformFeedbackVaryings.empty())
{
return;
}
if (mPod.transformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS)
{
mTransformFeedbackStrides.resize(1);
size_t totalSize = 0;
for (const TransformFeedbackVarying &varying : mLinkedTransformFeedbackVaryings)
{
totalSize += varying.size() * VariableExternalSize(varying.type);
}
mTransformFeedbackStrides[0] = static_cast<GLsizei>(totalSize);
}
else
{
mTransformFeedbackStrides.resize(mLinkedTransformFeedbackVaryings.size());
for (size_t i = 0; i < mLinkedTransformFeedbackVaryings.size(); i++)
{
TransformFeedbackVarying &varying = mLinkedTransformFeedbackVaryings[i];
mTransformFeedbackStrides[i] =
static_cast<GLsizei>(varying.size() * VariableExternalSize(varying.type));
}
}
}
bool ProgramExecutable::validateSamplersImpl(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)
{
mCachedValidateSamplersResult = false;
return false;
}
if (mActiveSamplerFormats[textureUnit] == SamplerFormat::InvalidEnum)
{
mCachedValidateSamplersResult = false;
return false;
}
}
mCachedValidateSamplersResult = true;
return true;
}
bool ProgramExecutable::linkValidateOutputVariables(
const Caps &caps,
const Version &version,
GLuint combinedImageUniformsCount,
GLuint combinedShaderStorageBlocksCount,
int fragmentShaderVersion,
const ProgramAliasedBindings &fragmentOutputLocations,
const ProgramAliasedBindings &fragmentOutputIndices)
{
ASSERT(mPod.activeOutputVariablesMask.none());
ASSERT(mPod.activeSecondaryOutputVariablesMask.none());
ASSERT(mPod.drawBufferTypeMask.none());
ASSERT(!mPod.hasYUVOutput);
if (fragmentShaderVersion == 100)
{
return gatherOutputTypes();
}
// EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
// output array in explicit terms.
//
// Assuming fragData is an output array, you can defend the position that:
// P1) you must support binding "fragData" because it's specified
// P2) you must support querying "fragData[x]" because it's specified
// P3) you must support binding "fragData[0]" because it's a frequently used pattern
//
// Then you can make the leap of faith:
// P4) you must support binding "fragData[x]" because you support "fragData[0]"
// P5) you must support binding "fragData[x]" because you support querying "fragData[x]"
//
// The spec brings in the "world of arrays" when it mentions binding the arrays and the
// automatic binding. Thus it must be interpreted that the thing is not undefined, rather you
// must infer the only possible interpretation (?). Note again: this need of interpretation
// might be completely off of what GL spec logic is.
//
// The other complexity is that unless you implement this feature, it's hard to understand what
// should happen when the client invokes the feature. You cannot add an additional error as it
// is not specified. One can ignore it, but obviously it creates the discrepancies...
std::vector<VariableLocation> reservedLocations;
// Process any output API bindings for arrays that don't alias to the first element.
for (const auto &bindingPair : fragmentOutputLocations)
{
const std::string &name = bindingPair.first;
const ProgramBinding &binding = bindingPair.second;
size_t nameLengthWithoutArrayIndex;
unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
{
continue;
}
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
outputVariableIndex++)
{
const ProgramOutput &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 ProgramOutput &outputVariable = mOutputVariables[outputVariableIndex];
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
if (outputVariable.isBuiltIn())
continue;
int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable);
if (fixedLocation == -1)
{
// Here we're only reserving locations for variables whose location is fixed.
continue;
}
unsigned int baseLocation = static_cast<unsigned int>(fixedLocation);
std::vector<VariableLocation> &outputLocations =
IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable)
? mSecondaryOutputLocations
: mOutputLocations;
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
// structures, so we may use getBasicTypeElementCount().
unsigned int elementCount = outputVariable.pod.basicTypeElementCount;
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 ProgramOutput &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.pod.basicTypeElementCount;
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;
}
}
if (!gatherOutputTypes())
{
return false;
}
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 +
mPod.activeOutputVariablesMask.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;
}
}
return true;
}
bool ProgramExecutable::gatherOutputTypes()
{
for (const ProgramOutput &outputVariable : mOutputVariables)
{
if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" &&
outputVariable.name != "gl_FragData" &&
outputVariable.name != "gl_SecondaryFragColorEXT" &&
outputVariable.name != "gl_SecondaryFragDataEXT")
{
continue;
}
unsigned int baseLocation = (outputVariable.pod.location == -1
? 0u
: static_cast<unsigned int>(outputVariable.pod.location));
const bool secondary =
outputVariable.pod.index == 1 || (outputVariable.name == "gl_SecondaryFragColorEXT" ||
outputVariable.name == "gl_SecondaryFragDataEXT");
const ComponentType componentType =
GLenumToComponentType(VariableComponentType(outputVariable.pod.type));
// 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.pod.basicTypeElementCount;
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
{
const unsigned int location = baseLocation + elementIndex;
ASSERT(location < mPod.activeOutputVariablesMask.size());
ASSERT(location < mPod.activeSecondaryOutputVariablesMask.size());
if (secondary)
{
mPod.activeSecondaryOutputVariablesMask.set(location);
}
else
{
mPod.activeOutputVariablesMask.set(location);
}
const ComponentType storedComponentType =
gl::GetComponentTypeMask(mPod.drawBufferTypeMask, location);
if (storedComponentType == ComponentType::InvalidEnum)
{
SetComponentTypeMask(componentType, location, &mPod.drawBufferTypeMask);
}
else if (storedComponentType != componentType)
{
*mInfoLog << "Inconsistent component types for fragment outputs at location "
<< location;
return false;
}
}
if (outputVariable.pod.yuv)
{
ASSERT(mOutputVariables.size() == 1);
mPod.hasYUVOutput = true;
}
}
return true;
}
bool ProgramExecutable::linkUniforms(
const Caps &caps,
const ShaderMap<std::vector<sh::ShaderVariable>> &shaderUniforms,
const ProgramAliasedBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCountOut,
std::vector<UnusedUniform> *unusedUniformsOutOrNull)
{
UniformLinker linker(mPod.linkedShaderStages, shaderUniforms);
if (!linker.link(caps, *mInfoLog, uniformLocationBindings))
{
return false;
}
linker.getResults(&mUniforms, &mUniformNames, &mUniformMappedNames, unusedUniformsOutOrNull,
&mUniformLocations);
linkSamplerAndImageBindings(combinedImageUniformsCountOut);
if (!linkAtomicCounterBuffers(caps))
{
return false;
}
return true;
}
void ProgramExecutable::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
{
ASSERT(combinedImageUniforms);
// Iterate over mExecutable->mUniforms from the back, and find the range of subpass inputs,
// atomic counters, images and samplers in that order.
auto highIter = mUniforms.rbegin();
auto lowIter = highIter;
unsigned int high = static_cast<unsigned int>(mUniforms.size());
unsigned int low = high;
// Note that uniform block uniforms are not yet appended to this list.
ASSERT(mUniforms.empty() || highIter->isAtomicCounter() || highIter->isImage() ||
highIter->isSampler() || highIter->isInDefaultBlock());
for (; lowIter != mUniforms.rend() && lowIter->isAtomicCounter(); ++lowIter)
{
--low;
}
mPod.atomicCounterUniformRange = RangeUI(low, high);
highIter = lowIter;
high = low;
for (; lowIter != mUniforms.rend() && lowIter->isImage(); ++lowIter)
{
--low;
}
mPod.imageUniformRange = RangeUI(low, high);
*combinedImageUniforms = 0u;
// If uniform is a image type, insert it into the mImageBindings array.
for (unsigned int imageIndex : mPod.imageUniformRange)
{
// 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.getType());
const GLuint arraySize = imageUniform.getBasicTypeElementCount();
if (imageUniform.getBinding() == -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.getBinding() + imageUniform.pod.parentArrayIndex * arraySize,
imageUniform.getBasicTypeElementCount(), textureType));
}
*combinedImageUniforms += imageUniform.activeShaderCount() * arraySize;
}
highIter = lowIter;
high = low;
for (; lowIter != mUniforms.rend() && lowIter->isSampler(); ++lowIter)
{
--low;
}
mPod.samplerUniformRange = RangeUI(low, high);
// If uniform is a sampler type, insert it into the mSamplerBindings array.
uint16_t totalCount = 0;
for (unsigned int samplerIndex : mPod.samplerUniformRange)
{
const auto &samplerUniform = mUniforms[samplerIndex];
TextureType textureType = SamplerTypeToTextureType(samplerUniform.getType());
GLenum samplerType = samplerUniform.getType();
uint16_t elementCount = samplerUniform.getBasicTypeElementCount();
SamplerFormat format = GetUniformTypeInfo(samplerType).samplerFormat;
mSamplerBindings.emplace_back(textureType, samplerType, format, totalCount, elementCount);
totalCount += elementCount;
}
mSamplerBoundTextureUnits.resize(totalCount, 0);
// Whatever is left constitutes the default uniforms.
mPod.defaultUniformRange = RangeUI(0, low);
}
bool ProgramExecutable::linkAtomicCounterBuffers(const Caps &caps)
{
for (unsigned int index : mPod.atomicCounterUniformRange)
{
auto &uniform = mUniforms[index];
uniform.pod.blockOffset = uniform.getOffset();
uniform.pod.blockArrayStride = uniform.isArray() ? 4 : 0;
uniform.pod.blockMatrixStride = 0;
uniform.pod.flagBits.blockIsRowMajorMatrix = false;
uniform.pod.flagBits.isBlock = true;
bool found = false;
for (size_t bufferIndex = 0; bufferIndex < mAtomicCounterBuffers.size(); ++bufferIndex)
{
AtomicCounterBuffer &buffer = mAtomicCounterBuffers[bufferIndex];
if (buffer.pod.inShaderBinding == uniform.getBinding())
{
buffer.memberIndexes.push_back(index);
SetBitField(uniform.pod.bufferIndex, bufferIndex);
found = true;
buffer.unionReferencesWith(uniform);
break;
}
}
if (!found)
{
AtomicCounterBuffer atomicCounterBuffer;
atomicCounterBuffer.pod.inShaderBinding = uniform.getBinding();
atomicCounterBuffer.memberIndexes.push_back(index);
atomicCounterBuffer.unionReferencesWith(uniform);
mAtomicCounterBuffers.push_back(atomicCounterBuffer);
SetBitField(uniform.pod.bufferIndex, mAtomicCounterBuffers.size() - 1);
}
}
// Count each atomic counter buffer to validate against
// per-stage and combined gl_Max*AtomicCounterBuffers.
GLint combinedShaderACBCount = 0;
gl::ShaderMap<GLint> perShaderACBCount = {};
for (size_t bufferIndex = 0; bufferIndex < mAtomicCounterBuffers.size(); ++bufferIndex)
{
AtomicCounterBuffer &acb = mAtomicCounterBuffers[bufferIndex];
const ShaderBitSet shaderStages = acb.activeShaders();
for (gl::ShaderType shaderType : shaderStages)
{
++perShaderACBCount[shaderType];
}
++combinedShaderACBCount;
}
if (combinedShaderACBCount > caps.maxCombinedAtomicCounterBuffers)
{
*mInfoLog << " combined AtomicCounterBuffers count exceeds limit";
return false;
}
for (gl::ShaderType stage : gl::AllShaderTypes())
{
if (perShaderACBCount[stage] > caps.maxShaderAtomicCounterBuffers[stage])
{
*mInfoLog << GetShaderTypeString(stage)
<< " shader AtomicCounterBuffers count exceeds limit";
return false;
}
}
return true;
}
void ProgramExecutable::copyInputsFromProgram(const ProgramExecutable &executable)
{
mProgramInputs = executable.getProgramInputs();
}
void ProgramExecutable::copyUniformBuffersFromProgram(
const ProgramExecutable &executable,
ShaderType shaderType,
ProgramUniformBlockArray<GLuint> *ppoUniformBlockMap)
{
AppendActiveBlocks(shaderType, executable.getUniformBlocks(), mUniformBlocks,
ppoUniformBlockMap);
const std::vector<InterfaceBlock> &blocks = executable.getUniformBlocks();
for (size_t blockIndex = 0; blockIndex < blocks.size(); ++blockIndex)
{
if (!blocks[blockIndex].isActive(shaderType))
{
continue;
}
const uint32_t blockIndexInPPO = (*ppoUniformBlockMap)[static_cast<uint32_t>(blockIndex)];
ASSERT(blockIndexInPPO < mUniformBlocks.size());
// Set the block buffer binding in the PPO to the same binding as the program's.
remapUniformBlockBinding({blockIndexInPPO}, executable.getUniformBlockBinding(blockIndex));
}
}
void ProgramExecutable::copyStorageBuffersFromProgram(const ProgramExecutable &executable,
ShaderType shaderType)
{
AppendActiveBlocks(shaderType, executable.getShaderStorageBlocks(), mShaderStorageBlocks,
nullptr);
AppendActiveBlocks(shaderType, executable.getAtomicCounterBuffers(), mAtomicCounterBuffers,
nullptr);
// Buffer variable info is queried through the program, and program pipelines don't access it.
ASSERT(mBufferVariables.empty());
}
void ProgramExecutable::clearSamplerBindings()
{
mSamplerBindings.clear();
mSamplerBoundTextureUnits.clear();
}
void ProgramExecutable::copySamplerBindingsFromProgram(const ProgramExecutable &executable)
{
const std::vector<SamplerBinding> &bindings = executable.getSamplerBindings();
const std::vector<GLuint> &textureUnits = executable.getSamplerBoundTextureUnits();
uint16_t adjustedStartIndex = mSamplerBoundTextureUnits.size();
mSamplerBoundTextureUnits.insert(mSamplerBoundTextureUnits.end(), textureUnits.begin(),
textureUnits.end());
for (const SamplerBinding &binding : bindings)
{
mSamplerBindings.push_back(binding);
mSamplerBindings.back().textureUnitsStartIndex += adjustedStartIndex;
}
}
void ProgramExecutable::copyImageBindingsFromProgram(const ProgramExecutable &executable)
{
const std::vector<ImageBinding> &bindings = executable.getImageBindings();
mImageBindings.insert(mImageBindings.end(), bindings.begin(), bindings.end());
}
void ProgramExecutable::copyOutputsFromProgram(const ProgramExecutable &executable)
{
mOutputVariables = executable.getOutputVariables();
mOutputLocations = executable.getOutputLocations();
mSecondaryOutputLocations = executable.getSecondaryOutputLocations();
}
void ProgramExecutable::copyUniformsFromProgramMap(
const ShaderMap<SharedProgramExecutable> &executables)
{
// Merge default uniforms.
auto getDefaultRange = [](const ProgramExecutable &state) {
return state.getDefaultUniformRange();
};
mPod.defaultUniformRange = AddUniforms(executables, mPod.linkedShaderStages, &mUniforms,
&mUniformNames, &mUniformMappedNames, getDefaultRange);
// Merge sampler uniforms.
auto getSamplerRange = [](const ProgramExecutable &state) {
return state.getSamplerUniformRange();
};
mPod.samplerUniformRange = AddUniforms(executables, mPod.linkedShaderStages, &mUniforms,
&mUniformNames, &mUniformMappedNames, getSamplerRange);
// Merge image uniforms.
auto getImageRange = [](const ProgramExecutable &state) {
return state.getImageUniformRange();
};
mPod.imageUniformRange = AddUniforms(executables, mPod.linkedShaderStages, &mUniforms,
&mUniformNames, &mUniformMappedNames, getImageRange);
// Merge atomic counter uniforms.
auto getAtomicRange = [](const ProgramExecutable &state) {
return state.getAtomicCounterUniformRange();
};
mPod.atomicCounterUniformRange =
AddUniforms(executables, mPod.linkedShaderStages, &mUniforms, &mUniformNames,
&mUniformMappedNames, getAtomicRange);
// Note: uniforms are set through the program, and the program pipeline never needs it.
ASSERT(mUniformLocations.empty());
}
void ProgramExecutable::getResourceName(const std::string name,
GLsizei bufSize,
GLsizei *length,
GLchar *dest) const
{
if (length)
{
*length = 0;
}
if (bufSize > 0)
{
CopyStringToBuffer(dest, name, bufSize, length);
}
}
GLuint ProgramExecutable::getInputResourceIndex(const GLchar *name) const
{
const std::string nameString = StripLastArrayIndex(name);
for (size_t index = 0; index < mProgramInputs.size(); index++)
{
if (mProgramInputs[index].name == nameString)
{
return static_cast<GLuint>(index);
}
}
return GL_INVALID_INDEX;
}
GLuint ProgramExecutable::getInputResourceMaxNameSize() const
{
GLint max = 0;
for (const ProgramInput &resource : mProgramInputs)
{
max = GetResourceMaxNameSize(resource, max);
}
return max;
}
GLuint ProgramExecutable::getOutputResourceMaxNameSize() const
{
GLint max = 0;
for (const gl::ProgramOutput &resource : mOutputVariables)
{
max = GetResourceMaxNameSize(resource, max);
}
return max;
}
GLuint ProgramExecutable::getInputResourceLocation(const GLchar *name) const
{
const GLuint index = getInputResourceIndex(name);
if (index == GL_INVALID_INDEX)
{
return index;
}
const ProgramInput &variable = getInputResource(index);
return GetResourceLocation(name, variable, variable.getLocation());
}
GLuint ProgramExecutable::getOutputResourceLocation(const GLchar *name) const
{
const GLuint index = getOutputResourceIndex(name);
if (index == GL_INVALID_INDEX)
{
return index;
}
const gl::ProgramOutput &variable = getOutputResource(index);
return GetResourceLocation(name, variable, variable.pod.location);
}
GLuint ProgramExecutable::getOutputResourceIndex(const GLchar *name) const
{
const std::string nameString = StripLastArrayIndex(name);
for (size_t index = 0; index < mOutputVariables.size(); index++)
{
if (mOutputVariables[index].name == nameString)
{
return static_cast<GLuint>(index);
}
}
return GL_INVALID_INDEX;
}
void ProgramExecutable::getInputResourceName(GLuint index,
GLsizei bufSize,
GLsizei *length,
GLchar *name) const
{
getResourceName(getInputResourceName(index), bufSize, length, name);
}
void ProgramExecutable::getOutputResourceName(GLuint index,
GLsizei bufSize,
GLsizei *length,
GLchar *name) const
{
getResourceName(getOutputResourceName(index), bufSize, length, name);
}
void ProgramExecutable::getUniformResourceName(GLuint index,
GLsizei bufSize,
GLsizei *length,
GLchar *name) const
{
getResourceName(getUniformNameByIndex(index), bufSize, length, name);
}
void ProgramExecutable::getBufferVariableResourceName(GLuint index,
GLsizei bufSize,
GLsizei *length,
GLchar *name) const
{
ASSERT(index < mBufferVariables.size());
getResourceName(mBufferVariables[index].name, bufSize, length, name);
}
const std::string ProgramExecutable::getInputResourceName(GLuint index) const
{
return GetResourceName(getInputResource(index));
}
const std::string ProgramExecutable::getOutputResourceName(GLuint index) const
{
return GetResourceName(getOutputResource(index));
}
GLint ProgramExecutable::getFragDataLocation(const std::string &name) const
{
const GLint primaryLocation = GetVariableLocation(mOutputVariables, mOutputLocations, name);
if (primaryLocation != -1)
{
return primaryLocation;
}
return GetVariableLocation(mOutputVariables, mSecondaryOutputLocations, name);
}
GLint ProgramExecutable::getFragDataIndex(const std::string &name) const
{
if (GetVariableLocation(mOutputVariables, mOutputLocations, name) != -1)
{
return 0;
}
if (GetVariableLocation(mOutputVariables, mSecondaryOutputLocations, name) != -1)
{
return 1;
}
return -1;
}
GLsizei ProgramExecutable::getTransformFeedbackVaryingMaxLength() const
{
GLsizei maxSize = 0;
for (const TransformFeedbackVarying &var : mLinkedTransformFeedbackVaryings)
{
maxSize = std::max(maxSize, static_cast<GLsizei>(var.nameWithArrayIndex().length() + 1));
}
return maxSize;
}
GLuint ProgramExecutable::getTransformFeedbackVaryingResourceIndex(const GLchar *name) const
{
for (GLuint tfIndex = 0; tfIndex < mLinkedTransformFeedbackVaryings.size(); ++tfIndex)
{
if (mLinkedTransformFeedbackVaryings[tfIndex].nameWithArrayIndex() == name)
{
return tfIndex;
}
}
return GL_INVALID_INDEX;
}
const TransformFeedbackVarying &ProgramExecutable::getTransformFeedbackVaryingResource(
GLuint index) const
{
ASSERT(index < mLinkedTransformFeedbackVaryings.size());
return mLinkedTransformFeedbackVaryings[index];
}
void ProgramExecutable::getTransformFeedbackVarying(GLuint index,
GLsizei bufSize,
GLsizei *length,
GLsizei *size,
GLenum *type,
GLchar *name) const
{
if (mLinkedTransformFeedbackVaryings.empty())
{
// Program is not successfully linked
return;
}
ASSERT(index < mLinkedTransformFeedbackVaryings.size());
const auto &var = mLinkedTransformFeedbackVaryings[index];
std::string varName = var.nameWithArrayIndex();
GLsizei lastNameIdx = std::min(bufSize - 1, static_cast<GLsizei>(varName.length()));
if (length)
{
*length = lastNameIdx;
}
if (size)
{
*size = var.size();
}
if (type)
{
*type = var.type;
}
if (name)
{
memcpy(name, varName.c_str(), lastNameIdx);
name[lastNameIdx] = '\0';
}
}
void ProgramExecutable::getActiveAttribute(GLuint index,
GLsizei bufsize,
GLsizei *length,
GLint *size,
GLenum *type,
GLchar *name) const
{
if (mProgramInputs.empty())
{
// Program is not successfully linked
if (bufsize > 0)
{
name[0] = '\0';
}
if (length)
{
*length = 0;
}
*type = GL_NONE;
*size = 1;
return;
}
ASSERT(index < mProgramInputs.size());
const ProgramInput &attrib = mProgramInputs[index];
if (bufsize > 0)
{
CopyStringToBuffer(name, attrib.name, bufsize, length);
}
// Always a single 'type' instance
*size = 1;
*type = attrib.getType();
}
GLint ProgramExecutable::getActiveAttributeMaxLength() const
{
size_t maxLength = 0;
for (const ProgramInput &attrib : mProgramInputs)
{
maxLength = std::max(attrib.name.length() + 1, maxLength);
}
return static_cast<GLint>(maxLength);
}
GLuint ProgramExecutable::getAttributeLocation(const std::string &name) const
{
for (const ProgramInput &attribute : mProgramInputs)
{
if (attribute.name == name)
{
return attribute.getLocation();
}
}
return static_cast<GLuint>(-1);
}
void ProgramExecutable::getActiveUniform(GLuint index,
GLsizei bufsize,
GLsizei *length,
GLint *size,
GLenum *type,
GLchar *name) const
{
if (mUniforms.empty())
{
// Program is not successfully linked
if (bufsize > 0)
{
name[0] = '\0';
}
if (length)
{
*length = 0;
}
*size = 0;
*type = GL_NONE;
}
ASSERT(index < mUniforms.size());
const LinkedUniform &uniform = mUniforms[index];
if (bufsize > 0)
{
const std::string &string = getUniformNameByIndex(index);
CopyStringToBuffer(name, string, bufsize, length);
}
*size = clampCast<GLint>(uniform.getBasicTypeElementCount());
*type = uniform.getType();
}
GLint ProgramExecutable::getActiveUniformMaxLength() const
{
size_t maxLength = 0;
for (GLuint index = 0; index < static_cast<size_t>(mUniformNames.size()); index++)
{
const std::string &uniformName = getUniformNameByIndex(index);
if (!uniformName.empty())
{
size_t length = uniformName.length() + 1u;
if (getUniformByIndex(index).isArray())
{
length += 3; // Counting in "[0]".
}
maxLength = std::max(length, maxLength);
}
}
return static_cast<GLint>(maxLength);
}
bool ProgramExecutable::isValidUniformLocation(UniformLocation location) const
{
ASSERT(angle::IsValueInRangeForNumericType<GLint>(mUniformLocations.size()));
return location.value >= 0 && static_cast<size_t>(location.value) < mUniformLocations.size() &&
mUniformLocations[location.value].used();
}
const LinkedUniform &ProgramExecutable::getUniformByLocation(UniformLocation location) const
{
ASSERT(location.value >= 0 && static_cast<size_t>(location.value) < mUniformLocations.size());
return mUniforms[getUniformIndexFromLocation(location)];
}
const VariableLocation &ProgramExecutable::getUniformLocation(UniformLocation location) const
{
ASSERT(location.value >= 0 && static_cast<size_t>(location.value) < mUniformLocations.size());
return mUniformLocations[location.value];
}
UniformLocation ProgramExecutable::getUniformLocation(const std::string &name) const
{
return {GetUniformLocation(mUniforms, mUniformNames, mUniformLocations, name)};
}
GLuint ProgramExecutable::getUniformIndex(const std::string &name) const
{
return getUniformIndexFromName(name);
}
bool ProgramExecutable::shouldIgnoreUniform(UniformLocation location) const
{
if (location.value < 0)
{
return true;
}
if (static_cast<size_t>(location.value) >= mUniformLocations.size())
{
ERR() << "Invalid uniform location " << location.value << ", expected [0, "
<< mUniformLocations.size() << ")";
return true;
}
return mUniformLocations[location.value].ignored;
}
GLuint ProgramExecutable::getUniformIndexFromName(const std::string &name) const
{
return GetUniformIndexFromName(mUniforms, mUniformNames, name);
}
GLuint ProgramExecutable::getBufferVariableIndexFromName(const std::string &name) const
{
return GetResourceIndexFromName(mBufferVariables, name);
}
GLuint ProgramExecutable::getUniformIndexFromLocation(UniformLocation location) const
{
ASSERT(location.value >= 0 && static_cast<size_t>(location.value) < mUniformLocations.size());
return mUniformLocations[location.value].index;
}
Optional<GLuint> ProgramExecutable::getSamplerIndex(UniformLocation location) const
{
GLuint index = getUniformIndexFromLocation(location);
if (!isSamplerUniformIndex(index))
{
return Optional<GLuint>::Invalid();
}
return getSamplerIndexFromUniformIndex(index);
}
bool ProgramExecutable::isSamplerUniformIndex(GLuint index) const
{
return mPod.samplerUniformRange.contains(index);
}
GLuint ProgramExecutable::getSamplerIndexFromUniformIndex(GLuint uniformIndex) const
{
ASSERT(isSamplerUniformIndex(uniformIndex));
return uniformIndex - mPod.samplerUniformRange.low();
}
bool ProgramExecutable::isImageUniformIndex(GLuint index) const
{
return mPod.imageUniformRange.contains(index);
}
GLuint ProgramExecutable::getImageIndexFromUniformIndex(GLuint uniformIndex) const
{
ASSERT(isImageUniformIndex(uniformIndex));
return uniformIndex - mPod.imageUniformRange.low();
}
void ProgramExecutable::getActiveUniformBlockName(const Context *context,
const UniformBlockIndex blockIndex,
GLsizei bufSize,
GLsizei *length,
GLchar *blockName) const
{
GetInterfaceBlockName(blockIndex, mUniformBlocks, bufSize, length, blockName);
}
void ProgramExecutable::getActiveShaderStorageBlockName(const GLuint blockIndex,
GLsizei bufSize,
GLsizei *length,
GLchar *blockName) const
{
GetInterfaceBlockName({blockIndex}, mShaderStorageBlocks, bufSize, length, blockName);
}
GLint ProgramExecutable::getActiveUniformBlockMaxNameLength() const
{
return GetActiveInterfaceBlockMaxNameLength(mUniformBlocks);
}
GLint ProgramExecutable::getActiveShaderStorageBlockMaxNameLength() const
{
return GetActiveInterfaceBlockMaxNameLength(mShaderStorageBlocks);
}
GLuint ProgramExecutable::getUniformBlockIndex(const std::string &name) const
{
return GetInterfaceBlockIndex(mUniformBlocks, name);
}
GLuint ProgramExecutable::getShaderStorageBlockIndex(const std::string &name) const
{
return GetInterfaceBlockIndex(mShaderStorageBlocks, name);
}
GLuint ProgramExecutable::getSamplerUniformBinding(const VariableLocation &uniformLocation) const
{
GLuint samplerIndex = getSamplerIndexFromUniformIndex(uniformLocation.index);
const SamplerBinding &samplerBinding = mSamplerBindings[samplerIndex];
if (uniformLocation.arrayIndex >= samplerBinding.textureUnitsCount)
{
return 0;
}
const std::vector<GLuint> &boundTextureUnits = mSamplerBoundTextureUnits;
return samplerBinding.getTextureUnit(boundTextureUnits, uniformLocation.arrayIndex);
}
GLuint ProgramExecutable::getImageUniformBinding(const VariableLocation &uniformLocation) const
{
GLuint imageIndex = getImageIndexFromUniformIndex(uniformLocation.index);
const std::vector<GLuint> &boundImageUnits = mImageBindings[imageIndex].boundImageUnits;
return boundImageUnits[uniformLocation.arrayIndex];
}
template <typename UniformT,
GLint UniformSize,
void (rx::ProgramExecutableImpl::*SetUniformFunc)(GLint, GLsizei, const UniformT *)>
void ProgramExecutable::setUniformGeneric(UniformLocation location,
GLsizei count,
const UniformT *v)
{
if (shouldIgnoreUniform(location))
{
return;
}
const VariableLocation &locationInfo = mUniformLocations[location.value];
GLsizei clampedCount = clampUniformCount(locationInfo, count, UniformSize, v);
(mImplementation->*SetUniformFunc)(location.value, clampedCount, v);
onStateChange(angle::SubjectMessage::ProgramUniformUpdated);
}
void ProgramExecutable::setUniform1fv(UniformLocation location, GLsizei count, const GLfloat *v)
{
setUniformGeneric<GLfloat, 1, &rx::ProgramExecutableImpl::setUniform1fv>(location, count, v);
}
void ProgramExecutable::setUniform2fv(UniformLocation location, GLsizei count, const GLfloat *v)
{
setUniformGeneric<GLfloat, 2, &rx::ProgramExecutableImpl::setUniform2fv>(location, count, v);
}
void ProgramExecutable::setUniform3fv(UniformLocation location, GLsizei count, const GLfloat *v)
{
setUniformGeneric<GLfloat, 3, &rx::ProgramExecutableImpl::setUniform3fv>(location, count, v);
}
void ProgramExecutable::setUniform4fv(UniformLocation location, GLsizei count, const GLfloat *v)
{
setUniformGeneric<GLfloat, 4, &rx::ProgramExecutableImpl::setUniform4fv>(location, count, v);
}
void ProgramExecutable::setUniform1iv(Context *context,
UniformLocation location,
GLsizei count,
const GLint *v)
{
if (shouldIgnoreUniform(location))
{
return;
}
const VariableLocation &locationInfo = mUniformLocations[location.value];
GLsizei clampedCount = clampUniformCount(locationInfo, count, 1, v);
mImplementation->setUniform1iv(location.value, clampedCount, v);
if (isSamplerUniformIndex(locationInfo.index))
{
updateSamplerUniform(context, locationInfo, clampedCount, v);
}
else
{
onStateChange(angle::SubjectMessage::ProgramUniformUpdated);
}
}
void ProgramExecutable::setUniform2iv(UniformLocation location, GLsizei count, const GLint *v)
{
setUniformGeneric<GLint, 2, &rx::ProgramExecutableImpl::setUniform2iv>(location, count, v);
}
void ProgramExecutable::setUniform3iv(UniformLocation location, GLsizei count, const GLint *v)
{
setUniformGeneric<GLint, 3, &rx::ProgramExecutableImpl::setUniform3iv>(location, count, v);
}
void ProgramExecutable::setUniform4iv(UniformLocation location, GLsizei count, const GLint *v)
{
setUniformGeneric<GLint, 4, &rx::ProgramExecutableImpl::setUniform4iv>(location, count, v);
}
void ProgramExecutable::setUniform1uiv(UniformLocation location, GLsizei count, const GLuint *v)
{
setUniformGeneric<GLuint, 1, &rx::ProgramExecutableImpl::setUniform1uiv>(location, count, v);
}
void ProgramExecutable::setUniform2uiv(UniformLocation location, GLsizei count, const GLuint *v)
{
setUniformGeneric<GLuint, 2, &rx::ProgramExecutableImpl::setUniform2uiv>(location, count, v);
}
void ProgramExecutable::setUniform3uiv(UniformLocation location, GLsizei count, const GLuint *v)
{
setUniformGeneric<GLuint, 3, &rx::ProgramExecutableImpl::setUniform3uiv>(location, count, v);
}
void ProgramExecutable::setUniform4uiv(UniformLocation location, GLsizei count, const GLuint *v)
{
setUniformGeneric<GLuint, 4, &rx::ProgramExecutableImpl::setUniform4uiv>(location, count, v);
}
template <typename UniformT,
GLint MatrixC,
GLint MatrixR,
void (rx::ProgramExecutableImpl::*
SetUniformMatrixFunc)(GLint, GLsizei, GLboolean, const UniformT *)>
void ProgramExecutable::setUniformMatrixGeneric(UniformLocation location,
GLsizei count,
GLboolean transpose,
const UniformT *v)
{
if (shouldIgnoreUniform(location))
{
return;
}
GLsizei clampedCount = clampMatrixUniformCount<MatrixC, MatrixR>(location, count, transpose, v);
(mImplementation->*SetUniformMatrixFunc)(location.value, clampedCount, transpose, v);
onStateChange(angle::SubjectMessage::ProgramUniformUpdated);
}
void ProgramExecutable::setUniformMatrix2fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 2, 2, &rx::ProgramExecutableImpl::setUniformMatrix2fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix3fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 3, 3, &rx::ProgramExecutableImpl::setUniformMatrix3fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix4fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 4, 4, &rx::ProgramExecutableImpl::setUniformMatrix4fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix2x3fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 2, 3, &rx::ProgramExecutableImpl::setUniformMatrix2x3fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix2x4fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 2, 4, &rx::ProgramExecutableImpl::setUniformMatrix2x4fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix3x2fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 3, 2, &rx::ProgramExecutableImpl::setUniformMatrix3x2fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix3x4fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 3, 4, &rx::ProgramExecutableImpl::setUniformMatrix3x4fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix4x2fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 4, 2, &rx::ProgramExecutableImpl::setUniformMatrix4x2fv>(
location, count, transpose, v);
}
void ProgramExecutable::setUniformMatrix4x3fv(UniformLocation location,
GLsizei count,
GLboolean transpose,
const GLfloat *v)
{
setUniformMatrixGeneric<GLfloat, 4, 3, &rx::ProgramExecutableImpl::setUniformMatrix4x3fv>(
location, count, transpose, v);
}
void ProgramExecutable::getUniformfv(const Context *context,
UniformLocation location,
GLfloat *v) const
{
const VariableLocation &uniformLocation = mUniformLocations[location.value];
const LinkedUniform &uniform = mUniforms[uniformLocation.index];
if (uniform.isSampler())
{
*v = static_cast<GLfloat>(getSamplerUniformBinding(uniformLocation));
return;
}
else if (uniform.isImage())
{
*v = static_cast<GLfloat>(getImageUniformBinding(uniformLocation));
return;
}
const GLenum nativeType = VariableComponentType(uniform.getType());
if (nativeType == GL_FLOAT)
{
mImplementation->getUniformfv(context, location.value, v);
}
else
{
getUniformInternal(context, v, location, nativeType,
VariableComponentCount(uniform.getType()));
}
}
void ProgramExecutable::getUniformiv(const Context *context,
UniformLocation location,
GLint *v) const
{
const VariableLocation &uniformLocation = mUniformLocations[location.value];
const LinkedUniform &uniform = mUniforms[uniformLocation.index];
if (uniform.isSampler())
{
*v = static_cast<GLint>(getSamplerUniformBinding(uniformLocation));
return;
}
else if (uniform.isImage())
{
*v = static_cast<GLint>(getImageUniformBinding(uniformLocation));
return;
}
const GLenum nativeType = VariableComponentType(uniform.getType());
if (nativeType == GL_INT || nativeType == GL_BOOL)
{
mImplementation->getUniformiv(context, location.value, v);
}
else
{
getUniformInternal(context, v, location, nativeType,
VariableComponentCount(uniform.getType()));
}
}
void ProgramExecutable::getUniformuiv(const Context *context,
UniformLocation location,
GLuint *v) const
{
const VariableLocation &uniformLocation = mUniformLocations[location.value];
const LinkedUniform &uniform = mUniforms[uniformLocation.index];
if (uniform.isSampler())
{
*v = getSamplerUniformBinding(uniformLocation);
return;
}
else if (uniform.isImage())
{
*v = getImageUniformBinding(uniformLocation);
return;
}
const GLenum nativeType = VariableComponentType(uniform.getType());
if (nativeType == GL_UNSIGNED_INT)
{
mImplementation->getUniformuiv(context, location.value, v);
}
else
{
getUniformInternal(context, v, location, nativeType,
VariableComponentCount(uniform.getType()));
}
}
void ProgramExecutable::initInterfaceBlockBindings()
{
// Set initial bindings from shader.
for (size_t blockIndex = 0; blockIndex < mUniformBlocks.size(); blockIndex++)
{
InterfaceBlock &uniformBlock = mUniformBlocks[blockIndex];
// All interface blocks either have |binding| defined, or default to binding 0.
ASSERT(uniformBlock.pod.inShaderBinding >= 0);
remapUniformBlockBinding({static_cast<uint32_t>(blockIndex)},
uniformBlock.pod.inShaderBinding);
// This is called on program link/binary, which means the executable has changed. There is
// no need to send any additional notifications to the contexts (where the program may be
// current) or program pipeline objects (that have this program attached), because they
// already assume all blocks are dirty.
}
}
void ProgramExecutable::remapUniformBlockBinding(UniformBlockIndex uniformBlockIndex,
GLuint uniformBlockBinding)
{
// Remove previous binding
const GLuint previousBinding = mUniformBlockIndexToBufferBinding[uniformBlockIndex.value];
mUniformBufferBindingToUniformBlocks[previousBinding].reset(uniformBlockIndex.value);
// Set new binding
mUniformBlockIndexToBufferBinding[uniformBlockIndex.value] = uniformBlockBinding;
mUniformBufferBindingToUniformBlocks[uniformBlockBinding].set(uniformBlockIndex.value);
}
void ProgramExecutable::setUniformValuesFromBindingQualifiers()
{
for (unsigned int samplerIndex : mPod.samplerUniformRange)
{
const auto &samplerUniform = mUniforms[samplerIndex];
if (samplerUniform.getBinding() != -1)
{
const std::string &uniformName = getUniformNameByIndex(samplerIndex);
UniformLocation location = getUniformLocation(uniformName);
ASSERT(location.value != -1);
std::vector<GLint> boundTextureUnits;
for (unsigned int elementIndex = 0;
elementIndex < samplerUniform.getBasicTypeElementCount(); ++elementIndex)
{
boundTextureUnits.push_back(samplerUniform.getBinding() + elementIndex);
}
// 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());
}
}
}
template <typename T>
GLsizei ProgramExecutable::clampUniformCount(const VariableLocation &locationInfo,
GLsizei count,
int vectorSize,
const T *v)
{
if (count == 1)
return 1;
const LinkedUniform &linkedUniform = mUniforms[locationInfo.index];
// 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."
unsigned int remainingElements =
linkedUniform.getBasicTypeElementCount() - locationInfo.arrayIndex;
GLsizei maxElementCount =
static_cast<GLsizei>(remainingElements * linkedUniform.getElementComponents());
if (count * vectorSize > maxElementCount)
{
return maxElementCount / vectorSize;
}
return count;
}
template <size_t cols, size_t rows, typename T>
GLsizei ProgramExecutable::clampMatrixUniformCount(UniformLocation location,
GLsizei count,
GLboolean transpose,
const T *v)
{
const VariableLocation &locationInfo = mUniformLocations[location.value];
if (!transpose)
{
return clampUniformCount(locationInfo, count, cols * rows, v);
}
const LinkedUniform &linkedUniform = mUniforms[locationInfo.index];
// 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."
unsigned int remainingElements =
linkedUniform.getBasicTypeElementCount() - locationInfo.arrayIndex;
return std::min(count, static_cast<GLsizei>(remainingElements));
}
void ProgramExecutable::updateSamplerUniform(Context *context,
const VariableLocation &locationInfo,
GLsizei clampedCount,
const GLint *v)
{
ASSERT(isSamplerUniformIndex(locationInfo.index));
GLuint samplerIndex = getSamplerIndexFromUniformIndex(locationInfo.index);
SamplerBinding &samplerBinding = mSamplerBindings[samplerIndex];
std::vector<GLuint> &boundTextureUnits = mSamplerBoundTextureUnits;
if (locationInfo.arrayIndex >= samplerBinding.textureUnitsCount)
{
return;
}
GLsizei safeUniformCount =
std::min(clampedCount,
static_cast<GLsizei>(samplerBinding.textureUnitsCount - locationInfo.arrayIndex));
// Update the sampler uniforms.
for (uint16_t arrayIndex = 0; arrayIndex < safeUniformCount; ++arrayIndex)
{
GLint oldTextureUnit =
samplerBinding.getTextureUnit(boundTextureUnits, arrayIndex + locationInfo.arrayIndex);
GLint newTextureUnit = v[arrayIndex];
if (oldTextureUnit == newTextureUnit)
{
continue;
}
// Update sampler's bound textureUnit
boundTextureUnits[samplerBinding.textureUnitsStartIndex + arrayIndex +
locationInfo.arrayIndex] = newTextureUnit;
// Update the reference counts.
uint32_t &oldRefCount = mActiveSamplerRefCounts[oldTextureUnit];
uint32_t &newRefCount = mActiveSamplerRefCounts[newTextureUnit];
ASSERT(oldRefCount > 0);
ASSERT(newRefCount < std::numeric_limits<uint32_t>::max());
oldRefCount--;
newRefCount++;
// Check for binding type change.
TextureType newSamplerType = mActiveSamplerTypes[newTextureUnit];
TextureType oldSamplerType = mActiveSamplerTypes[oldTextureUnit];
SamplerFormat newSamplerFormat = mActiveSamplerFormats[newTextureUnit];
SamplerFormat oldSamplerFormat = mActiveSamplerFormats[oldTextureUnit];
bool newSamplerYUV = mActiveSamplerYUV.test(newTextureUnit);
if (newRefCount == 1)
{
setActive(newTextureUnit, samplerBinding, mUniforms[locationInfo.index]);
}
else
{
if (newSamplerType != samplerBinding.textureType ||
newSamplerYUV != IsSamplerYUVType(samplerBinding.samplerType))
{
hasSamplerTypeConflict(newTextureUnit);
}
if (newSamplerFormat != samplerBinding.format)
{
hasSamplerFormatConflict(newTextureUnit);
}
}
// Unset previously active sampler.
if (oldRefCount == 0)
{
setInactive(oldTextureUnit);
}
else
{
if (oldSamplerType == TextureType::InvalidEnum ||
oldSamplerFormat == SamplerFormat::InvalidEnum)
{
// Previous conflict. Check if this new change fixed the conflict.
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 (mPod.isSeparable)
{
onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged);
}
// Notify context.
if (context)
{
context->onSamplerUniformChange(newTextureUnit);
context->onSamplerUniformChange(oldTextureUnit);
}
}
// Invalidate the validation cache.
resetCachedValidateSamplersResult();
// Inform any PPOs this Program may be bound to.
onStateChange(angle::SubjectMessage::SamplerUniformsUpdated);
}
// 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 ProgramExecutable::getUniformInternal(const Context *context,
DestT *dataOut,
UniformLocation location,
GLenum nativeType,
int components) const
{
switch (nativeType)
{
case GL_BOOL:
{
GLint tempValue[16] = {0};
mImplementation->getUniformiv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLboolean>(
dataOut, reinterpret_cast<const uint8_t *>(tempValue), components);
break;
}
case GL_INT:
{
GLint tempValue[16] = {0};
mImplementation->getUniformiv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue),
components);
break;
}
case GL_UNSIGNED_INT:
{
GLuint tempValue[16] = {0};
mImplementation->getUniformuiv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLuint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue),
components);
break;
}
case GL_FLOAT:
{
GLfloat tempValue[16] = {0};
mImplementation->getUniformfv(context, location.value, tempValue);
UniformStateQueryCastLoop<GLfloat>(
dataOut, reinterpret_cast<const uint8_t *>(tempValue), components);
break;
}
default:
UNREACHABLE();
break;
}
}
void ProgramExecutable::setDrawIDUniform(GLint drawid)
{
ASSERT(hasDrawIDUniform());
mImplementation->setUniform1iv(mPod.drawIDLocation, 1, &drawid);
}
void ProgramExecutable::setBaseVertexUniform(GLint baseVertex)
{
ASSERT(hasBaseVertexUniform());
if (baseVertex == mCachedBaseVertex)
{
return;
}
mCachedBaseVertex = baseVertex;
mImplementation->setUniform1iv(mPod.baseVertexLocation, 1, &baseVertex);
}
void ProgramExecutable::setBaseInstanceUniform(GLuint baseInstance)
{
ASSERT(hasBaseInstanceUniform());
if (baseInstance == mCachedBaseInstance)
{
return;
}
mCachedBaseInstance = baseInstance;
GLint baseInstanceInt = baseInstance;
mImplementation->setUniform1iv(mPod.baseInstanceLocation, 1, &baseInstanceInt);
}
void ProgramExecutable::waitForPostLinkTasks(const Context *context)
{
if (mPostLinkSubTasks.empty())
{
return;
}
mImplementation->waitForPostLinkTasks(context);
}
void InstallExecutable(const Context *context,
const SharedProgramExecutable &toInstall,
SharedProgramExecutable *executable)
{
// There should never be a need to re-install the same executable.
ASSERT(toInstall.get() != executable->get());
// Destroy the old executable before it gets deleted.
UninstallExecutable(context, executable);
// Install the new executable.
*executable = toInstall;
}
void UninstallExecutable(const Context *context, SharedProgramExecutable *executable)
{
if (executable->use_count() == 1)
{
(*executable)->destroy(context);
}
executable->reset();
}
} // namespace gl