diff options
Diffstat (limited to 'src/3rdparty/angle/src/libANGLE/Program.cpp')
-rw-r--r-- | src/3rdparty/angle/src/libANGLE/Program.cpp | 2889 |
1 files changed, 1711 insertions, 1178 deletions
diff --git a/src/3rdparty/angle/src/libANGLE/Program.cpp b/src/3rdparty/angle/src/libANGLE/Program.cpp index 69497c4436..795d326041 100644 --- a/src/3rdparty/angle/src/libANGLE/Program.cpp +++ b/src/3rdparty/angle/src/libANGLE/Program.cpp @@ -11,18 +11,24 @@ #include <algorithm> -#include "common/BitSetIterator.h" +#include "common/bitset_utils.h" #include "common/debug.h" #include "common/platform.h" +#include "common/string_utils.h" #include "common/utilities.h" -#include "common/version.h" #include "compiler/translator/blocklayout.h" -#include "libANGLE/Data.h" +#include "libANGLE/Context.h" +#include "libANGLE/MemoryProgramCache.h" +#include "libANGLE/ProgramLinkedResources.h" #include "libANGLE/ResourceManager.h" +#include "libANGLE/Uniform.h" +#include "libANGLE/VaryingPacking.h" #include "libANGLE/features.h" -#include "libANGLE/renderer/Renderer.h" -#include "libANGLE/renderer/ProgramImpl.h" +#include "libANGLE/histogram_macros.h" #include "libANGLE/queryconversions.h" +#include "libANGLE/renderer/GLImplFactory.h" +#include "libANGLE/renderer/ProgramImpl.h" +#include "platform/Platform.h" namespace gl { @@ -30,29 +36,6 @@ namespace gl namespace { -void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var) -{ - stream->writeInt(var.type); - stream->writeInt(var.precision); - stream->writeString(var.name); - stream->writeString(var.mappedName); - stream->writeInt(var.arraySize); - stream->writeInt(var.staticUse); - stream->writeString(var.structName); - ASSERT(var.fields.empty()); -} - -void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var) -{ - var->type = stream->readInt<GLenum>(); - var->precision = stream->readInt<GLenum>(); - var->name = stream->readString(); - var->mappedName = stream->readString(); - var->arraySize = stream->readInt<unsigned int>(); - var->staticUse = stream->readBool(); - var->structName = stream->readString(); -} - // 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> @@ -88,19 +71,19 @@ GLuint UniformStateQueryCast(GLint value) template <> GLfloat UniformStateQueryCast(GLboolean value) { - return (value == GL_TRUE ? 1.0f : 0.0f); + return (ConvertToBool(value) ? 1.0f : 0.0f); } template <> GLint UniformStateQueryCast(GLboolean value) { - return (value == GL_TRUE ? 1 : 0); + return (ConvertToBool(value) ? 1 : 0); } template <> GLuint UniformStateQueryCast(GLboolean value) { - return (value == GL_TRUE ? 1u : 0u); + return (ConvertToBool(value) ? 1u : 0u); } // Default to static_cast @@ -123,29 +106,224 @@ void UniformStateQueryCastLoop(DestT *dataOut, const uint8_t *srcPointer, int co } } -bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &name) +template <typename VarT> +GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string &name) { - for (const LinkedUniform &uniform : list) + std::string nameAsArrayName = name + "[0]"; + for (size_t index = 0; index < list.size(); index++) { - if (uniform.name == name) - return true; + const VarT &resource = list[index]; + if (resource.name == name || (resource.isArray() && resource.name == nameAsArrayName)) + { + return static_cast<GLuint>(index); + } + } + + return GL_INVALID_INDEX; +} + +template <typename VarT> +GLint GetVariableLocation(const std::vector<VarT> &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 VarT &variable = list[variableLocation.index]; + + if (angle::BeginsWith(variable.name, name)) + { + if (name.length() == variable.name.length()) + { + ASSERT(name == variable.name); + // GLES 3.1 November 2016 page 87. + // The string exactly matches the name of the active variable. + return static_cast<GLint>(location); + } + if (name.length() + 3u == variable.name.length() && variable.isArray()) + { + ASSERT(name + "[0]" == variable.name); + // The string identifies the base name of an active array, where the string would + // exactly match the name of the variable if the suffix "[0]" were appended to the + // string. + return static_cast<GLint>(location); + } + } + if (variable.isArray() && variableLocation.arrayIndex == arrayIndex && + nameLengthWithoutArrayIndex + 3u == variable.name.length() && + angle::BeginsWith(variable.name, name, nameLengthWithoutArrayIndex)) + { + ASSERT(name.substr(0u, nameLengthWithoutArrayIndex) + "[0]" == variable.name); + // The string identifies an active element of the array, where the string ends with the + // concatenation of the "[" character, an integer (with no "+" sign, extra leading + // zeroes, or whitespace) identifying an array element, and the "]" character, the + // integer is less than the number of active elements of the array variable, and where + // the string would exactly match the enumerated name of the array if the decimal + // integer were replaced with zero. + return static_cast<GLint>(location); + } } + return -1; +} + +void CopyStringToBuffer(GLchar *buffer, const std::string &string, GLsizei bufSize, GLsizei *length) +{ + ASSERT(bufSize > 0); + strncpy(buffer, string.c_str(), bufSize); + buffer[bufSize - 1] = '\0'; + + if (length) + { + *length = static_cast<GLsizei>(strlen(buffer)); + } +} + +bool IncludeSameArrayElement(const std::set<std::string> &nameSet, const std::string &name) +{ + std::vector<unsigned int> subscripts; + std::string baseName = ParseResourceName(name, &subscripts); + for (auto 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; } -} // anonymous namespace +bool validateInterfaceBlocksCount(GLuint maxInterfaceBlocks, + const std::vector<sh::InterfaceBlock> &interfaceBlocks, + const std::string &errorMessage, + InfoLog &infoLog) +{ + GLuint blockCount = 0; + for (const sh::InterfaceBlock &block : interfaceBlocks) + { + if (block.staticUse || block.layout != sh::BLOCKLAYOUT_PACKED) + { + blockCount += (block.arraySize ? block.arraySize : 1); + if (blockCount > maxInterfaceBlocks) + { + infoLog << errorMessage << maxInterfaceBlocks << ")"; + return false; + } + } + } + return true; +} -const char *const g_fakepath = "C:\\fakepath"; +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.isArray || block.arrayElement == 0)); + const bool arrayElementMatches = + (subscripts.size() == 1 && subscripts[0] == block.arrayElement); + if (arrayElementMatches || arrayElementZero) + { + return blockIndex; + } + } + } + + return GL_INVALID_INDEX; +} + +void GetInterfaceBlockName(const GLuint index, + const std::vector<InterfaceBlock> &list, + GLsizei bufSize, + GLsizei *length, + GLchar *name) +{ + ASSERT(index < list.size()); + + const auto &block = list[index]; + + if (bufSize > 0) + { + std::string blockName = block.name; + + if (block.isArray) + { + blockName += ArrayString(block.arrayElement); + } + CopyStringToBuffer(name, blockName, bufSize, length); + } +} -AttributeBindings::AttributeBindings() +void InitUniformBlockLinker(const gl::Context *context, + const ProgramState &state, + UniformBlockLinker *blockLinker) { + if (state.getAttachedVertexShader()) + { + blockLinker->addShaderBlocks(GL_VERTEX_SHADER, + &state.getAttachedVertexShader()->getUniformBlocks(context)); + } + + if (state.getAttachedFragmentShader()) + { + blockLinker->addShaderBlocks(GL_FRAGMENT_SHADER, + &state.getAttachedFragmentShader()->getUniformBlocks(context)); + } + + if (state.getAttachedComputeShader()) + { + blockLinker->addShaderBlocks(GL_COMPUTE_SHADER, + &state.getAttachedComputeShader()->getUniformBlocks(context)); + } } -AttributeBindings::~AttributeBindings() +void InitShaderStorageBlockLinker(const gl::Context *context, + const ProgramState &state, + ShaderStorageBlockLinker *blockLinker) { + if (state.getAttachedVertexShader()) + { + blockLinker->addShaderBlocks( + GL_VERTEX_SHADER, &state.getAttachedVertexShader()->getShaderStorageBlocks(context)); + } + + if (state.getAttachedFragmentShader()) + { + blockLinker->addShaderBlocks( + GL_FRAGMENT_SHADER, + &state.getAttachedFragmentShader()->getShaderStorageBlocks(context)); + } + + if (state.getAttachedComputeShader()) + { + blockLinker->addShaderBlocks( + GL_COMPUTE_SHADER, &state.getAttachedComputeShader()->getShaderStorageBlocks(context)); + } } +} // anonymous namespace + +const char *const g_fakepath = "C:\\fakepath"; + InfoLog::InfoLog() { } @@ -156,7 +334,12 @@ InfoLog::~InfoLog() size_t InfoLog::getLength() const { - const std::string &logString = mStream.str(); + if (!mLazyStream) + { + return 0; + } + + const std::string &logString = mLazyStream->str(); return logString.empty() ? 0 : logString.length() + 1; } @@ -166,12 +349,12 @@ void InfoLog::getLog(GLsizei bufSize, GLsizei *length, char *infoLog) const if (bufSize > 0) { - const std::string str(mStream.str()); + const std::string logString(str()); - if (!str.empty()) + if (!logString.empty()) { - index = std::min(static_cast<size_t>(bufSize) - 1, str.length()); - memcpy(infoLog, str.c_str(), index); + index = std::min(static_cast<size_t>(bufSize) - 1, logString.length()); + memcpy(infoLog, logString.c_str(), index); } infoLog[index] = '\0'; @@ -184,10 +367,12 @@ void InfoLog::getLog(GLsizei bufSize, GLsizei *length, char *infoLog) const } // append a santized message to the program info log. -// The D3D compiler includes a fake file path in some of the warning or error +// The D3D compiler includes a fake file path in some of the warning or error // messages, so lets remove all occurrences of this fake file path from the log. void InfoLog::appendSanitized(const char *message) { + ensureInitialized(); + std::string msg(message); size_t found; @@ -201,569 +386,682 @@ void InfoLog::appendSanitized(const char *message) } while (found != std::string::npos); - mStream << message << std::endl; + *mLazyStream << message << std::endl; } void InfoLog::reset() { } -VariableLocation::VariableLocation() - : name(), - element(0), - index(0) +VariableLocation::VariableLocation() : arrayIndex(0), index(kUnused), ignored(false) +{ +} + +VariableLocation::VariableLocation(unsigned int arrayIndex, unsigned int index) + : arrayIndex(arrayIndex), index(index), ignored(false) { + ASSERT(arrayIndex != GL_INVALID_INDEX); } -VariableLocation::VariableLocation(const std::string &name, unsigned int element, unsigned int index) - : name(name), - element(element), - index(index) +SamplerBinding::SamplerBinding(GLenum textureTypeIn, size_t elementCount, bool unreferenced) + : textureType(textureTypeIn), boundTextureUnits(elementCount, 0), unreferenced(unreferenced) { } -Program::Data::Data() +SamplerBinding::SamplerBinding(const SamplerBinding &other) = default; + +SamplerBinding::~SamplerBinding() = default; + +Program::Bindings::Bindings() +{ +} + +Program::Bindings::~Bindings() +{ +} + +void Program::Bindings::bindLocation(GLuint index, const std::string &name) +{ + mBindings[name] = index; +} + +int Program::Bindings::getBinding(const std::string &name) const +{ + auto iter = mBindings.find(name); + return (iter != mBindings.end()) ? iter->second : -1; +} + +Program::Bindings::const_iterator Program::Bindings::begin() const +{ + return mBindings.begin(); +} + +Program::Bindings::const_iterator Program::Bindings::end() const +{ + return mBindings.end(); +} + +ImageBinding::ImageBinding(size_t count) : boundImageUnits(count, 0) +{ +} +ImageBinding::ImageBinding(GLuint imageUnit, size_t count) +{ + for (size_t index = 0; index < count; ++index) + { + boundImageUnits.push_back(imageUnit + static_cast<GLuint>(index)); + } +} + +ImageBinding::ImageBinding(const ImageBinding &other) = default; + +ImageBinding::~ImageBinding() = default; + +ProgramState::ProgramState() : mLabel(), mAttachedFragmentShader(nullptr), mAttachedVertexShader(nullptr), + mAttachedComputeShader(nullptr), + mAttachedGeometryShader(nullptr), mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS), - mBinaryRetrieveableHint(false) + mMaxActiveAttribLocation(0), + mSamplerUniformRange(0, 0), + mImageUniformRange(0, 0), + mAtomicCounterUniformRange(0, 0), + mBinaryRetrieveableHint(false), + mNumViews(-1) { + mComputeShaderLocalSize.fill(1); } -Program::Data::~Data() +ProgramState::~ProgramState() { - if (mAttachedVertexShader != nullptr) - { - mAttachedVertexShader->release(); - } - - if (mAttachedFragmentShader != nullptr) - { - mAttachedFragmentShader->release(); - } + ASSERT(!mAttachedVertexShader && !mAttachedFragmentShader && !mAttachedComputeShader && + !mAttachedGeometryShader); } -const std::string &Program::Data::getLabel() +const std::string &ProgramState::getLabel() { return mLabel; } -const LinkedUniform *Program::Data::getUniformByName(const std::string &name) const +GLuint ProgramState::getUniformIndexFromName(const std::string &name) const { - for (const LinkedUniform &linkedUniform : mUniforms) - { - if (linkedUniform.name == name) - { - return &linkedUniform; - } - } + return GetResourceIndexFromName(mUniforms, name); +} - return nullptr; +GLuint ProgramState::getBufferVariableIndexFromName(const std::string &name) const +{ + return GetResourceIndexFromName(mBufferVariables, name); } -GLint Program::Data::getUniformLocation(const std::string &name) const +GLuint ProgramState::getUniformIndexFromLocation(GLint location) const { - size_t subscript = GL_INVALID_INDEX; - std::string baseName = gl::ParseUniformName(name, &subscript); + ASSERT(location >= 0 && static_cast<size_t>(location) < mUniformLocations.size()); + return mUniformLocations[location].index; +} - for (size_t location = 0; location < mUniformLocations.size(); ++location) +Optional<GLuint> ProgramState::getSamplerIndex(GLint location) const +{ + GLuint index = getUniformIndexFromLocation(location); + if (!isSamplerUniformIndex(index)) { - const VariableLocation &uniformLocation = mUniformLocations[location]; - const LinkedUniform &uniform = mUniforms[uniformLocation.index]; - - if (uniform.name == baseName) - { - if ((uniform.isArray() && uniformLocation.element == subscript) || - (subscript == GL_INVALID_INDEX)) - { - return static_cast<GLint>(location); - } - } + return Optional<GLuint>::Invalid(); } - return -1; + return getSamplerIndexFromUniformIndex(index); } -GLuint Program::Data::getUniformIndex(const std::string &name) const +bool ProgramState::isSamplerUniformIndex(GLuint index) const { - size_t subscript = GL_INVALID_INDEX; - std::string baseName = gl::ParseUniformName(name, &subscript); + return mSamplerUniformRange.contains(index); +} - // The app is not allowed to specify array indices other than 0 for arrays of basic types - if (subscript != 0 && subscript != GL_INVALID_INDEX) - { - return GL_INVALID_INDEX; - } +GLuint ProgramState::getSamplerIndexFromUniformIndex(GLuint uniformIndex) const +{ + ASSERT(isSamplerUniformIndex(uniformIndex)); + return uniformIndex - mSamplerUniformRange.low(); +} - for (size_t index = 0; index < mUniforms.size(); index++) +GLuint ProgramState::getAttributeLocation(const std::string &name) const +{ + for (const sh::Attribute &attribute : mAttributes) { - const LinkedUniform &uniform = mUniforms[index]; - if (uniform.name == baseName) + if (attribute.name == name) { - if (uniform.isArray() || subscript == GL_INVALID_INDEX) - { - return static_cast<GLuint>(index); - } + return attribute.location; } } - return GL_INVALID_INDEX; + return static_cast<GLuint>(-1); } -Program::Program(rx::ImplFactory *factory, ResourceManager *manager, GLuint handle) - : mProgram(factory->createProgram(mData)), +Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, GLuint handle) + : mProgram(factory->createProgram(mState)), mValidated(false), mLinked(false), mDeleteStatus(false), mRefCount(0), mResourceManager(manager), - mHandle(handle), - mSamplerUniformRange(0, 0) + mHandle(handle) { ASSERT(mProgram); - resetUniformBlockBindings(); unlink(); } Program::~Program() { - unlink(true); + ASSERT(!mProgram); +} + +void Program::onDestroy(const Context *context) +{ + if (mState.mAttachedVertexShader != nullptr) + { + mState.mAttachedVertexShader->release(context); + mState.mAttachedVertexShader = nullptr; + } + if (mState.mAttachedFragmentShader != nullptr) + { + mState.mAttachedFragmentShader->release(context); + mState.mAttachedFragmentShader = nullptr; + } + + if (mState.mAttachedComputeShader != nullptr) + { + mState.mAttachedComputeShader->release(context); + mState.mAttachedComputeShader = nullptr; + } + + if (mState.mAttachedGeometryShader != nullptr) + { + mState.mAttachedGeometryShader->release(context); + mState.mAttachedGeometryShader = nullptr; + } + + mProgram->destroy(context); + + ASSERT(!mState.mAttachedVertexShader && !mState.mAttachedFragmentShader && + !mState.mAttachedComputeShader && !mState.mAttachedGeometryShader); SafeDelete(mProgram); + + delete this; } void Program::setLabel(const std::string &label) { - mData.mLabel = label; + mState.mLabel = label; } const std::string &Program::getLabel() const { - return mData.mLabel; + return mState.mLabel; } -bool Program::attachShader(Shader *shader) +void Program::attachShader(Shader *shader) { - if (shader->getType() == GL_VERTEX_SHADER) + switch (shader->getType()) { - if (mData.mAttachedVertexShader) + case GL_VERTEX_SHADER: { - return false; + ASSERT(!mState.mAttachedVertexShader); + mState.mAttachedVertexShader = shader; + mState.mAttachedVertexShader->addRef(); + break; } - - mData.mAttachedVertexShader = shader; - mData.mAttachedVertexShader->addRef(); - } - else if (shader->getType() == GL_FRAGMENT_SHADER) - { - if (mData.mAttachedFragmentShader) + case GL_FRAGMENT_SHADER: { - return false; + ASSERT(!mState.mAttachedFragmentShader); + mState.mAttachedFragmentShader = shader; + mState.mAttachedFragmentShader->addRef(); + break; } - - mData.mAttachedFragmentShader = shader; - mData.mAttachedFragmentShader->addRef(); + case GL_COMPUTE_SHADER: + { + ASSERT(!mState.mAttachedComputeShader); + mState.mAttachedComputeShader = shader; + mState.mAttachedComputeShader->addRef(); + break; + } + case GL_GEOMETRY_SHADER_EXT: + { + ASSERT(!mState.mAttachedGeometryShader); + mState.mAttachedGeometryShader = shader; + mState.mAttachedGeometryShader->addRef(); + break; + } + default: + UNREACHABLE(); } - else UNREACHABLE(); - - return true; } -bool Program::detachShader(Shader *shader) +void Program::detachShader(const Context *context, Shader *shader) { - if (shader->getType() == GL_VERTEX_SHADER) + switch (shader->getType()) { - if (mData.mAttachedVertexShader != shader) + case GL_VERTEX_SHADER: { - return false; + ASSERT(mState.mAttachedVertexShader == shader); + shader->release(context); + mState.mAttachedVertexShader = nullptr; + break; } - - shader->release(); - mData.mAttachedVertexShader = nullptr; - } - else if (shader->getType() == GL_FRAGMENT_SHADER) - { - if (mData.mAttachedFragmentShader != shader) + case GL_FRAGMENT_SHADER: { - return false; + ASSERT(mState.mAttachedFragmentShader == shader); + shader->release(context); + mState.mAttachedFragmentShader = nullptr; + break; } - - shader->release(); - mData.mAttachedFragmentShader = nullptr; + case GL_COMPUTE_SHADER: + { + ASSERT(mState.mAttachedComputeShader == shader); + shader->release(context); + mState.mAttachedComputeShader = nullptr; + break; + } + case GL_GEOMETRY_SHADER_EXT: + { + ASSERT(mState.mAttachedGeometryShader == shader); + shader->release(context); + mState.mAttachedGeometryShader = nullptr; + break; + } + default: + UNREACHABLE(); } - else UNREACHABLE(); - - return true; } int Program::getAttachedShadersCount() const { - return (mData.mAttachedVertexShader ? 1 : 0) + (mData.mAttachedFragmentShader ? 1 : 0); + return (mState.mAttachedVertexShader ? 1 : 0) + (mState.mAttachedFragmentShader ? 1 : 0) + + (mState.mAttachedComputeShader ? 1 : 0) + (mState.mAttachedGeometryShader ? 1 : 0); } -void AttributeBindings::bindAttributeLocation(GLuint index, const char *name) +void Program::bindAttributeLocation(GLuint index, const char *name) { - if (index < MAX_VERTEX_ATTRIBS) - { - for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++) - { - mAttributeBinding[i].erase(name); - } + mAttributeBindings.bindLocation(index, name); +} - mAttributeBinding[index].insert(name); - } +void Program::bindUniformLocation(GLuint index, const char *name) +{ + mUniformLocationBindings.bindLocation(index, name); } -void Program::bindAttributeLocation(GLuint index, const char *name) +void Program::bindFragmentInputLocation(GLint index, const char *name) { - mAttributeBindings.bindAttributeLocation(index, name); + mFragmentInputBindings.bindLocation(index, name); } -// Links the HLSL code of the vertex and pixel shader by matching up their varyings, -// compiling them into binaries, determining the attribute mappings, and collecting -// a list of uniforms -Error Program::link(const gl::Data &data) +BindingInfo Program::getFragmentInputBindingInfo(const Context *context, GLint index) const { - unlink(false); + BindingInfo ret; + ret.type = GL_NONE; + ret.valid = false; - mInfoLog.reset(); - resetUniformBlockBindings(); + Shader *fragmentShader = mState.getAttachedFragmentShader(); + ASSERT(fragmentShader); - if (!mData.mAttachedFragmentShader || !mData.mAttachedFragmentShader->isCompiled()) - { - return Error(GL_NO_ERROR); - } - ASSERT(mData.mAttachedFragmentShader->getType() == GL_FRAGMENT_SHADER); + // Find the actual fragment shader varying we're interested in + const std::vector<sh::Varying> &inputs = fragmentShader->getInputVaryings(context); - if (!mData.mAttachedVertexShader || !mData.mAttachedVertexShader->isCompiled()) + for (const auto &binding : mFragmentInputBindings) { - return Error(GL_NO_ERROR); - } - ASSERT(mData.mAttachedVertexShader->getType() == GL_VERTEX_SHADER); + if (binding.second != static_cast<GLuint>(index)) + continue; - if (!linkAttributes(data, mInfoLog, mAttributeBindings, mData.mAttachedVertexShader)) - { - return Error(GL_NO_ERROR); - } + ret.valid = true; - if (!linkVaryings(mInfoLog, mData.mAttachedVertexShader, mData.mAttachedFragmentShader)) - { - return Error(GL_NO_ERROR); + size_t nameLengthWithoutArrayIndex; + unsigned int arrayIndex = ParseArrayIndex(binding.first, &nameLengthWithoutArrayIndex); + + for (const auto &in : inputs) + { + if (in.name.length() == nameLengthWithoutArrayIndex && + angle::BeginsWith(in.name, binding.first, nameLengthWithoutArrayIndex)) + { + if (in.isArray()) + { + // The client wants to bind either "name" or "name[0]". + // GL ES 3.1 spec refers to active array names with language such as: + // "if 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". + if (arrayIndex == GL_INVALID_INDEX) + arrayIndex = 0; + + ret.name = in.mappedName + "[" + ToString(arrayIndex) + "]"; + } + else + { + ret.name = in.mappedName; + } + ret.type = in.type; + return ret; + } + } } - if (!linkUniforms(mInfoLog, *data.caps)) + return ret; +} + +void Program::pathFragmentInputGen(const Context *context, + GLint index, + GLenum genMode, + GLint components, + const GLfloat *coeffs) +{ + // If the location is -1 then the command is silently ignored + if (index == -1) + return; + + const auto &binding = getFragmentInputBindingInfo(context, index); + + // If the input doesn't exist then then the command is silently ignored + // This could happen through optimization for example, the shader translator + // decides that a variable is not actually being used and optimizes it away. + if (binding.name.empty()) + return; + + mProgram->setPathFragmentInputGen(binding.name, genMode, components, coeffs); +} + +// The attached shaders are checked for linking errors by matching up their variables. +// Uniform, input and output variables get collected. +// The code gets compiled into binaries. +Error Program::link(const gl::Context *context) +{ + const auto &data = context->getContextState(); + + auto *platform = ANGLEPlatformCurrent(); + double startTime = platform->currentTime(platform); + + unlink(); + + ProgramHash programHash; + auto *cache = context->getMemoryProgramCache(); + if (cache) { - return Error(GL_NO_ERROR); + ANGLE_TRY_RESULT(cache->getProgram(context, this, &mState, &programHash), mLinked); + ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", mLinked); } - if (!linkUniformBlocks(mInfoLog, *data.caps)) + if (mLinked) { - return Error(GL_NO_ERROR); + double delta = platform->currentTime(platform) - startTime; + int us = static_cast<int>(delta * 1000000.0); + ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramCacheHitTimeUS", us); + return NoError(); } - const auto &mergedVaryings = getMergedVaryings(); + // Cache load failed, fall through to normal linking. + unlink(); + mInfoLog.reset(); - if (!linkValidateTransformFeedback(mInfoLog, mergedVaryings, *data.caps)) - { - return Error(GL_NO_ERROR); - } + const Caps &caps = data.getCaps(); - linkOutputVariables(); + Shader *vertexShader = mState.mAttachedVertexShader; + Shader *fragmentShader = mState.mAttachedFragmentShader; + Shader *computeShader = mState.mAttachedComputeShader; - rx::LinkResult result = mProgram->link(data, mInfoLog); - if (result.error.isError() || !result.linkSuccess) + bool isComputeShaderAttached = (computeShader != nullptr); + bool nonComputeShadersAttached = (vertexShader != nullptr || fragmentShader != nullptr); + // Check whether we both have a compute and non-compute shaders attached. + // If there are of both types attached, then linking should fail. + // OpenGL ES 3.10, 7.3 Program Objects, under LinkProgram + if (isComputeShaderAttached == true && nonComputeShadersAttached == true) { - return result.error; + mInfoLog << "Both a compute and non-compute shaders are attached to the same program."; + return NoError(); } - gatherTransformFeedbackVaryings(mergedVaryings); - gatherInterfaceBlockInfo(); - - mLinked = true; - return gl::Error(GL_NO_ERROR); -} - -int AttributeBindings::getAttributeBinding(const std::string &name) const -{ - for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++) + if (computeShader) { - if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end()) + if (!computeShader->isCompiled(context)) { - return location; + mInfoLog << "Attached compute shader is not compiled."; + return NoError(); } - } + ASSERT(computeShader->getType() == GL_COMPUTE_SHADER); - return -1; -} + mState.mComputeShaderLocalSize = computeShader->getWorkGroupSize(context); -// Returns the program object to an unlinked state, before re-linking, or at destruction -void Program::unlink(bool destroy) -{ - if (destroy) // Object being destructed - { - if (mData.mAttachedFragmentShader) + // GLSL ES 3.10, 4.4.1.1 Compute Shader Inputs + // If the work group size is not specified, a link time error should occur. + if (!mState.mComputeShaderLocalSize.isDeclared()) { - mData.mAttachedFragmentShader->release(); - mData.mAttachedFragmentShader = nullptr; + mInfoLog << "Work group size is not specified."; + return NoError(); } - if (mData.mAttachedVertexShader) + if (!linkUniforms(context, mInfoLog, mUniformLocationBindings)) { - mData.mAttachedVertexShader->release(); - mData.mAttachedVertexShader = nullptr; + return NoError(); } - } - mData.mAttributes.clear(); - mData.mActiveAttribLocationsMask.reset(); - mData.mTransformFeedbackVaryingVars.clear(); - mData.mUniforms.clear(); - mData.mUniformLocations.clear(); - mData.mUniformBlocks.clear(); - mData.mOutputVariables.clear(); + if (!linkInterfaceBlocks(context, mInfoLog)) + { + return NoError(); + } - mValidated = false; + ProgramLinkedResources resources = { + {0, PackMode::ANGLE_RELAXED}, + {&mState.mUniformBlocks, &mState.mUniforms}, + {&mState.mShaderStorageBlocks, &mState.mBufferVariables}}; - mLinked = false; -} + InitUniformBlockLinker(context, mState, &resources.uniformBlockLinker); + InitShaderStorageBlockLinker(context, mState, &resources.shaderStorageBlockLinker); -bool Program::isLinked() const -{ - return mLinked; -} + ANGLE_TRY_RESULT(mProgram->link(context, resources, mInfoLog), mLinked); + if (!mLinked) + { + return NoError(); + } + } + else + { + if (!fragmentShader || !fragmentShader->isCompiled(context)) + { + return NoError(); + } + ASSERT(fragmentShader->getType() == GL_FRAGMENT_SHADER); -Error Program::loadBinary(GLenum binaryFormat, const void *binary, GLsizei length) -{ - unlink(false); + if (!vertexShader || !vertexShader->isCompiled(context)) + { + return NoError(); + } + ASSERT(vertexShader->getType() == GL_VERTEX_SHADER); -#if ANGLE_PROGRAM_BINARY_LOAD != ANGLE_ENABLED - return Error(GL_NO_ERROR); -#else - ASSERT(binaryFormat == GL_PROGRAM_BINARY_ANGLE); - if (binaryFormat != GL_PROGRAM_BINARY_ANGLE) - { - mInfoLog << "Invalid program binary format."; - return Error(GL_NO_ERROR); - } + if (fragmentShader->getShaderVersion(context) != vertexShader->getShaderVersion(context)) + { + mInfoLog << "Fragment shader version does not match vertex shader version."; + return NoError(); + } - BinaryInputStream stream(binary, length); + if (!linkAttributes(context, mInfoLog)) + { + return NoError(); + } - int majorVersion = stream.readInt<int>(); - int minorVersion = stream.readInt<int>(); - if (majorVersion != ANGLE_MAJOR_VERSION || minorVersion != ANGLE_MINOR_VERSION) - { - mInfoLog << "Invalid program binary version."; - return Error(GL_NO_ERROR); - } + if (!linkVaryings(context, mInfoLog)) + { + return NoError(); + } - unsigned char commitString[ANGLE_COMMIT_HASH_SIZE]; - stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE); - if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) != 0) - { - mInfoLog << "Invalid program binary version."; - return Error(GL_NO_ERROR); - } + if (!linkUniforms(context, mInfoLog, mUniformLocationBindings)) + { + return NoError(); + } - static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8, - "Too many vertex attribs for mask"); - mData.mActiveAttribLocationsMask = stream.readInt<unsigned long>(); + if (!linkInterfaceBlocks(context, mInfoLog)) + { + return NoError(); + } - unsigned int attribCount = stream.readInt<unsigned int>(); - ASSERT(mData.mAttributes.empty()); - for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex) - { - sh::Attribute attrib; - LoadShaderVar(&stream, &attrib); - attrib.location = stream.readInt<int>(); - mData.mAttributes.push_back(attrib); - } + if (!linkValidateGlobalNames(context, mInfoLog)) + { + return NoError(); + } - unsigned int uniformCount = stream.readInt<unsigned int>(); - ASSERT(mData.mUniforms.empty()); - for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex) - { - LinkedUniform uniform; - LoadShaderVar(&stream, &uniform); + const auto &mergedVaryings = getMergedVaryings(context); - uniform.blockIndex = stream.readInt<int>(); - uniform.blockInfo.offset = stream.readInt<int>(); - uniform.blockInfo.arrayStride = stream.readInt<int>(); - uniform.blockInfo.matrixStride = stream.readInt<int>(); - uniform.blockInfo.isRowMajorMatrix = stream.readBool(); + mState.mNumViews = vertexShader->getNumViews(context); - mData.mUniforms.push_back(uniform); - } + linkOutputVariables(context); - const unsigned int uniformIndexCount = stream.readInt<unsigned int>(); - ASSERT(mData.mUniformLocations.empty()); - for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount; - uniformIndexIndex++) - { - VariableLocation variable; - stream.readString(&variable.name); - stream.readInt(&variable.element); - stream.readInt(&variable.index); + // Map the varyings to the register file + // In WebGL, we use a slightly different handling for packing variables. + auto packMode = data.getExtensions().webglCompatibility ? PackMode::WEBGL_STRICT + : PackMode::ANGLE_RELAXED; - mData.mUniformLocations.push_back(variable); - } + ProgramLinkedResources resources = { + {data.getCaps().maxVaryingVectors, packMode}, + {&mState.mUniformBlocks, &mState.mUniforms}, + {&mState.mShaderStorageBlocks, &mState.mBufferVariables}}; - unsigned int uniformBlockCount = stream.readInt<unsigned int>(); - ASSERT(mData.mUniformBlocks.empty()); - for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; - ++uniformBlockIndex) - { - UniformBlock uniformBlock; - stream.readString(&uniformBlock.name); - stream.readBool(&uniformBlock.isArray); - stream.readInt(&uniformBlock.arrayElement); - stream.readInt(&uniformBlock.dataSize); - stream.readBool(&uniformBlock.vertexStaticUse); - stream.readBool(&uniformBlock.fragmentStaticUse); + InitUniformBlockLinker(context, mState, &resources.uniformBlockLinker); + InitShaderStorageBlockLinker(context, mState, &resources.shaderStorageBlockLinker); - unsigned int numMembers = stream.readInt<unsigned int>(); - for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++) + if (!linkValidateTransformFeedback(context, mInfoLog, mergedVaryings, caps)) { - uniformBlock.memberUniformIndexes.push_back(stream.readInt<unsigned int>()); + return NoError(); } - mData.mUniformBlocks.push_back(uniformBlock); - } + if (!resources.varyingPacking.collectAndPackUserVaryings( + mInfoLog, mergedVaryings, mState.getTransformFeedbackVaryingNames())) + { + return NoError(); + } - unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>(); - ASSERT(mData.mTransformFeedbackVaryingVars.empty()); - for (unsigned int transformFeedbackVaryingIndex = 0; - transformFeedbackVaryingIndex < transformFeedbackVaryingCount; - ++transformFeedbackVaryingIndex) - { - sh::Varying varying; - stream.readInt(&varying.arraySize); - stream.readInt(&varying.type); - stream.readString(&varying.name); + ANGLE_TRY_RESULT(mProgram->link(context, resources, mInfoLog), mLinked); + if (!mLinked) + { + return NoError(); + } - mData.mTransformFeedbackVaryingVars.push_back(varying); + gatherTransformFeedbackVaryings(mergedVaryings); } - stream.readInt(&mData.mTransformFeedbackBufferMode); + gatherAtomicCounterBuffers(); + initInterfaceBlockBindings(); - unsigned int outputVarCount = stream.readInt<unsigned int>(); - for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex) - { - int locationIndex = stream.readInt<int>(); - VariableLocation locationData; - stream.readInt(&locationData.element); - stream.readInt(&locationData.index); - stream.readString(&locationData.name); - mData.mOutputVariables[locationIndex] = locationData; - } + setUniformValuesFromBindingQualifiers(); - stream.readInt(&mSamplerUniformRange.start); - stream.readInt(&mSamplerUniformRange.end); + ASSERT(mLinked); + updateLinkedShaderStages(); - rx::LinkResult result = mProgram->load(mInfoLog, &stream); - if (result.error.isError() || !result.linkSuccess) + // Mark implementation-specific unreferenced uniforms as ignored. + mProgram->markUnusedUniformLocations(&mState.mUniformLocations, &mState.mSamplerBindings); + + // Save to the program cache. + if (cache && (mState.mLinkedTransformFeedbackVaryings.empty() || + !context->getWorkarounds().disableProgramCachingForTransformFeedback)) { - return result.error; + cache->putProgram(programHash, context, this); } - mLinked = true; - return Error(GL_NO_ERROR); -#endif // #if ANGLE_PROGRAM_BINARY_LOAD == ANGLE_ENABLED + double delta = platform->currentTime(platform) - startTime; + int us = static_cast<int>(delta * 1000000.0); + ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramCacheMissTimeUS", us); + + return NoError(); } -Error Program::saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, GLsizei *length) const +void Program::updateLinkedShaderStages() { - if (binaryFormat) + if (mState.mAttachedVertexShader) { - *binaryFormat = GL_PROGRAM_BINARY_ANGLE; + mState.mLinkedShaderStages.set(SHADER_VERTEX); } - BinaryOutputStream stream; - - stream.writeInt(ANGLE_MAJOR_VERSION); - stream.writeInt(ANGLE_MINOR_VERSION); - stream.writeBytes(reinterpret_cast<const unsigned char*>(ANGLE_COMMIT_HASH), ANGLE_COMMIT_HASH_SIZE); - - stream.writeInt(mData.mActiveAttribLocationsMask.to_ulong()); - - stream.writeInt(mData.mAttributes.size()); - for (const sh::Attribute &attrib : mData.mAttributes) + if (mState.mAttachedFragmentShader) { - WriteShaderVar(&stream, attrib); - stream.writeInt(attrib.location); + mState.mLinkedShaderStages.set(SHADER_FRAGMENT); } - stream.writeInt(mData.mUniforms.size()); - for (const gl::LinkedUniform &uniform : mData.mUniforms) + if (mState.mAttachedComputeShader) { - WriteShaderVar(&stream, uniform); - - // FIXME: referenced - - stream.writeInt(uniform.blockIndex); - stream.writeInt(uniform.blockInfo.offset); - stream.writeInt(uniform.blockInfo.arrayStride); - stream.writeInt(uniform.blockInfo.matrixStride); - stream.writeInt(uniform.blockInfo.isRowMajorMatrix); + mState.mLinkedShaderStages.set(SHADER_COMPUTE); } +} - stream.writeInt(mData.mUniformLocations.size()); - for (const auto &variable : mData.mUniformLocations) - { - stream.writeString(variable.name); - stream.writeInt(variable.element); - stream.writeInt(variable.index); - } +// Returns the program object to an unlinked state, before re-linking, or at destruction +void Program::unlink() +{ + mState.mAttributes.clear(); + mState.mActiveAttribLocationsMask.reset(); + mState.mMaxActiveAttribLocation = 0; + mState.mLinkedTransformFeedbackVaryings.clear(); + mState.mUniforms.clear(); + mState.mUniformLocations.clear(); + mState.mUniformBlocks.clear(); + mState.mActiveUniformBlockBindings.reset(); + mState.mAtomicCounterBuffers.clear(); + mState.mOutputVariables.clear(); + mState.mOutputLocations.clear(); + mState.mOutputVariableTypes.clear(); + mState.mActiveOutputVariables.reset(); + mState.mComputeShaderLocalSize.fill(1); + mState.mSamplerBindings.clear(); + mState.mImageBindings.clear(); + mState.mNumViews = -1; + mState.mLinkedShaderStages.reset(); - stream.writeInt(mData.mUniformBlocks.size()); - for (const UniformBlock &uniformBlock : mData.mUniformBlocks) - { - stream.writeString(uniformBlock.name); - stream.writeInt(uniformBlock.isArray); - stream.writeInt(uniformBlock.arrayElement); - stream.writeInt(uniformBlock.dataSize); + mValidated = false; - stream.writeInt(uniformBlock.vertexStaticUse); - stream.writeInt(uniformBlock.fragmentStaticUse); + mLinked = false; +} - stream.writeInt(uniformBlock.memberUniformIndexes.size()); - for (unsigned int memberUniformIndex : uniformBlock.memberUniformIndexes) - { - stream.writeInt(memberUniformIndex); - } - } +bool Program::isLinked() const +{ + return mLinked; +} + +Error Program::loadBinary(const Context *context, + GLenum binaryFormat, + const void *binary, + GLsizei length) +{ + unlink(); - stream.writeInt(mData.mTransformFeedbackVaryingVars.size()); - for (const sh::Varying &varying : mData.mTransformFeedbackVaryingVars) +#if ANGLE_PROGRAM_BINARY_LOAD != ANGLE_ENABLED + return NoError(); +#else + ASSERT(binaryFormat == GL_PROGRAM_BINARY_ANGLE); + if (binaryFormat != GL_PROGRAM_BINARY_ANGLE) { - stream.writeInt(varying.arraySize); - stream.writeInt(varying.type); - stream.writeString(varying.name); + mInfoLog << "Invalid program binary format."; + return NoError(); } - stream.writeInt(mData.mTransformFeedbackBufferMode); + const uint8_t *bytes = reinterpret_cast<const uint8_t *>(binary); + ANGLE_TRY_RESULT( + MemoryProgramCache::Deserialize(context, this, &mState, bytes, length, mInfoLog), mLinked); - stream.writeInt(mData.mOutputVariables.size()); - for (const auto &outputPair : mData.mOutputVariables) - { - stream.writeInt(outputPair.first); - stream.writeInt(outputPair.second.element); - stream.writeInt(outputPair.second.index); - stream.writeString(outputPair.second.name); - } + // Currently we require the full shader text to compute the program hash. + // TODO(jmadill): Store the binary in the internal program cache. - stream.writeInt(mSamplerUniformRange.start); - stream.writeInt(mSamplerUniformRange.end); + return NoError(); +#endif // #if ANGLE_PROGRAM_BINARY_LOAD == ANGLE_ENABLED +} - gl::Error error = mProgram->save(&stream); - if (error.isError()) +Error Program::saveBinary(const Context *context, + GLenum *binaryFormat, + void *binary, + GLsizei bufSize, + GLsizei *length) const +{ + if (binaryFormat) { - return error; + *binaryFormat = GL_PROGRAM_BINARY_ANGLE; } - GLsizei streamLength = static_cast<GLsizei>(stream.length()); - const void *streamData = stream.data(); + angle::MemoryBuffer memoryBuf; + MemoryProgramCache::Serialize(context, this, &memoryBuf); + + GLsizei streamLength = static_cast<GLsizei>(memoryBuf.size()); + const uint8_t *streamState = memoryBuf.data(); if (streamLength > bufSize) { @@ -775,14 +1073,14 @@ Error Program::saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, G // TODO: This should be moved to the validation layer but computing the size of the binary before saving // it causes the save to happen twice. It may be possible to write the binary to a separate buffer, validate // sizes and then copy it. - return Error(GL_INVALID_OPERATION); + return InternalError(); } if (binary) { char *ptr = reinterpret_cast<char*>(binary); - memcpy(ptr, streamData, streamLength); + memcpy(ptr, streamState, streamLength); ptr += streamLength; ASSERT(ptr - streamLength == binary); @@ -793,13 +1091,13 @@ Error Program::saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, G *length = streamLength; } - return Error(GL_NO_ERROR); + return NoError(); } -GLint Program::getBinaryLength() const +GLint Program::getBinaryLength(const Context *context) const { GLint length; - Error error = saveBinary(nullptr, nullptr, std::numeric_limits<GLint>::max(), &length); + Error error = saveBinary(context, nullptr, nullptr, std::numeric_limits<GLint>::max(), &length); if (error.isError()) { return 0; @@ -812,21 +1110,36 @@ void Program::setBinaryRetrievableHint(bool retrievable) { // TODO(jmadill) : replace with dirty bits mProgram->setBinaryRetrievableHint(retrievable); - mData.mBinaryRetrieveableHint = retrievable; + mState.mBinaryRetrieveableHint = retrievable; } bool Program::getBinaryRetrievableHint() const { - return mData.mBinaryRetrieveableHint; + return mState.mBinaryRetrieveableHint; } -void Program::release() +void Program::setSeparable(bool separable) +{ + // TODO(yunchao) : replace with dirty bits + if (mState.mSeparable != separable) + { + mProgram->setSeparable(separable); + mState.mSeparable = separable; + } +} + +bool Program::isSeparable() const +{ + return mState.mSeparable; +} + +void Program::release(const Context *context) { mRefCount--; if (mRefCount == 0 && mDeleteStatus) { - mResourceManager->deleteProgram(mHandle); + mResourceManager->deleteProgram(context, mHandle); } } @@ -854,24 +1167,40 @@ void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shade { int total = 0; - if (mData.mAttachedVertexShader) + if (mState.mAttachedComputeShader) { if (total < maxCount) { - shaders[total] = mData.mAttachedVertexShader->getHandle(); + shaders[total] = mState.mAttachedComputeShader->getHandle(); + total++; } + } - total++; + if (mState.mAttachedVertexShader) + { + if (total < maxCount) + { + shaders[total] = mState.mAttachedVertexShader->getHandle(); + total++; + } } - if (mData.mAttachedFragmentShader) + if (mState.mAttachedFragmentShader) { if (total < maxCount) { - shaders[total] = mData.mAttachedFragmentShader->getHandle(); + shaders[total] = mState.mAttachedFragmentShader->getHandle(); + total++; } + } - total++; + if (mState.mAttachedGeometryShader) + { + if (total < maxCount) + { + shaders[total] = mState.mAttachedGeometryShader->getHandle(); + total++; + } } if (count) @@ -882,24 +1211,21 @@ void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shade GLuint Program::getAttributeLocation(const std::string &name) const { - for (const sh::Attribute &attribute : mData.mAttributes) - { - if (attribute.name == name && attribute.staticUse) - { - return attribute.location; - } - } - - return static_cast<GLuint>(-1); + return mState.getAttributeLocation(name); } bool Program::isAttribLocationActive(size_t attribLocation) const { - ASSERT(attribLocation < mData.mActiveAttribLocationsMask.size()); - return mData.mActiveAttribLocationsMask[attribLocation]; + ASSERT(attribLocation < mState.mActiveAttribLocationsMask.size()); + return mState.mActiveAttribLocationsMask[attribLocation]; } -void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) +void Program::getActiveAttribute(GLuint index, + GLsizei bufsize, + GLsizei *length, + GLint *size, + GLenum *type, + GLchar *name) const { if (!mLinked) { @@ -918,35 +1244,12 @@ void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, return; } - size_t attributeIndex = 0; - - for (const sh::Attribute &attribute : mData.mAttributes) - { - // Skip over inactive attributes - if (attribute.staticUse) - { - if (static_cast<size_t>(index) == attributeIndex) - { - break; - } - attributeIndex++; - } - } - - ASSERT(index == attributeIndex && attributeIndex < mData.mAttributes.size()); - const sh::Attribute &attrib = mData.mAttributes[attributeIndex]; + ASSERT(index < mState.mAttributes.size()); + const sh::Attribute &attrib = mState.mAttributes[index]; if (bufsize > 0) { - const char *string = attrib.name.c_str(); - - strncpy(name, string, bufsize); - name[bufsize - 1] = '\0'; - - if (length) - { - *length = static_cast<GLsizei>(strlen(name)); - } + CopyStringToBuffer(name, attrib.name, bufsize, length); } // Always a single 'type' instance @@ -961,14 +1264,7 @@ GLint Program::getActiveAttributeCount() const return 0; } - GLint count = 0; - - for (const sh::Attribute &attrib : mData.mAttributes) - { - count += (attrib.staticUse ? 1 : 0); - } - - return count; + return static_cast<GLint>(mState.mAttributes.size()); } GLint Program::getActiveAttributeMaxLength() const @@ -980,30 +1276,105 @@ GLint Program::getActiveAttributeMaxLength() const size_t maxLength = 0; - for (const sh::Attribute &attrib : mData.mAttributes) + for (const sh::Attribute &attrib : mState.mAttributes) { - if (attrib.staticUse) - { - maxLength = std::max(attrib.name.length() + 1, maxLength); - } + maxLength = std::max(attrib.name.length() + 1, maxLength); } return static_cast<GLint>(maxLength); } -GLint Program::getFragDataLocation(const std::string &name) const +GLuint Program::getInputResourceIndex(const GLchar *name) const { - std::string baseName(name); - unsigned int arrayIndex = ParseAndStripArrayIndex(&baseName); - for (auto outputPair : mData.mOutputVariables) + return GetResourceIndexFromName(mState.mAttributes, std::string(name)); +} + +GLuint Program::getOutputResourceIndex(const GLchar *name) const +{ + return GetResourceIndexFromName(mState.mOutputVariables, std::string(name)); +} + +size_t Program::getOutputResourceCount() const +{ + return (mLinked ? mState.mOutputVariables.size() : 0); +} + +template <typename T> +void Program::getResourceName(GLuint index, + const std::vector<T> &resources, + GLsizei bufSize, + GLsizei *length, + GLchar *name) const +{ + if (length) + { + *length = 0; + } + + if (!mLinked) { - const VariableLocation &outputVariable = outputPair.second; - if (outputVariable.name == baseName && (arrayIndex == GL_INVALID_INDEX || arrayIndex == outputVariable.element)) + if (bufSize > 0) { - return static_cast<GLint>(outputPair.first); + name[0] = '\0'; } + return; } - return -1; + ASSERT(index < resources.size()); + const auto &resource = resources[index]; + + if (bufSize > 0) + { + CopyStringToBuffer(name, resource.name, bufSize, length); + } +} + +void Program::getInputResourceName(GLuint index, + GLsizei bufSize, + GLsizei *length, + GLchar *name) const +{ + getResourceName(index, mState.mAttributes, bufSize, length, name); +} + +void Program::getOutputResourceName(GLuint index, + GLsizei bufSize, + GLsizei *length, + GLchar *name) const +{ + getResourceName(index, mState.mOutputVariables, bufSize, length, name); +} + +void Program::getUniformResourceName(GLuint index, + GLsizei bufSize, + GLsizei *length, + GLchar *name) const +{ + getResourceName(index, mState.mUniforms, bufSize, length, name); +} + +void Program::getBufferVariableResourceName(GLuint index, + GLsizei bufSize, + GLsizei *length, + GLchar *name) const +{ + getResourceName(index, mState.mBufferVariables, bufSize, length, name); +} + +const sh::Attribute &Program::getInputResource(GLuint index) const +{ + ASSERT(index < mState.mAttributes.size()); + return mState.mAttributes[index]; +} + +const sh::OutputVariable &Program::getOutputResource(GLuint index) const +{ + ASSERT(index < mState.mOutputVariables.size()); + return mState.mOutputVariables[index]; +} + +GLint Program::getFragDataLocation(const std::string &name) const +{ + return GetVariableLocation(mState.mOutputVariables, mState.mOutputLocations, name); } void Program::getActiveUniform(GLuint index, @@ -1016,27 +1387,16 @@ void Program::getActiveUniform(GLuint index, if (mLinked) { // index must be smaller than getActiveUniformCount() - ASSERT(index < mData.mUniforms.size()); - const LinkedUniform &uniform = mData.mUniforms[index]; + ASSERT(index < mState.mUniforms.size()); + const LinkedUniform &uniform = mState.mUniforms[index]; if (bufsize > 0) { std::string string = uniform.name; - if (uniform.isArray()) - { - string += "[0]"; - } - - strncpy(name, string.c_str(), bufsize); - name[bufsize - 1] = '\0'; - - if (length) - { - *length = static_cast<GLsizei>(strlen(name)); - } + CopyStringToBuffer(name, string, bufsize, length); } - *size = uniform.elementCount(); + *size = clampCast<GLint>(uniform.getBasicTypeElementCount()); *type = uniform.type; } else @@ -1060,7 +1420,7 @@ GLint Program::getActiveUniformCount() const { if (mLinked) { - return static_cast<GLint>(mData.mUniforms.size()); + return static_cast<GLint>(mState.mUniforms.size()); } else { @@ -1068,13 +1428,18 @@ GLint Program::getActiveUniformCount() const } } +size_t Program::getActiveBufferVariableCount() const +{ + return mLinked ? mState.mBufferVariables.size() : 0; +} + GLint Program::getActiveUniformMaxLength() const { size_t maxLength = 0; if (mLinked) { - for (const LinkedUniform &uniform : mData.mUniforms) + for (const LinkedUniform &uniform : mState.mUniforms) { if (!uniform.name.empty()) { @@ -1091,188 +1456,248 @@ GLint Program::getActiveUniformMaxLength() const return static_cast<GLint>(maxLength); } -GLint Program::getActiveUniformi(GLuint index, GLenum pname) const +bool Program::isValidUniformLocation(GLint location) const { - ASSERT(static_cast<size_t>(index) < mData.mUniforms.size()); - const gl::LinkedUniform &uniform = mData.mUniforms[index]; - switch (pname) - { - case GL_UNIFORM_TYPE: return static_cast<GLint>(uniform.type); - case GL_UNIFORM_SIZE: return static_cast<GLint>(uniform.elementCount()); - case GL_UNIFORM_NAME_LENGTH: return static_cast<GLint>(uniform.name.size() + 1 + (uniform.isArray() ? 3 : 0)); - case GL_UNIFORM_BLOCK_INDEX: return uniform.blockIndex; - case GL_UNIFORM_OFFSET: return uniform.blockInfo.offset; - case GL_UNIFORM_ARRAY_STRIDE: return uniform.blockInfo.arrayStride; - case GL_UNIFORM_MATRIX_STRIDE: return uniform.blockInfo.matrixStride; - case GL_UNIFORM_IS_ROW_MAJOR: return static_cast<GLint>(uniform.blockInfo.isRowMajorMatrix); - default: - UNREACHABLE(); - break; - } - return 0; + ASSERT(angle::IsValueInRangeForNumericType<GLint>(mState.mUniformLocations.size())); + return (location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size() && + mState.mUniformLocations[static_cast<size_t>(location)].used()); } -bool Program::isValidUniformLocation(GLint location) const +const LinkedUniform &Program::getUniformByLocation(GLint location) const { - ASSERT(rx::IsIntegerCastSafe<GLint>(mData.mUniformLocations.size())); - return (location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size()); + ASSERT(location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size()); + return mState.mUniforms[mState.getUniformIndexFromLocation(location)]; } -const LinkedUniform &Program::getUniformByLocation(GLint location) const +const VariableLocation &Program::getUniformLocation(GLint location) const +{ + ASSERT(location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size()); + return mState.mUniformLocations[location]; +} + +const std::vector<VariableLocation> &Program::getUniformLocations() const { - ASSERT(location >= 0 && static_cast<size_t>(location) < mData.mUniformLocations.size()); - return mData.mUniforms[mData.mUniformLocations[location].index]; + return mState.mUniformLocations; +} + +const LinkedUniform &Program::getUniformByIndex(GLuint index) const +{ + ASSERT(index < static_cast<size_t>(mState.mUniforms.size())); + return mState.mUniforms[index]; +} + +const BufferVariable &Program::getBufferVariableByIndex(GLuint index) const +{ + ASSERT(index < static_cast<size_t>(mState.mBufferVariables.size())); + return mState.mBufferVariables[index]; } GLint Program::getUniformLocation(const std::string &name) const { - return mData.getUniformLocation(name); + return GetVariableLocation(mState.mUniforms, mState.mUniformLocations, name); } GLuint Program::getUniformIndex(const std::string &name) const { - return mData.getUniformIndex(name); + return mState.getUniformIndexFromName(name); } void Program::setUniform1fv(GLint location, GLsizei count, const GLfloat *v) { - setUniformInternal(location, count * 1, v); - mProgram->setUniform1fv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 1, v); + mProgram->setUniform1fv(location, clampedCount, v); } void Program::setUniform2fv(GLint location, GLsizei count, const GLfloat *v) { - setUniformInternal(location, count * 2, v); - mProgram->setUniform2fv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 2, v); + mProgram->setUniform2fv(location, clampedCount, v); } void Program::setUniform3fv(GLint location, GLsizei count, const GLfloat *v) { - setUniformInternal(location, count * 3, v); - mProgram->setUniform3fv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 3, v); + mProgram->setUniform3fv(location, clampedCount, v); } void Program::setUniform4fv(GLint location, GLsizei count, const GLfloat *v) { - setUniformInternal(location, count * 4, v); - mProgram->setUniform4fv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 4, v); + mProgram->setUniform4fv(location, clampedCount, v); } -void Program::setUniform1iv(GLint location, GLsizei count, const GLint *v) +Program::SetUniformResult Program::setUniform1iv(GLint location, GLsizei count, const GLint *v) { - setUniformInternal(location, count * 1, v); - mProgram->setUniform1iv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 1, v); + + mProgram->setUniform1iv(location, clampedCount, v); + + if (mState.isSamplerUniformIndex(locationInfo.index)) + { + updateSamplerUniform(locationInfo, clampedCount, v); + return SetUniformResult::SamplerChanged; + } + + return SetUniformResult::NoSamplerChange; } void Program::setUniform2iv(GLint location, GLsizei count, const GLint *v) { - setUniformInternal(location, count * 2, v); - mProgram->setUniform2iv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 2, v); + mProgram->setUniform2iv(location, clampedCount, v); } void Program::setUniform3iv(GLint location, GLsizei count, const GLint *v) { - setUniformInternal(location, count * 3, v); - mProgram->setUniform3iv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 3, v); + mProgram->setUniform3iv(location, clampedCount, v); } void Program::setUniform4iv(GLint location, GLsizei count, const GLint *v) { - setUniformInternal(location, count * 4, v); - mProgram->setUniform4iv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 4, v); + mProgram->setUniform4iv(location, clampedCount, v); } void Program::setUniform1uiv(GLint location, GLsizei count, const GLuint *v) { - setUniformInternal(location, count * 1, v); - mProgram->setUniform1uiv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 1, v); + mProgram->setUniform1uiv(location, clampedCount, v); } void Program::setUniform2uiv(GLint location, GLsizei count, const GLuint *v) { - setUniformInternal(location, count * 2, v); - mProgram->setUniform2uiv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 2, v); + mProgram->setUniform2uiv(location, clampedCount, v); } void Program::setUniform3uiv(GLint location, GLsizei count, const GLuint *v) { - setUniformInternal(location, count * 3, v); - mProgram->setUniform3uiv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 3, v); + mProgram->setUniform3uiv(location, clampedCount, v); } void Program::setUniform4uiv(GLint location, GLsizei count, const GLuint *v) { - setUniformInternal(location, count * 4, v); - mProgram->setUniform4uiv(location, count, v); + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + GLsizei clampedCount = clampUniformCount(locationInfo, count, 4, v); + mProgram->setUniform4uiv(location, clampedCount, v); } void Program::setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<2, 2>(location, count, transpose, v); - mProgram->setUniformMatrix2fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<2, 2>(location, count, transpose, v); + mProgram->setUniformMatrix2fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<3, 3>(location, count, transpose, v); - mProgram->setUniformMatrix3fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<3, 3>(location, count, transpose, v); + mProgram->setUniformMatrix3fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<4, 4>(location, count, transpose, v); - mProgram->setUniformMatrix4fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<4, 4>(location, count, transpose, v); + mProgram->setUniformMatrix4fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<2, 3>(location, count, transpose, v); - mProgram->setUniformMatrix2x3fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<2, 3>(location, count, transpose, v); + mProgram->setUniformMatrix2x3fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<2, 4>(location, count, transpose, v); - mProgram->setUniformMatrix2x4fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<2, 4>(location, count, transpose, v); + mProgram->setUniformMatrix2x4fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<3, 2>(location, count, transpose, v); - mProgram->setUniformMatrix3x2fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<3, 2>(location, count, transpose, v); + mProgram->setUniformMatrix3x2fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<3, 4>(location, count, transpose, v); - mProgram->setUniformMatrix3x4fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<3, 4>(location, count, transpose, v); + mProgram->setUniformMatrix3x4fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<4, 2>(location, count, transpose, v); - mProgram->setUniformMatrix4x2fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<4, 2>(location, count, transpose, v); + mProgram->setUniformMatrix4x2fv(location, clampedCount, transpose, v); } void Program::setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *v) { - setMatrixUniformInternal<4, 3>(location, count, transpose, v); - mProgram->setUniformMatrix4x3fv(location, count, transpose, v); + GLsizei clampedCount = clampMatrixUniformCount<4, 3>(location, count, transpose, v); + mProgram->setUniformMatrix4x3fv(location, clampedCount, transpose, v); } -void Program::getUniformfv(GLint location, GLfloat *v) const +void Program::getUniformfv(const Context *context, GLint location, GLfloat *v) const { - getUniformInternal(location, v); + const auto &uniformLocation = mState.getUniformLocations()[location]; + const auto &uniform = mState.getUniforms()[uniformLocation.index]; + + GLenum nativeType = gl::VariableComponentType(uniform.type); + if (nativeType == GL_FLOAT) + { + mProgram->getUniformfv(context, location, v); + } + else + { + getUniformInternal(context, v, location, nativeType, + gl::VariableComponentCount(uniform.type)); + } } -void Program::getUniformiv(GLint location, GLint *v) const +void Program::getUniformiv(const Context *context, GLint location, GLint *v) const { - getUniformInternal(location, v); + const auto &uniformLocation = mState.getUniformLocations()[location]; + const auto &uniform = mState.getUniforms()[uniformLocation.index]; + + GLenum nativeType = gl::VariableComponentType(uniform.type); + if (nativeType == GL_INT || nativeType == GL_BOOL) + { + mProgram->getUniformiv(context, location, v); + } + else + { + getUniformInternal(context, v, location, nativeType, + gl::VariableComponentCount(uniform.type)); + } } -void Program::getUniformuiv(GLint location, GLuint *v) const +void Program::getUniformuiv(const Context *context, GLint location, GLuint *v) const { - getUniformInternal(location, v); + const auto &uniformLocation = mState.getUniformLocations()[location]; + const auto &uniform = mState.getUniforms()[uniformLocation.index]; + + GLenum nativeType = gl::VariableComponentType(uniform.type); + if (nativeType == GL_UNSIGNED_INT) + { + mProgram->getUniformuiv(context, location, v); + } + else + { + getUniformInternal(context, v, location, nativeType, + gl::VariableComponentCount(uniform.type)); + } } void Program::flagForDeletion() @@ -1291,7 +1716,7 @@ void Program::validate(const Caps &caps) if (mLinked) { - mValidated = (mProgram->validate(caps, &mInfoLog) == GL_TRUE); + mValidated = ConvertToBool(mProgram->validate(caps, &mInfoLog)); } else { @@ -1320,22 +1745,15 @@ bool Program::validateSamplers(InfoLog *infoLog, const Caps &caps) // 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 (unsigned int samplerIndex = mSamplerUniformRange.start; - samplerIndex < mSamplerUniformRange.end; ++samplerIndex) + for (const auto &samplerBinding : mState.mSamplerBindings) { - const LinkedUniform &uniform = mData.mUniforms[samplerIndex]; - ASSERT(uniform.isSampler()); - - if (!uniform.staticUse) + if (samplerBinding.unreferenced) continue; - const GLuint *dataPtr = reinterpret_cast<const GLuint *>(uniform.getDataPtrToElement(0)); - GLenum textureType = SamplerTypeToTextureType(uniform.type); + GLenum textureType = samplerBinding.textureType; - for (unsigned int arrayElement = 0; arrayElement < uniform.elementCount(); ++arrayElement) + for (GLuint textureUnit : samplerBinding.boundTextureUnits) { - GLuint textureUnit = dataPtr[arrayElement]; - if (textureUnit >= caps.maxCombinedTextureImageUnits) { if (infoLog) @@ -1382,70 +1800,34 @@ bool Program::isValidated() const GLuint Program::getActiveUniformBlockCount() const { - return static_cast<GLuint>(mData.mUniformBlocks.size()); + return static_cast<GLuint>(mState.mUniformBlocks.size()); } -void Program::getActiveUniformBlockName(GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) const +GLuint Program::getActiveAtomicCounterBufferCount() const { - ASSERT(uniformBlockIndex < - mData.mUniformBlocks.size()); // index must be smaller than getActiveUniformBlockCount() - - const UniformBlock &uniformBlock = mData.mUniformBlocks[uniformBlockIndex]; - - if (bufSize > 0) - { - std::string string = uniformBlock.name; - - if (uniformBlock.isArray) - { - string += ArrayString(uniformBlock.arrayElement); - } - - strncpy(uniformBlockName, string.c_str(), bufSize); - uniformBlockName[bufSize - 1] = '\0'; + return static_cast<GLuint>(mState.mAtomicCounterBuffers.size()); +} - if (length) - { - *length = static_cast<GLsizei>(strlen(uniformBlockName)); - } - } +GLuint Program::getActiveShaderStorageBlockCount() const +{ + return static_cast<GLuint>(mState.mShaderStorageBlocks.size()); } -void Program::getActiveUniformBlockiv(GLuint uniformBlockIndex, GLenum pname, GLint *params) const +void Program::getActiveUniformBlockName(const GLuint blockIndex, + GLsizei bufSize, + GLsizei *length, + GLchar *blockName) const { - ASSERT(uniformBlockIndex < - mData.mUniformBlocks.size()); // index must be smaller than getActiveUniformBlockCount() + GetInterfaceBlockName(blockIndex, mState.mUniformBlocks, bufSize, length, blockName); +} - const UniformBlock &uniformBlock = mData.mUniformBlocks[uniformBlockIndex]; +void Program::getActiveShaderStorageBlockName(const GLuint blockIndex, + GLsizei bufSize, + GLsizei *length, + GLchar *blockName) const +{ - switch (pname) - { - case GL_UNIFORM_BLOCK_DATA_SIZE: - *params = static_cast<GLint>(uniformBlock.dataSize); - break; - case GL_UNIFORM_BLOCK_NAME_LENGTH: - *params = - static_cast<GLint>(uniformBlock.name.size() + 1 + (uniformBlock.isArray ? 3 : 0)); - break; - case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: - *params = static_cast<GLint>(uniformBlock.memberUniformIndexes.size()); - break; - case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: - { - for (unsigned int blockMemberIndex = 0; blockMemberIndex < uniformBlock.memberUniformIndexes.size(); blockMemberIndex++) - { - params[blockMemberIndex] = static_cast<GLint>(uniformBlock.memberUniformIndexes[blockMemberIndex]); - } - } - break; - case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: - *params = static_cast<GLint>(uniformBlock.vertexStaticUse); - break; - case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: - *params = static_cast<GLint>(uniformBlock.fragmentStaticUse); - break; - default: UNREACHABLE(); - } + GetInterfaceBlockName(blockIndex, mState.mShaderStorageBlocks, bufSize, length, blockName); } GLint Program::getActiveUniformBlockMaxLength() const @@ -1454,18 +1836,14 @@ GLint Program::getActiveUniformBlockMaxLength() const if (mLinked) { - unsigned int numUniformBlocks = static_cast<unsigned int>(mData.mUniformBlocks.size()); + unsigned int numUniformBlocks = static_cast<unsigned int>(mState.mUniformBlocks.size()); for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < numUniformBlocks; uniformBlockIndex++) { - const UniformBlock &uniformBlock = mData.mUniformBlocks[uniformBlockIndex]; + const InterfaceBlock &uniformBlock = mState.mUniformBlocks[uniformBlockIndex]; if (!uniformBlock.name.empty()) { - const int length = static_cast<int>(uniformBlock.name.length()) + 1; - - // Counting in "[0]". - const int arrayLength = (uniformBlock.isArray ? 3 : 0); - - maxLength = std::max(length + arrayLength, maxLength); + int length = static_cast<int>(uniformBlock.nameWithArrayIndex().length()); + maxLength = std::max(length + 1, maxLength); } } } @@ -1475,87 +1853,77 @@ GLint Program::getActiveUniformBlockMaxLength() const GLuint Program::getUniformBlockIndex(const std::string &name) const { - size_t subscript = GL_INVALID_INDEX; - std::string baseName = gl::ParseUniformName(name, &subscript); + return GetInterfaceBlockIndex(mState.mUniformBlocks, name); +} - unsigned int numUniformBlocks = static_cast<unsigned int>(mData.mUniformBlocks.size()); - for (unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++) - { - const gl::UniformBlock &uniformBlock = mData.mUniformBlocks[blockIndex]; - if (uniformBlock.name == baseName) - { - const bool arrayElementZero = - (subscript == GL_INVALID_INDEX && - (!uniformBlock.isArray || uniformBlock.arrayElement == 0)); - if (subscript == uniformBlock.arrayElement || arrayElementZero) - { - return blockIndex; - } - } - } +GLuint Program::getShaderStorageBlockIndex(const std::string &name) const +{ + return GetInterfaceBlockIndex(mState.mShaderStorageBlocks, name); +} - return GL_INVALID_INDEX; +const InterfaceBlock &Program::getUniformBlockByIndex(GLuint index) const +{ + ASSERT(index < static_cast<GLuint>(mState.mUniformBlocks.size())); + return mState.mUniformBlocks[index]; } -const UniformBlock &Program::getUniformBlockByIndex(GLuint index) const +const InterfaceBlock &Program::getShaderStorageBlockByIndex(GLuint index) const { - ASSERT(index < static_cast<GLuint>(mData.mUniformBlocks.size())); - return mData.mUniformBlocks[index]; + ASSERT(index < static_cast<GLuint>(mState.mShaderStorageBlocks.size())); + return mState.mShaderStorageBlocks[index]; } void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding) { - mData.mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding; + mState.mUniformBlocks[uniformBlockIndex].binding = uniformBlockBinding; + mState.mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlockBinding != 0); mProgram->setUniformBlockBinding(uniformBlockIndex, uniformBlockBinding); } GLuint Program::getUniformBlockBinding(GLuint uniformBlockIndex) const { - return mData.getUniformBlockBinding(uniformBlockIndex); + return mState.getUniformBlockBinding(uniformBlockIndex); } -void Program::resetUniformBlockBindings() +GLuint Program::getShaderStorageBlockBinding(GLuint shaderStorageBlockIndex) const { - for (unsigned int blockId = 0; blockId < IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS; blockId++) - { - mData.mUniformBlockBindings[blockId] = 0; - } - mData.mActiveUniformBlockBindings.reset(); + return mState.getShaderStorageBlockBinding(shaderStorageBlockIndex); } void Program::setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode) { - mData.mTransformFeedbackVaryingNames.resize(count); + mState.mTransformFeedbackVaryingNames.resize(count); for (GLsizei i = 0; i < count; i++) { - mData.mTransformFeedbackVaryingNames[i] = varyings[i]; + mState.mTransformFeedbackVaryingNames[i] = varyings[i]; } - mData.mTransformFeedbackBufferMode = bufferMode; + mState.mTransformFeedbackBufferMode = bufferMode; } void Program::getTransformFeedbackVarying(GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) const { if (mLinked) { - ASSERT(index < mData.mTransformFeedbackVaryingVars.size()); - const sh::Varying &varying = mData.mTransformFeedbackVaryingVars[index]; - GLsizei lastNameIdx = std::min(bufSize - 1, static_cast<GLsizei>(varying.name.length())); + ASSERT(index < mState.mLinkedTransformFeedbackVaryings.size()); + const auto &var = mState.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 = varying.elementCount(); + *size = var.size(); } if (type) { - *type = varying.type; + *type = var.type; } if (name) { - memcpy(name, varying.name.c_str(), lastNameIdx); + memcpy(name, varName.c_str(), lastNameIdx); name[lastNameIdx] = '\0'; } } @@ -1565,7 +1933,7 @@ GLsizei Program::getTransformFeedbackVaryingCount() const { if (mLinked) { - return static_cast<GLsizei>(mData.mTransformFeedbackVaryingVars.size()); + return static_cast<GLsizei>(mState.mLinkedTransformFeedbackVaryings.size()); } else { @@ -1578,9 +1946,10 @@ GLsizei Program::getTransformFeedbackVaryingMaxLength() const if (mLinked) { GLsizei maxSize = 0; - for (const sh::Varying &varying : mData.mTransformFeedbackVaryingVars) + for (const auto &var : mState.mLinkedTransformFeedbackVaryings) { - maxSize = std::max(maxSize, static_cast<GLsizei>(varying.name.length() + 1)); + maxSize = + std::max(maxSize, static_cast<GLsizei>(var.nameWithArrayIndex().length() + 1)); } return maxSize; @@ -1593,16 +1962,20 @@ GLsizei Program::getTransformFeedbackVaryingMaxLength() const GLenum Program::getTransformFeedbackBufferMode() const { - return mData.mTransformFeedbackBufferMode; + return mState.mTransformFeedbackBufferMode; } -// static -bool Program::linkVaryings(InfoLog &infoLog, - const Shader *vertexShader, - const Shader *fragmentShader) +bool Program::linkVaryings(const Context *context, InfoLog &infoLog) const { - const std::vector<sh::Varying> &vertexVaryings = vertexShader->getVaryings(); - const std::vector<sh::Varying> &fragmentVaryings = fragmentShader->getVaryings(); + Shader *vertexShader = mState.mAttachedVertexShader; + Shader *fragmentShader = mState.mAttachedFragmentShader; + + ASSERT(vertexShader->getShaderVersion(context) == fragmentShader->getShaderVersion(context)); + + const std::vector<sh::Varying> &vertexVaryings = vertexShader->getOutputVaryings(context); + const std::vector<sh::Varying> &fragmentVaryings = fragmentShader->getInputVaryings(context); + + std::map<GLuint, std::string> staticFragmentInputLocations; for (const sh::Varying &output : fragmentVaryings) { @@ -1619,7 +1992,8 @@ bool Program::linkVaryings(InfoLog &infoLog, if (output.name == input.name) { ASSERT(!input.isBuiltIn()); - if (!linkValidateVaryings(infoLog, output.name, input, output)) + if (!linkValidateVaryings(infoLog, output.name, input, output, + vertexShader->getShaderVersion(context))) { return false; } @@ -1635,6 +2009,34 @@ bool Program::linkVaryings(InfoLog &infoLog, infoLog << "Fragment varying " << output.name << " does not match any vertex varying"; return false; } + + // Check for aliased path rendering input bindings (if any). + // If more than one binding refer statically to the same + // location the link must fail. + + if (!output.staticUse) + continue; + + const auto inputBinding = mFragmentInputBindings.getBinding(output.name); + if (inputBinding == -1) + continue; + + const auto it = staticFragmentInputLocations.find(inputBinding); + if (it == std::end(staticFragmentInputLocations)) + { + staticFragmentInputLocations.insert(std::make_pair(inputBinding, output.name)); + } + else + { + infoLog << "Binding for fragment input " << output.name << " conflicts with " + << it->second; + return false; + } + } + + if (!linkValidateBuiltInVaryings(context, infoLog)) + { + return false; } // TODO(jmadill): verify no unmatched vertex varyings? @@ -1642,67 +2044,135 @@ bool Program::linkVaryings(InfoLog &infoLog, return true; } -bool Program::linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps) +bool Program::linkUniforms(const Context *context, + InfoLog &infoLog, + const Bindings &uniformLocationBindings) { - const std::vector<sh::Uniform> &vertexUniforms = mData.mAttachedVertexShader->getUniforms(); - const std::vector<sh::Uniform> &fragmentUniforms = mData.mAttachedFragmentShader->getUniforms(); + UniformLinker linker(mState); + if (!linker.link(context, infoLog, uniformLocationBindings)) + { + return false; + } + + linker.getResults(&mState.mUniforms, &mState.mUniformLocations); - // Check that uniforms defined in the vertex and fragment shaders are identical - std::map<std::string, LinkedUniform> linkedUniforms; + linkSamplerAndImageBindings(); - for (const sh::Uniform &vertexUniform : vertexUniforms) + if (!linkAtomicCounterBuffers()) { - linkedUniforms[vertexUniform.name] = LinkedUniform(vertexUniform); + return false; } - for (const sh::Uniform &fragmentUniform : fragmentUniforms) + return true; +} + +void Program::linkSamplerAndImageBindings() +{ + unsigned int high = static_cast<unsigned int>(mState.mUniforms.size()); + unsigned int low = high; + + for (auto counterIter = mState.mUniforms.rbegin(); + counterIter != mState.mUniforms.rend() && counterIter->isAtomicCounter(); ++counterIter) { - auto entry = linkedUniforms.find(fragmentUniform.name); - if (entry != linkedUniforms.end()) + --low; + } + + mState.mAtomicCounterUniformRange = RangeUI(low, high); + + high = low; + + for (auto imageIter = mState.mUniforms.rbegin(); + imageIter != mState.mUniforms.rend() && imageIter->isImage(); ++imageIter) + { + --low; + } + + mState.mImageUniformRange = RangeUI(low, high); + + // If uniform is a image type, insert it into the mImageBindings array. + for (unsigned int imageIndex : mState.mImageUniformRange) + { + // ES3.1 (section 7.6.1) and GLSL ES3.1 (section 4.4.5), Uniform*i{v} commands + // cannot load values into a uniform defined as an image. if declare without a + // binding qualifier, any uniform image variable (include all elements of + // unbound image array) shoud be bound to unit zero. + auto &imageUniform = mState.mUniforms[imageIndex]; + if (imageUniform.binding == -1) { - LinkedUniform *vertexUniform = &entry->second; - const std::string &uniformName = "uniform '" + vertexUniform->name + "'"; - if (!linkValidateUniforms(infoLog, uniformName, *vertexUniform, fragmentUniform)) - { - return false; - } + mState.mImageBindings.emplace_back( + ImageBinding(imageUniform.getBasicTypeElementCount())); + } + else + { + mState.mImageBindings.emplace_back( + ImageBinding(imageUniform.binding, imageUniform.getBasicTypeElementCount())); } } - // Flatten the uniforms list (nested fields) into a simple list (no nesting). - // Also check the maximum uniform vector and sampler counts. - if (!flattenUniformsAndCheckCaps(caps, infoLog)) + high = low; + + for (auto samplerIter = mState.mUniforms.rbegin() + mState.mImageUniformRange.length(); + samplerIter != mState.mUniforms.rend() && samplerIter->isSampler(); ++samplerIter) { - return false; + --low; } - indexUniforms(); + mState.mSamplerUniformRange = RangeUI(low, high); - return true; + // If uniform is a sampler type, insert it into the mSamplerBindings array. + for (unsigned int samplerIndex : mState.mSamplerUniformRange) + { + const auto &samplerUniform = mState.mUniforms[samplerIndex]; + GLenum textureType = SamplerTypeToTextureType(samplerUniform.type); + mState.mSamplerBindings.emplace_back( + SamplerBinding(textureType, samplerUniform.getBasicTypeElementCount(), false)); + } } -void Program::indexUniforms() +bool Program::linkAtomicCounterBuffers() { - for (size_t uniformIndex = 0; uniformIndex < mData.mUniforms.size(); uniformIndex++) + for (unsigned int index : mState.mAtomicCounterUniformRange) { - const gl::LinkedUniform &uniform = mData.mUniforms[uniformIndex]; - - for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++) + auto &uniform = mState.mUniforms[index]; + bool found = false; + for (unsigned int bufferIndex = 0; bufferIndex < mState.mAtomicCounterBuffers.size(); + ++bufferIndex) { - if (!uniform.isBuiltIn()) + auto &buffer = mState.mAtomicCounterBuffers[bufferIndex]; + if (buffer.binding == uniform.binding) { - // Assign in-order uniform locations - mData.mUniformLocations.push_back(gl::VariableLocation( - uniform.name, arrayIndex, static_cast<unsigned int>(uniformIndex))); + buffer.memberIndexes.push_back(index); + uniform.bufferIndex = bufferIndex; + found = true; + buffer.unionReferencesWith(uniform); + break; } } + if (!found) + { + AtomicCounterBuffer atomicCounterBuffer; + atomicCounterBuffer.binding = uniform.binding; + atomicCounterBuffer.memberIndexes.push_back(index); + atomicCounterBuffer.unionReferencesWith(uniform); + mState.mAtomicCounterBuffers.push_back(atomicCounterBuffer); + uniform.bufferIndex = static_cast<int>(mState.mAtomicCounterBuffers.size() - 1); + } } + // TODO(jie.a.chen@intel.com): Count each atomic counter buffer to validate against + // gl_Max[Vertex|Fragment|Compute|Combined]AtomicCounterBuffers. + + return true; } -bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform) +bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, + const std::string &uniformName, + const sh::InterfaceBlockField &vertexUniform, + const sh::InterfaceBlockField &fragmentUniform, + bool webglCompatibility) { - // We don't validate precision on UBO fields. See resolution of Khronos bug 10287. - if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, false)) + // If webgl, validate precision of UBO fields, otherwise don't. See Khronos bug 10287. + if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, + webglCompatibility)) { return false; } @@ -1716,32 +2186,34 @@ bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::stri return true; } -// Determines the mapping between GL attributes and Direct3D 9 vertex stream usage indices -bool Program::linkAttributes(const gl::Data &data, - InfoLog &infoLog, - const AttributeBindings &attributeBindings, - const Shader *vertexShader) +// Assigns locations to all attributes from the bindings and program locations. +bool Program::linkAttributes(const Context *context, InfoLog &infoLog) { + const ContextState &data = context->getContextState(); + auto *vertexShader = mState.getAttachedVertexShader(); + unsigned int usedLocations = 0; - mData.mAttributes = vertexShader->getActiveAttributes(); - GLuint maxAttribs = data.caps->maxVertexAttributes; + mState.mAttributes = vertexShader->getActiveAttributes(context); + GLuint maxAttribs = data.getCaps().maxVertexAttributes; // TODO(jmadill): handle aliasing robustly - if (mData.mAttributes.size() > maxAttribs) + if (mState.mAttributes.size() > maxAttribs) { infoLog << "Too many vertex attributes."; return false; } - std::vector<sh::Attribute *> usedAttribMap(data.caps->maxVertexAttributes, nullptr); + std::vector<sh::Attribute *> usedAttribMap(maxAttribs, nullptr); // Link attributes that have a binding location - for (sh::Attribute &attribute : mData.mAttributes) + for (sh::Attribute &attribute : mState.mAttributes) { - // TODO(jmadill): do staticUse filtering step here, or not at all - ASSERT(attribute.staticUse); + // GLSL ES 3.10 January 2016 section 4.3.4: Vertex shader inputs can't be arrays or + // structures, so we don't need to worry about adjusting their names or generating entries + // for each member/element (unlike uniforms for example). + ASSERT(!attribute.isArray() && !attribute.isStruct()); - int bindingLocation = attributeBindings.getAttributeBinding(attribute.name); + int bindingLocation = mAttributeBindings.getBinding(attribute.name); if (attribute.location == -1 && bindingLocation != -1) { attribute.location = bindingLocation; @@ -1788,10 +2260,8 @@ bool Program::linkAttributes(const gl::Data &data, } // Link attributes that don't have a binding location - for (sh::Attribute &attribute : mData.mAttributes) + for (sh::Attribute &attribute : mState.mAttributes) { - ASSERT(attribute.staticUse); - // Not set by glBindAttribLocation or by location layout qualifier if (attribute.location == -1) { @@ -1808,82 +2278,150 @@ bool Program::linkAttributes(const gl::Data &data, } } - for (const sh::Attribute &attribute : mData.mAttributes) + for (const sh::Attribute &attribute : mState.mAttributes) { - ASSERT(attribute.staticUse); ASSERT(attribute.location != -1); - int regs = VariableRegisterCount(attribute.type); + unsigned int regs = static_cast<unsigned int>(VariableRegisterCount(attribute.type)); - for (int r = 0; r < regs; r++) + for (unsigned int r = 0; r < regs; r++) { - mData.mActiveAttribLocationsMask.set(attribute.location + r); + unsigned int location = static_cast<unsigned int>(attribute.location) + r; + mState.mActiveAttribLocationsMask.set(location); + mState.mMaxActiveAttribLocation = + std::max(mState.mMaxActiveAttribLocation, location + 1); } } return true; } -bool Program::linkUniformBlocks(InfoLog &infoLog, const Caps &caps) +bool Program::validateVertexAndFragmentInterfaceBlocks( + const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks, + const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks, + InfoLog &infoLog, + bool webglCompatibility) const { - const Shader &vertexShader = *mData.mAttachedVertexShader; - const Shader &fragmentShader = *mData.mAttachedFragmentShader; - - const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks = vertexShader.getInterfaceBlocks(); - const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks = fragmentShader.getInterfaceBlocks(); - // Check that interface blocks defined in the vertex and fragment shaders are identical - typedef std::map<std::string, const sh::InterfaceBlock*> UniformBlockMap; - UniformBlockMap linkedUniformBlocks; + typedef std::map<std::string, const sh::InterfaceBlock *> InterfaceBlockMap; + InterfaceBlockMap linkedInterfaceBlocks; - GLuint vertexBlockCount = 0; for (const sh::InterfaceBlock &vertexInterfaceBlock : vertexInterfaceBlocks) { - linkedUniformBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock; + linkedInterfaceBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock; + } - // Note: shared and std140 layouts are always considered active - if (vertexInterfaceBlock.staticUse || vertexInterfaceBlock.layout != sh::BLOCKLAYOUT_PACKED) + for (const sh::InterfaceBlock &fragmentInterfaceBlock : fragmentInterfaceBlocks) + { + auto entry = linkedInterfaceBlocks.find(fragmentInterfaceBlock.name); + if (entry != linkedInterfaceBlocks.end()) { - if (++vertexBlockCount > caps.maxVertexUniformBlocks) + const sh::InterfaceBlock &vertexInterfaceBlock = *entry->second; + if (!areMatchingInterfaceBlocks(infoLog, vertexInterfaceBlock, fragmentInterfaceBlock, + webglCompatibility)) { - infoLog << "Vertex shader uniform block count exceed GL_MAX_VERTEX_UNIFORM_BLOCKS (" - << caps.maxVertexUniformBlocks << ")"; return false; } } + // TODO(jiajia.qin@intel.com): Add + // MAX_COMBINED_UNIFORM_BLOCKS/MAX_COMBINED_SHADER_STORAGE_BLOCKS validation. } + return true; +} - GLuint fragmentBlockCount = 0; - for (const sh::InterfaceBlock &fragmentInterfaceBlock : fragmentInterfaceBlocks) +bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog) +{ + const auto &caps = context->getCaps(); + + if (mState.mAttachedComputeShader) { - auto entry = linkedUniformBlocks.find(fragmentInterfaceBlock.name); - if (entry != linkedUniformBlocks.end()) + Shader &computeShader = *mState.mAttachedComputeShader; + const auto &computeUniformBlocks = computeShader.getUniformBlocks(context); + + if (!validateInterfaceBlocksCount( + caps.maxComputeUniformBlocks, computeUniformBlocks, + "Compute shader uniform block count exceeds GL_MAX_COMPUTE_UNIFORM_BLOCKS (", + infoLog)) { - const sh::InterfaceBlock &vertexInterfaceBlock = *entry->second; - if (!areMatchingInterfaceBlocks(infoLog, vertexInterfaceBlock, fragmentInterfaceBlock)) - { - return false; - } + return false; } - // Note: shared and std140 layouts are always considered active - if (fragmentInterfaceBlock.staticUse || - fragmentInterfaceBlock.layout != sh::BLOCKLAYOUT_PACKED) + const auto &computeShaderStorageBlocks = computeShader.getShaderStorageBlocks(context); + if (!validateInterfaceBlocksCount(caps.maxComputeShaderStorageBlocks, + computeShaderStorageBlocks, + "Compute shader shader storage block count exceeds " + "GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS (", + infoLog)) { - if (++fragmentBlockCount > caps.maxFragmentUniformBlocks) - { - infoLog - << "Fragment shader uniform block count exceed GL_MAX_FRAGMENT_UNIFORM_BLOCKS (" - << caps.maxFragmentUniformBlocks << ")"; - return false; - } + return false; } + return true; } + Shader &vertexShader = *mState.mAttachedVertexShader; + Shader &fragmentShader = *mState.mAttachedFragmentShader; + + const auto &vertexUniformBlocks = vertexShader.getUniformBlocks(context); + const auto &fragmentUniformBlocks = fragmentShader.getUniformBlocks(context); + + if (!validateInterfaceBlocksCount( + caps.maxVertexUniformBlocks, vertexUniformBlocks, + "Vertex shader uniform block count exceeds GL_MAX_VERTEX_UNIFORM_BLOCKS (", infoLog)) + { + return false; + } + if (!validateInterfaceBlocksCount( + caps.maxFragmentUniformBlocks, fragmentUniformBlocks, + "Fragment shader uniform block count exceeds GL_MAX_FRAGMENT_UNIFORM_BLOCKS (", + infoLog)) + { + + return false; + } + + bool webglCompatibility = context->getExtensions().webglCompatibility; + if (!validateVertexAndFragmentInterfaceBlocks(vertexUniformBlocks, fragmentUniformBlocks, + infoLog, webglCompatibility)) + { + return false; + } + + if (context->getClientVersion() >= Version(3, 1)) + { + const auto &vertexShaderStorageBlocks = vertexShader.getShaderStorageBlocks(context); + const auto &fragmentShaderStorageBlocks = fragmentShader.getShaderStorageBlocks(context); + + if (!validateInterfaceBlocksCount(caps.maxVertexShaderStorageBlocks, + vertexShaderStorageBlocks, + "Vertex shader shader storage block count exceeds " + "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS (", + infoLog)) + { + return false; + } + if (!validateInterfaceBlocksCount(caps.maxFragmentShaderStorageBlocks, + fragmentShaderStorageBlocks, + "Fragment shader shader storage block count exceeds " + "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS (", + infoLog)) + { + + return false; + } + + if (!validateVertexAndFragmentInterfaceBlocks(vertexShaderStorageBlocks, + fragmentShaderStorageBlocks, infoLog, + webglCompatibility)) + { + return false; + } + } return true; } -bool Program::areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock, - const sh::InterfaceBlock &fragmentInterfaceBlock) +bool Program::areMatchingInterfaceBlocks(InfoLog &infoLog, + const sh::InterfaceBlock &vertexInterfaceBlock, + const sh::InterfaceBlock &fragmentInterfaceBlock, + bool webglCompatibility) const { const char* blockName = vertexInterfaceBlock.name.c_str(); // validate blocks for the same member types @@ -1899,7 +2437,9 @@ bool Program::areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::Interfa << "' between vertex and fragment shaders"; return false; } - if (vertexInterfaceBlock.layout != fragmentInterfaceBlock.layout || vertexInterfaceBlock.isRowMajorLayout != fragmentInterfaceBlock.isRowMajorLayout) + if (vertexInterfaceBlock.layout != fragmentInterfaceBlock.layout || + vertexInterfaceBlock.isRowMajorLayout != fragmentInterfaceBlock.isRowMajorLayout || + vertexInterfaceBlock.binding != fragmentInterfaceBlock.binding) { infoLog << "Layout qualifiers differ for interface block '" << blockName << "' between vertex and fragment shaders"; @@ -1920,7 +2460,8 @@ bool Program::areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::Interfa return false; } std::string memberName = "interface block '" + vertexInterfaceBlock.name + "' member '" + vertexMember.name + "'"; - if (!linkValidateInterfaceBlockFields(infoLog, memberName, vertexMember, fragmentMember)) + if (!linkValidateInterfaceBlockFields(infoLog, memberName, vertexMember, fragmentMember, + webglCompatibility)) { return false; } @@ -1936,7 +2477,7 @@ bool Program::linkValidateVariablesBase(InfoLog &infoLog, const std::string &var infoLog << "Types for " << variableName << " differ between vertex and fragment shaders"; return false; } - if (vertexVariable.arraySize != fragmentVariable.arraySize) + if (vertexVariable.arraySizes != fragmentVariable.arraySizes) { infoLog << "Array sizes for " << variableName << " differ between vertex and fragment shaders"; return false; @@ -1946,6 +2487,12 @@ bool Program::linkValidateVariablesBase(InfoLog &infoLog, const std::string &var infoLog << "Precisions for " << variableName << " differ between vertex and fragment shaders"; return false; } + if (vertexVariable.structName != fragmentVariable.structName) + { + infoLog << "Structure names for " << variableName + << " differ between vertex and fragment shaders"; + return false; + } if (vertexVariable.fields.size() != fragmentVariable.fields.size()) { @@ -1979,52 +2526,125 @@ bool Program::linkValidateVariablesBase(InfoLog &infoLog, const std::string &var return true; } -bool Program::linkValidateUniforms(InfoLog &infoLog, const std::string &uniformName, const sh::Uniform &vertexUniform, const sh::Uniform &fragmentUniform) +bool Program::linkValidateVaryings(InfoLog &infoLog, + const std::string &varyingName, + const sh::Varying &vertexVarying, + const sh::Varying &fragmentVarying, + int shaderVersion) { -#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED - const bool validatePrecision = true; -#else - const bool validatePrecision = false; -#endif + if (!linkValidateVariablesBase(infoLog, varyingName, vertexVarying, fragmentVarying, false)) + { + return false; + } + + if (!sh::InterpolationTypesMatch(vertexVarying.interpolation, fragmentVarying.interpolation)) + { + infoLog << "Interpolation types for " << varyingName + << " differ between vertex and fragment shaders."; + return false; + } - if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, validatePrecision)) + if (shaderVersion == 100 && vertexVarying.isInvariant != fragmentVarying.isInvariant) { + infoLog << "Invariance for " << varyingName + << " differs between vertex and fragment shaders."; return false; } return true; } -bool Program::linkValidateVaryings(InfoLog &infoLog, const std::string &varyingName, const sh::Varying &vertexVarying, const sh::Varying &fragmentVarying) +bool Program::linkValidateBuiltInVaryings(const Context *context, InfoLog &infoLog) const { - if (!linkValidateVariablesBase(infoLog, varyingName, vertexVarying, fragmentVarying, false)) + Shader *vertexShader = mState.mAttachedVertexShader; + Shader *fragmentShader = mState.mAttachedFragmentShader; + const auto &vertexVaryings = vertexShader->getOutputVaryings(context); + const auto &fragmentVaryings = fragmentShader->getInputVaryings(context); + int shaderVersion = vertexShader->getShaderVersion(context); + + if (shaderVersion != 100) { - return false; + // Only ESSL 1.0 has restrictions on matching input and output invariance + return true; } - if (!sh::InterpolationTypesMatch(vertexVarying.interpolation, fragmentVarying.interpolation)) + bool glPositionIsInvariant = false; + bool glPointSizeIsInvariant = false; + bool glFragCoordIsInvariant = false; + bool glPointCoordIsInvariant = false; + + for (const sh::Varying &varying : vertexVaryings) + { + if (!varying.isBuiltIn()) + { + continue; + } + if (varying.name.compare("gl_Position") == 0) + { + glPositionIsInvariant = varying.isInvariant; + } + else if (varying.name.compare("gl_PointSize") == 0) + { + glPointSizeIsInvariant = varying.isInvariant; + } + } + + for (const sh::Varying &varying : fragmentVaryings) + { + if (!varying.isBuiltIn()) + { + continue; + } + if (varying.name.compare("gl_FragCoord") == 0) + { + glFragCoordIsInvariant = varying.isInvariant; + } + else if (varying.name.compare("gl_PointCoord") == 0) + { + glPointCoordIsInvariant = varying.isInvariant; + } + } + + // There is some ambiguity in ESSL 1.00.17 paragraph 4.6.4 interpretation, + // for example, https://cvs.khronos.org/bugzilla/show_bug.cgi?id=13842. + // Not requiring invariance to match is supported by: + // dEQP, WebGL CTS, Nexus 5X GLES + if (glFragCoordIsInvariant && !glPositionIsInvariant) + { + infoLog << "gl_FragCoord can only be declared invariant if and only if gl_Position is " + "declared invariant."; + return false; + } + if (glPointCoordIsInvariant && !glPointSizeIsInvariant) { - infoLog << "Interpolation types for " << varyingName << " differ between vertex and fragment shaders"; + infoLog << "gl_PointCoord can only be declared invariant if and only if gl_PointSize is " + "declared invariant."; return false; } return true; } -bool Program::linkValidateTransformFeedback(InfoLog &infoLog, - const std::vector<const sh::Varying *> &varyings, +bool Program::linkValidateTransformFeedback(const gl::Context *context, + InfoLog &infoLog, + const Program::MergedVaryings &varyings, const Caps &caps) const { size_t totalComponents = 0; std::set<std::string> uniqueNames; - for (const std::string &tfVaryingName : mData.mTransformFeedbackVaryingNames) + for (const std::string &tfVaryingName : mState.mTransformFeedbackVaryingNames) { bool found = false; - for (const sh::Varying *varying : varyings) + std::vector<unsigned int> subscripts; + std::string baseName = ParseResourceName(tfVaryingName, &subscripts); + + for (const auto &ref : varyings) { - if (tfVaryingName == varying->name) + const sh::Varying *varying = ref.second.get(); + + if (baseName == varying->name) { if (uniqueNames.count(tfVaryingName) > 0) { @@ -2032,17 +2652,33 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog, << tfVaryingName << ")."; return false; } - uniqueNames.insert(tfVaryingName); - - if (varying->isArray()) + if (context->getClientVersion() >= Version(3, 1)) + { + if (IncludeSameArrayElement(uniqueNames, tfVaryingName)) + { + infoLog + << "Two transform feedback varyings include the same array element (" + << tfVaryingName << ")."; + return false; + } + } + else if (varying->isArray()) { infoLog << "Capture of arrays is undefined and not supported."; return false; } + uniqueNames.insert(tfVaryingName); + // TODO(jmadill): Investigate implementation limits on D3D11 - size_t componentCount = gl::VariableComponentCount(varying->type); - if (mData.mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS && + + // GLSL ES 3.10 section 4.3.6: A vertex output can't be an array of arrays. + ASSERT(!varying->isArrayOfArrays()); + size_t elementCount = + ((varying->isArray() && subscripts.empty()) ? varying->getOutermostArraySize() + : 1); + size_t componentCount = VariableComponentCount(varying->type) * elementCount; + if (mState.mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS && componentCount > caps.maxTransformFeedbackSeparateComponents) { infoLog << "Transform feedback varying's " << varying->name << " components (" @@ -2056,19 +2692,21 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog, break; } } - - if (tfVaryingName.find('[') != std::string::npos) + if (context->getClientVersion() < Version(3, 1) && + tfVaryingName.find('[') != std::string::npos) { infoLog << "Capture of array elements is undefined and not supported."; return false; } - - // All transform feedback varyings are expected to exist since packVaryings checks for them. - ASSERT(found); - UNUSED_ASSERTION_VARIABLE(found); + if (!found) + { + infoLog << "Transform feedback varying " << tfVaryingName + << " does not exist in the vertex shader."; + return false; + } } - if (mData.mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS && + if (mState.mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS && totalComponents > caps.maxTransformFeedbackInterleavedComponents) { infoLog << "Transform feedback varying total components (" << totalComponents @@ -2080,454 +2718,349 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog, return true; } -void Program::gatherTransformFeedbackVaryings(const std::vector<const sh::Varying *> &varyings) +bool Program::linkValidateGlobalNames(const Context *context, InfoLog &infoLog) const { - // Gather the linked varyings that are used for transform feedback, they should all exist. - mData.mTransformFeedbackVaryingVars.clear(); - for (const std::string &tfVaryingName : mData.mTransformFeedbackVaryingNames) + const std::vector<sh::Uniform> &vertexUniforms = + mState.mAttachedVertexShader->getUniforms(context); + const std::vector<sh::Uniform> &fragmentUniforms = + mState.mAttachedFragmentShader->getUniforms(context); + const std::vector<sh::Attribute> &attributes = + mState.mAttachedVertexShader->getActiveAttributes(context); + for (const auto &attrib : attributes) { - for (const sh::Varying *varying : varyings) + for (const auto &uniform : vertexUniforms) { - if (tfVaryingName == varying->name) + if (uniform.name == attrib.name) { - mData.mTransformFeedbackVaryingVars.push_back(*varying); - break; + infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name; + return false; + } + } + for (const auto &uniform : fragmentUniforms) + { + if (uniform.name == attrib.name) + { + infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name; + return false; } } } + return true; } -std::vector<const sh::Varying *> Program::getMergedVaryings() const +void Program::gatherTransformFeedbackVaryings(const Program::MergedVaryings &varyings) { - std::set<std::string> uniqueNames; - std::vector<const sh::Varying *> varyings; - - for (const sh::Varying &varying : mData.mAttachedVertexShader->getVaryings()) + // Gather the linked varyings that are used for transform feedback, they should all exist. + mState.mLinkedTransformFeedbackVaryings.clear(); + for (const std::string &tfVaryingName : mState.mTransformFeedbackVaryingNames) { - if (uniqueNames.count(varying.name) == 0) + std::vector<unsigned int> subscripts; + std::string baseName = ParseResourceName(tfVaryingName, &subscripts); + size_t subscript = GL_INVALID_INDEX; + if (!subscripts.empty()) { - uniqueNames.insert(varying.name); - varyings.push_back(&varying); + subscript = subscripts.back(); } - } - - for (const sh::Varying &varying : mData.mAttachedFragmentShader->getVaryings()) - { - if (uniqueNames.count(varying.name) == 0) + for (const auto &ref : varyings) { - uniqueNames.insert(varying.name); - varyings.push_back(&varying); + const sh::Varying *varying = ref.second.get(); + if (baseName == varying->name) + { + mState.mLinkedTransformFeedbackVaryings.emplace_back( + *varying, static_cast<GLuint>(subscript)); + break; + } } } - - return varyings; } -void Program::linkOutputVariables() +Program::MergedVaryings Program::getMergedVaryings(const Context *context) const { - const Shader *fragmentShader = mData.mAttachedFragmentShader; - ASSERT(fragmentShader != nullptr); - - // Skip this step for GLES2 shaders. - if (fragmentShader->getShaderVersion() == 100) - return; - - const auto &shaderOutputVars = fragmentShader->getActiveOutputVariables(); - - // TODO(jmadill): any caps validation here? + MergedVaryings merged; - for (unsigned int outputVariableIndex = 0; outputVariableIndex < shaderOutputVars.size(); - outputVariableIndex++) + for (const sh::Varying &varying : mState.mAttachedVertexShader->getOutputVaryings(context)) { - const sh::OutputVariable &outputVariable = shaderOutputVars[outputVariableIndex]; - - // Don't store outputs for gl_FragDepth, gl_FragColor, etc. - if (outputVariable.isBuiltIn()) - continue; - - // Since multiple output locations must be specified, use 0 for non-specified locations. - int baseLocation = (outputVariable.location == -1 ? 0 : outputVariable.location); - - ASSERT(outputVariable.staticUse); - - for (unsigned int elementIndex = 0; elementIndex < outputVariable.elementCount(); - elementIndex++) - { - const int location = baseLocation + elementIndex; - ASSERT(mData.mOutputVariables.count(location) == 0); - unsigned int element = outputVariable.isArray() ? elementIndex : GL_INVALID_INDEX; - mData.mOutputVariables[location] = - VariableLocation(outputVariable.name, element, outputVariableIndex); - } + merged[varying.name].vertex = &varying; } -} - -bool Program::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog) -{ - const gl::Shader *vertexShader = mData.getAttachedVertexShader(); - VectorAndSamplerCount vsCounts; - std::vector<LinkedUniform> samplerUniforms; - - for (const sh::Uniform &uniform : vertexShader->getUniforms()) + for (const sh::Varying &varying : mState.mAttachedFragmentShader->getInputVaryings(context)) { - if (uniform.staticUse) - { - vsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms); - } + merged[varying.name].fragment = &varying; } - if (vsCounts.vectorCount > caps.maxVertexUniformVectors) - { - infoLog << "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (" - << caps.maxVertexUniformVectors << ")."; - return false; - } + return merged; +} - if (vsCounts.samplerCount > caps.maxVertexTextureImageUnits) - { - infoLog << "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (" - << caps.maxVertexTextureImageUnits << ")."; - return false; - } - const gl::Shader *fragmentShader = mData.getAttachedFragmentShader(); - VectorAndSamplerCount fsCounts; +void Program::linkOutputVariables(const Context *context) +{ + Shader *fragmentShader = mState.mAttachedFragmentShader; + ASSERT(fragmentShader != nullptr); + + ASSERT(mState.mOutputVariableTypes.empty()); + ASSERT(mState.mActiveOutputVariables.none()); - for (const sh::Uniform &uniform : fragmentShader->getUniforms()) + // Gather output variable types + for (const auto &outputVariable : fragmentShader->getActiveOutputVariables(context)) { - if (uniform.staticUse) + if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" && + outputVariable.name != "gl_FragData") { - fsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms); + continue; } - } - - if (fsCounts.vectorCount > caps.maxFragmentUniformVectors) - { - infoLog << "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (" - << caps.maxFragmentUniformVectors << ")."; - return false; - } - if (fsCounts.samplerCount > caps.maxTextureImageUnits) - { - infoLog << "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (" - << caps.maxTextureImageUnits << ")."; - return false; - } - - mSamplerUniformRange.start = static_cast<unsigned int>(mData.mUniforms.size()); - mSamplerUniformRange.end = - mSamplerUniformRange.start + static_cast<unsigned int>(samplerUniforms.size()); - - mData.mUniforms.insert(mData.mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end()); - - return true; -} - -Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable &uniform, - const std::string &fullName, - std::vector<LinkedUniform> *samplerUniforms) -{ - VectorAndSamplerCount vectorAndSamplerCount; + unsigned int baseLocation = + (outputVariable.location == -1 ? 0u + : static_cast<unsigned int>(outputVariable.location)); - if (uniform.isStruct()) - { - for (unsigned int elementIndex = 0; elementIndex < uniform.elementCount(); elementIndex++) + // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of + // structures, so we may use getBasicTypeElementCount(). + unsigned int elementCount = outputVariable.getBasicTypeElementCount(); + for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++) { - const std::string &elementString = (uniform.isArray() ? ArrayString(elementIndex) : ""); - - for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++) + const unsigned int location = baseLocation + elementIndex; + if (location >= mState.mOutputVariableTypes.size()) { - const sh::ShaderVariable &field = uniform.fields[fieldIndex]; - const std::string &fieldFullName = (fullName + elementString + "." + field.name); - - vectorAndSamplerCount += flattenUniform(field, fieldFullName, samplerUniforms); + mState.mOutputVariableTypes.resize(location + 1, GL_NONE); } + ASSERT(location < mState.mActiveOutputVariables.size()); + mState.mActiveOutputVariables.set(location); + mState.mOutputVariableTypes[location] = VariableComponentType(outputVariable.type); } - - return vectorAndSamplerCount; } - // Not a struct - bool isSampler = IsSamplerType(uniform.type); - if (!UniformInList(mData.getUniforms(), fullName) && !UniformInList(*samplerUniforms, fullName)) + // Skip this step for GLES2 shaders. + if (fragmentShader->getShaderVersion(context) == 100) + return; + + mState.mOutputVariables = fragmentShader->getActiveOutputVariables(context); + // TODO(jmadill): any caps validation here? + + for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size(); + outputVariableIndex++) { - gl::LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName, - uniform.arraySize, -1, - sh::BlockMemberInfo::getDefaultBlockInfo()); - linkedUniform.staticUse = true; + const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex]; - // Store sampler uniforms separately, so we'll append them to the end of the list. - if (isSampler) + if (outputVariable.isArray()) { - samplerUniforms->push_back(linkedUniform); + // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active + // Resources and including [0] at the end of array variable names. + mState.mOutputVariables[outputVariableIndex].name += "[0]"; + mState.mOutputVariables[outputVariableIndex].mappedName += "[0]"; } - else - { - mData.mUniforms.push_back(linkedUniform); - } - } - unsigned int elementCount = uniform.elementCount(); - - // Samplers aren't "real" uniforms, so they don't count towards register usage. - // Likewise, don't count "real" uniforms towards sampler count. - vectorAndSamplerCount.vectorCount = - (isSampler ? 0 : (VariableRegisterCount(uniform.type) * elementCount)); - vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0); - - return vectorAndSamplerCount; -} - -void Program::gatherInterfaceBlockInfo() -{ - std::set<std::string> visitedList; - - const gl::Shader *vertexShader = mData.getAttachedVertexShader(); - - ASSERT(mData.mUniformBlocks.empty()); - for (const sh::InterfaceBlock &vertexBlock : vertexShader->getInterfaceBlocks()) - { - // Only 'packed' blocks are allowed to be considered inacive. - if (!vertexBlock.staticUse && vertexBlock.layout == sh::BLOCKLAYOUT_PACKED) - continue; - - if (visitedList.count(vertexBlock.name) > 0) + // Don't store outputs for gl_FragDepth, gl_FragColor, etc. + if (outputVariable.isBuiltIn()) continue; - defineUniformBlock(vertexBlock, GL_VERTEX_SHADER); - visitedList.insert(vertexBlock.name); - } - - const gl::Shader *fragmentShader = mData.getAttachedFragmentShader(); - - for (const sh::InterfaceBlock &fragmentBlock : fragmentShader->getInterfaceBlocks()) - { - // Only 'packed' blocks are allowed to be considered inacive. - if (!fragmentBlock.staticUse && fragmentBlock.layout == sh::BLOCKLAYOUT_PACKED) - continue; + // Since multiple output locations must be specified, use 0 for non-specified locations. + unsigned int baseLocation = + (outputVariable.location == -1 ? 0u + : static_cast<unsigned int>(outputVariable.location)); - if (visitedList.count(fragmentBlock.name) > 0) + // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of + // structures, so we may use getBasicTypeElementCount(). + unsigned int elementCount = outputVariable.getBasicTypeElementCount(); + for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++) { - for (gl::UniformBlock &block : mData.mUniformBlocks) + const unsigned int location = baseLocation + elementIndex; + if (location >= mState.mOutputLocations.size()) { - if (block.name == fragmentBlock.name) - { - block.fragmentStaticUse = fragmentBlock.staticUse; - } + mState.mOutputLocations.resize(location + 1); + } + ASSERT(!mState.mOutputLocations.at(location).used()); + if (outputVariable.isArray()) + { + mState.mOutputLocations[location] = + VariableLocation(elementIndex, outputVariableIndex); + } + else + { + VariableLocation locationInfo; + locationInfo.index = outputVariableIndex; + mState.mOutputLocations[location] = locationInfo; } - - continue; } - - defineUniformBlock(fragmentBlock, GL_FRAGMENT_SHADER); - visitedList.insert(fragmentBlock.name); } } -template <typename VarT> -void Program::defineUniformBlockMembers(const std::vector<VarT> &fields, - const std::string &prefix, - int blockIndex) +void Program::setUniformValuesFromBindingQualifiers() { - for (const VarT &field : fields) + for (unsigned int samplerIndex : mState.mSamplerUniformRange) { - const std::string &fullName = (prefix.empty() ? field.name : prefix + "." + field.name); - - if (field.isStruct()) - { - for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++) - { - const std::string uniformElementName = - fullName + (field.isArray() ? ArrayString(arrayElement) : ""); - defineUniformBlockMembers(field.fields, uniformElementName, blockIndex); - } - } - else + const auto &samplerUniform = mState.mUniforms[samplerIndex]; + if (samplerUniform.binding != -1) { - // If getBlockMemberInfo returns false, the uniform is optimized out. - sh::BlockMemberInfo memberInfo; - if (!mProgram->getUniformBlockMemberInfo(fullName, &memberInfo)) + GLint location = getUniformLocation(samplerUniform.name); + ASSERT(location != -1); + std::vector<GLint> boundTextureUnits; + for (unsigned int elementIndex = 0; + elementIndex < samplerUniform.getBasicTypeElementCount(); ++elementIndex) { - continue; + boundTextureUnits.push_back(samplerUniform.binding + elementIndex); } - - LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize, - blockIndex, memberInfo); - - // Since block uniforms have no location, we don't need to store them in the uniform - // locations list. - mData.mUniforms.push_back(newUniform); + setUniform1iv(location, static_cast<GLsizei>(boundTextureUnits.size()), + boundTextureUnits.data()); } } } -void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType) +void Program::gatherAtomicCounterBuffers() { - int blockIndex = static_cast<int>(mData.mUniformBlocks.size()); - size_t blockSize = 0; - - // Don't define this block at all if it's not active in the implementation. - if (!mProgram->getUniformBlockSize(interfaceBlock.name, &blockSize)) + for (unsigned int index : mState.mAtomicCounterUniformRange) { - return; + auto &uniform = mState.mUniforms[index]; + uniform.blockInfo.offset = uniform.offset; + uniform.blockInfo.arrayStride = (uniform.isArray() ? 4 : 0); + uniform.blockInfo.matrixStride = 0; + uniform.blockInfo.isRowMajorMatrix = false; } - // Track the first and last uniform index to determine the range of active uniforms in the - // block. - size_t firstBlockUniformIndex = mData.mUniforms.size(); - defineUniformBlockMembers(interfaceBlock.fields, interfaceBlock.fieldPrefix(), blockIndex); - size_t lastBlockUniformIndex = mData.mUniforms.size(); + // TODO(jie.a.chen@intel.com): Get the actual BUFFER_DATA_SIZE from backend for each buffer. +} - std::vector<unsigned int> blockUniformIndexes; - for (size_t blockUniformIndex = firstBlockUniformIndex; - blockUniformIndex < lastBlockUniformIndex; ++blockUniformIndex) +void Program::initInterfaceBlockBindings() +{ + // Set initial bindings from shader. + for (unsigned int blockIndex = 0; blockIndex < mState.mUniformBlocks.size(); blockIndex++) { - blockUniformIndexes.push_back(static_cast<unsigned int>(blockUniformIndex)); + InterfaceBlock &uniformBlock = mState.mUniformBlocks[blockIndex]; + bindUniformBlock(blockIndex, uniformBlock.binding); } +} - if (interfaceBlock.arraySize > 0) - { - for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.arraySize; ++arrayElement) - { - UniformBlock block(interfaceBlock.name, true, arrayElement); - block.memberUniformIndexes = blockUniformIndexes; - - if (shaderType == GL_VERTEX_SHADER) - { - block.vertexStaticUse = interfaceBlock.staticUse; - } - else - { - ASSERT(shaderType == GL_FRAGMENT_SHADER); - block.fragmentStaticUse = interfaceBlock.staticUse; - } - - // TODO(jmadill): Determine if we can ever have an inactive array element block. - size_t blockElementSize = 0; - if (!mProgram->getUniformBlockSize(block.nameWithArrayIndex(), &blockElementSize)) - { - continue; - } - - ASSERT(blockElementSize == blockSize); - block.dataSize = static_cast<unsigned int>(blockElementSize); - mData.mUniformBlocks.push_back(block); - } - } - else - { - UniformBlock block(interfaceBlock.name, false, 0); - block.memberUniformIndexes = blockUniformIndexes; +void Program::updateSamplerUniform(const VariableLocation &locationInfo, + GLsizei clampedCount, + const GLint *v) +{ + ASSERT(mState.isSamplerUniformIndex(locationInfo.index)); + GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(locationInfo.index); + std::vector<GLuint> *boundTextureUnits = + &mState.mSamplerBindings[samplerIndex].boundTextureUnits; - if (shaderType == GL_VERTEX_SHADER) - { - block.vertexStaticUse = interfaceBlock.staticUse; - } - else - { - ASSERT(shaderType == GL_FRAGMENT_SHADER); - block.fragmentStaticUse = interfaceBlock.staticUse; - } + std::copy(v, v + clampedCount, boundTextureUnits->begin() + locationInfo.arrayIndex); - block.dataSize = static_cast<unsigned int>(blockSize); - mData.mUniformBlocks.push_back(block); - } + // Invalidate the validation cache. + mCachedValidateSamplersResult.reset(); } template <typename T> -void Program::setUniformInternal(GLint location, GLsizei count, const T *v) +GLsizei Program::clampUniformCount(const VariableLocation &locationInfo, + GLsizei count, + int vectorSize, + const T *v) { - const VariableLocation &locationInfo = mData.mUniformLocations[location]; - LinkedUniform *linkedUniform = &mData.mUniforms[locationInfo.index]; - uint8_t *destPointer = linkedUniform->getDataPtrToElement(locationInfo.element); + if (count == 1) + return 1; + + const LinkedUniform &linkedUniform = mState.mUniforms[locationInfo.index]; - if (VariableComponentType(linkedUniform->type) == GL_BOOL) + // 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) { - // Do a cast conversion for boolean types. From the spec: - // "The uniform is set to FALSE if the input value is 0 or 0.0f, and set to TRUE otherwise." - GLint *destAsInt = reinterpret_cast<GLint *>(destPointer); - for (GLsizei component = 0; component < count; ++component) - { - destAsInt[component] = (v[component] != static_cast<T>(0) ? GL_TRUE : GL_FALSE); - } + return maxElementCount / vectorSize; } - else - { - // Invalide the validation cache if we modify the sampler data. - if (linkedUniform->isSampler() && memcmp(destPointer, v, sizeof(T) * count) != 0) - { - mCachedValidateSamplersResult.reset(); - } - memcpy(destPointer, v, sizeof(T) * count); - } + return count; } template <size_t cols, size_t rows, typename T> -void Program::setMatrixUniformInternal(GLint location, - GLsizei count, - GLboolean transpose, - const T *v) +GLsizei Program::clampMatrixUniformCount(GLint location, + GLsizei count, + GLboolean transpose, + const T *v) { + const VariableLocation &locationInfo = mState.mUniformLocations[location]; + if (!transpose) { - setUniformInternal(location, count * cols * rows, v); - return; + return clampUniformCount(locationInfo, count, cols * rows, v); } - // Perform a transposing copy. - const VariableLocation &locationInfo = mData.mUniformLocations[location]; - LinkedUniform *linkedUniform = &mData.mUniforms[locationInfo.index]; - T *destPtr = reinterpret_cast<T *>(linkedUniform->getDataPtrToElement(locationInfo.element)); - for (GLsizei element = 0; element < count; ++element) - { - size_t elementOffset = element * rows * cols; + const LinkedUniform &linkedUniform = mState.mUniforms[locationInfo.index]; - for (size_t row = 0; row < rows; ++row) - { - for (size_t col = 0; col < cols; ++col) - { - destPtr[col * rows + row + elementOffset] = v[row * cols + col + elementOffset]; - } - } - } + // 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)); } +// Driver differences mean that doing the uniform value cast ourselves gives consistent results. +// EG: on NVIDIA drivers, it was observed that getUniformi for MAX_INT+1 returned MIN_INT. template <typename DestT> -void Program::getUniformInternal(GLint location, DestT *dataOut) const +void Program::getUniformInternal(const Context *context, + DestT *dataOut, + GLint location, + GLenum nativeType, + int components) const { - const VariableLocation &locationInfo = mData.mUniformLocations[location]; - const LinkedUniform &uniform = mData.mUniforms[locationInfo.index]; - - const uint8_t *srcPointer = uniform.getDataPtrToElement(locationInfo.element); - - GLenum componentType = VariableComponentType(uniform.type); - if (componentType == GLTypeToGLenum<DestT>::value) - { - memcpy(dataOut, srcPointer, uniform.getElementSize()); - return; - } - - int components = VariableComponentCount(uniform.type); - - switch (componentType) + switch (nativeType) { + case GL_BOOL: + { + GLint tempValue[16] = {0}; + mProgram->getUniformiv(context, location, tempValue); + UniformStateQueryCastLoop<GLboolean>( + dataOut, reinterpret_cast<const uint8_t *>(tempValue), components); + break; + } case GL_INT: - UniformStateQueryCastLoop<GLint>(dataOut, srcPointer, components); + { + GLint tempValue[16] = {0}; + mProgram->getUniformiv(context, location, tempValue); + UniformStateQueryCastLoop<GLint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue), + components); break; + } case GL_UNSIGNED_INT: - UniformStateQueryCastLoop<GLuint>(dataOut, srcPointer, components); - break; - case GL_BOOL: - UniformStateQueryCastLoop<GLboolean>(dataOut, srcPointer, components); + { + GLuint tempValue[16] = {0}; + mProgram->getUniformuiv(context, location, tempValue); + UniformStateQueryCastLoop<GLuint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue), + components); break; + } case GL_FLOAT: - UniformStateQueryCastLoop<GLfloat>(dataOut, srcPointer, components); + { + GLfloat tempValue[16] = {0}; + mProgram->getUniformfv(context, location, tempValue); + UniformStateQueryCastLoop<GLfloat>( + dataOut, reinterpret_cast<const uint8_t *>(tempValue), components); break; + } default: UNREACHABLE(); + break; } } + +bool Program::samplesFromTexture(const gl::State &state, GLuint textureID) const +{ + // Must be called after samplers are validated. + ASSERT(mCachedValidateSamplersResult.valid() && mCachedValidateSamplersResult.value()); + + for (const auto &binding : mState.mSamplerBindings) + { + GLenum textureType = binding.textureType; + for (const auto &unit : binding.boundTextureUnits) + { + GLenum programTextureID = state.getSamplerTextureId(unit, textureType); + if (programTextureID == textureID) + { + // TODO(jmadill): Check for appropriate overlap. + return true; + } + } + } + + return false; } + +} // namespace gl |