Re-land "Move sampler validation to the GL layer."
This previously was D3D-only, but is required for every draw call.
This completes the work of removing the D3D-specific Impl methods
from ProgramImpl.
Also add several regression tests to cover texture and sampler
validation.
Re-land with a fix for duplicate sampler active uniforms.
BUG=angleproject:1123
Change-Id: Iefef06e7901873c98bf2ba7864efd16a4c6435d3
Reviewed-on: https://chromium-review.googlesource.com/301581
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 6922adf..14a50ba 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -174,6 +174,17 @@
}
}
+bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &name)
+{
+ for (const LinkedUniform &uniform : list)
+ {
+ if (uniform.name == name)
+ return true;
+ }
+
+ return false;
+}
+
} // anonymous namespace
AttributeBindings::AttributeBindings()
@@ -359,7 +370,8 @@
mDeleteStatus(false),
mRefCount(0),
mResourceManager(manager),
- mHandle(handle)
+ mHandle(handle),
+ mSamplerUniformRange(0, 0)
{
ASSERT(mProgram);
@@ -674,12 +686,15 @@
{
int locationIndex = stream.readInt<int>();
VariableLocation locationData;
- locationData.element = stream.readInt<unsigned int>();
- locationData.index = stream.readInt<unsigned int>();
- locationData.name = stream.readString();
+ stream.readInt(&locationData.element);
+ stream.readInt(&locationData.index);
+ stream.readString(&locationData.name);
mData.mOutputVariables[locationIndex] = locationData;
}
+ stream.readInt(&mSamplerUniformRange.start);
+ stream.readInt(&mSamplerUniformRange.end);
+
rx::LinkResult result = mProgram->load(mInfoLog, &stream);
if (result.error.isError() || !result.linkSuccess)
{
@@ -769,6 +784,9 @@
stream.writeString(outputPair.second.name);
}
+ stream.writeInt(mSamplerUniformRange.start);
+ stream.writeInt(mSamplerUniformRange.end);
+
gl::Error error = mProgram->save(&stream);
if (error.isError())
{
@@ -1297,7 +1315,78 @@
bool Program::validateSamplers(InfoLog *infoLog, const Caps &caps)
{
- return mProgram->validateSamplers(infoLog, caps);
+ // Skip cache if we're using an infolog, so we get the full error.
+ // Also skip the cache if the sample mapping has changed, or if we haven't ever validated.
+ if (infoLog == nullptr && mCachedValidateSamplersResult.valid())
+ {
+ return mCachedValidateSamplersResult.value();
+ }
+
+ if (mTextureUnitTypesCache.empty())
+ {
+ mTextureUnitTypesCache.resize(caps.maxCombinedTextureImageUnits, GL_NONE);
+ }
+ else
+ {
+ std::fill(mTextureUnitTypesCache.begin(), mTextureUnitTypesCache.end(), GL_NONE);
+ }
+
+ // 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)
+ {
+ const LinkedUniform &uniform = mData.mUniforms[samplerIndex];
+ ASSERT(uniform.isSampler());
+
+ if (!uniform.staticUse)
+ continue;
+
+ const GLuint *dataPtr = reinterpret_cast<const GLuint *>(uniform.getDataPtrToElement(0));
+ GLenum textureType = SamplerTypeToTextureType(uniform.type);
+
+ for (unsigned int arrayElement = 0; arrayElement < uniform.elementCount(); ++arrayElement)
+ {
+ GLuint textureUnit = dataPtr[arrayElement];
+
+ if (textureUnit >= caps.maxCombinedTextureImageUnits)
+ {
+ if (infoLog)
+ {
+ (*infoLog) << "Sampler uniform (" << textureUnit
+ << ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
+ << caps.maxCombinedTextureImageUnits << ")";
+ }
+
+ mCachedValidateSamplersResult = false;
+ return false;
+ }
+
+ if (mTextureUnitTypesCache[textureUnit] != GL_NONE)
+ {
+ if (textureType != mTextureUnitTypesCache[textureUnit])
+ {
+ if (infoLog)
+ {
+ (*infoLog) << "Samplers of conflicting types refer to the same texture "
+ "image unit ("
+ << textureUnit << ").";
+ }
+
+ mCachedValidateSamplersResult = false;
+ return false;
+ }
+ }
+ else
+ {
+ mTextureUnitTypesCache[textureUnit] = textureType;
+ }
+ }
+ }
+
+ mCachedValidateSamplersResult = true;
+ return true;
}
bool Program::isValidated() const
@@ -2086,11 +2175,13 @@
const gl::Shader *vertexShader = mData.getAttachedVertexShader();
VectorAndSamplerCount vsCounts;
+ std::vector<LinkedUniform> samplerUniforms;
+
for (const sh::Uniform &uniform : vertexShader->getUniforms())
{
if (uniform.staticUse)
{
- vsCounts += flattenUniform(uniform, uniform.name);
+ vsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
}
}
@@ -2115,7 +2206,7 @@
{
if (uniform.staticUse)
{
- fsCounts += flattenUniform(uniform, uniform.name);
+ fsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
}
}
@@ -2133,11 +2224,18 @@
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)
+ const std::string &fullName,
+ std::vector<LinkedUniform> *samplerUniforms)
{
VectorAndSamplerCount vectorAndSamplerCount;
@@ -2152,7 +2250,7 @@
const sh::ShaderVariable &field = uniform.fields[fieldIndex];
const std::string &fieldFullName = (fullName + elementString + "." + field.name);
- vectorAndSamplerCount += flattenUniform(field, fieldFullName);
+ vectorAndSamplerCount += flattenUniform(field, fieldFullName, samplerUniforms);
}
}
@@ -2160,18 +2258,28 @@
}
// Not a struct
- if (mData.getUniformByName(fullName) == nullptr)
+ bool isSampler = IsSamplerType(uniform.type);
+ if (!UniformInList(mData.getUniforms(), fullName) && !UniformInList(*samplerUniforms, fullName))
{
gl::LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName,
uniform.arraySize, -1,
sh::BlockMemberInfo::getDefaultBlockInfo());
linkedUniform.staticUse = true;
- mData.mUniforms.push_back(linkedUniform);
+
+ // Store sampler uniforms separately, so we'll append them to the end of the list.
+ if (isSampler)
+ {
+ samplerUniforms->push_back(linkedUniform);
+ }
+ else
+ {
+ mData.mUniforms.push_back(linkedUniform);
+ }
}
- vectorAndSamplerCount.vectorCount =
- (VariableRegisterCount(uniform.type) * uniform.elementCount());
- vectorAndSamplerCount.samplerCount = (IsSamplerType(uniform.type) ? uniform.elementCount() : 0);
+ unsigned int elementCount = uniform.elementCount();
+ vectorAndSamplerCount.vectorCount = (VariableRegisterCount(uniform.type) * elementCount);
+ vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0);
return vectorAndSamplerCount;
}
@@ -2294,6 +2402,12 @@
}
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);
}
}