summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/libANGLE/Program.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/angle/src/libANGLE/Program.cpp')
-rw-r--r--src/3rdparty/angle/src/libANGLE/Program.cpp2889
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