Program: Add cache samplers and type masks.
This mask cleans up some of the iteration logic in
State::syncProgramTextures. It will make it easier to optimize this
function in the future. This will also make it easier to recompute
the sampler type validation.
Leads to a 5% improvement in State::syncProgramTextures.
Bug: angleproject:2747
Bug: angleproject:2763
Change-Id: Ic9a555df843ad23b4c562e6e4a2d425bee58a856
Reviewed-on: https://chromium-review.googlesource.com/1164306
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 15cd8bc..0a2ebe1 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -794,9 +794,11 @@
mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles),
mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip),
mGeometryShaderInvocations(1),
- mGeometryShaderMaxVertices(0)
+ mGeometryShaderMaxVertices(0),
+ mActiveSamplerRefCounts{}
{
mComputeShaderLocalSize.fill(1);
+ mActiveSamplerTypes.fill(TextureType::InvalidEnum);
}
ProgramState::~ProgramState()
@@ -1226,8 +1228,6 @@
initInterfaceBlockBindings();
- setUniformValuesFromBindingQualifiers();
-
// According to GLES 3.0/3.1 spec for LinkProgram and UseProgram,
// Only successfully linked program can replace the executables.
ASSERT(mLinked);
@@ -1236,6 +1236,11 @@
// Mark implementation-specific unreferenced uniforms as ignored.
mProgram->markUnusedUniformLocations(&mState.mUniformLocations, &mState.mSamplerBindings);
+ // Must be called after markUnusedUniformLocations.
+ mState.updateActiveSamplers();
+
+ setUniformValuesFromBindingQualifiers();
+
// Save to the program cache.
if (cache && (mState.mLinkedTransformFeedbackVaryings.empty() ||
!context->getWorkarounds().disableProgramCachingForTransformFeedback))
@@ -1287,6 +1292,22 @@
}
}
+void ProgramState::updateActiveSamplers()
+{
+ for (SamplerBinding &samplerBinding : mSamplerBindings)
+ {
+ if (samplerBinding.unreferenced)
+ continue;
+
+ for (GLint textureUnit : samplerBinding.boundTextureUnits)
+ {
+ mActiveSamplerRefCounts[textureUnit]++;
+ mActiveSamplerTypes[textureUnit] = getSamplerUniformTextureType(textureUnit);
+ mActiveSamplersMask.set(textureUnit);
+ }
+ }
+}
+
// Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink()
{
@@ -2046,60 +2067,22 @@
bool Program::validateSamplersImpl(InfoLog *infoLog, const Caps &caps)
{
- if (mTextureUnitTypesCache.empty())
- {
- mTextureUnitTypesCache.resize(caps.maxCombinedTextureImageUnits, TextureType::InvalidEnum);
- }
- else
- {
- std::fill(mTextureUnitTypesCache.begin(), mTextureUnitTypesCache.end(),
- TextureType::InvalidEnum);
- }
-
// 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 (const auto &samplerBinding : mState.mSamplerBindings)
+ for (size_t textureUnit : mState.mActiveSamplersMask)
{
- if (samplerBinding.unreferenced)
- continue;
-
- TextureType textureType = samplerBinding.textureType;
-
- for (GLuint textureUnit : samplerBinding.boundTextureUnits)
+ if (mState.mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum)
{
- if (textureUnit >= caps.maxCombinedTextureImageUnits)
+ if (infoLog)
{
- if (infoLog)
- {
- (*infoLog) << "Sampler uniform (" << textureUnit
- << ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
- << caps.maxCombinedTextureImageUnits << ")";
- }
-
- mCachedValidateSamplersResult = false;
- return false;
+ (*infoLog) << "Samplers of conflicting types refer to the same texture "
+ "image unit ("
+ << textureUnit << ").";
}
- if (mTextureUnitTypesCache[textureUnit] != TextureType::InvalidEnum)
- {
- 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 = false;
+ return false;
}
}
@@ -2651,8 +2634,8 @@
{
const auto &samplerUniform = mState.mUniforms[samplerIndex];
TextureType textureType = SamplerTypeToTextureType(samplerUniform.type);
- mState.mSamplerBindings.emplace_back(
- SamplerBinding(textureType, samplerUniform.getBasicTypeElementCount(), false));
+ unsigned int elementCount = samplerUniform.getBasicTypeElementCount();
+ mState.mSamplerBindings.emplace_back(textureType, elementCount, false);
}
}
@@ -3469,15 +3452,93 @@
{
ASSERT(mState.isSamplerUniformIndex(locationInfo.index));
GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(locationInfo.index);
- std::vector<GLuint> *boundTextureUnits =
- &mState.mSamplerBindings[samplerIndex].boundTextureUnits;
+ SamplerBinding &samplerBinding = mState.mSamplerBindings[samplerIndex];
+ std::vector<GLuint> &boundTextureUnits = samplerBinding.boundTextureUnits;
- std::copy(v, v + clampedCount, boundTextureUnits->begin() + locationInfo.arrayIndex);
+ if (samplerBinding.unreferenced)
+ return;
+
+ // Update the sampler uniforms.
+ for (GLsizei arrayIndex = 0; arrayIndex < clampedCount; ++arrayIndex)
+ {
+ GLint oldSamplerIndex = boundTextureUnits[arrayIndex + locationInfo.arrayIndex];
+ GLint newSamplerIndex = v[arrayIndex];
+
+ if (oldSamplerIndex == newSamplerIndex)
+ continue;
+
+ boundTextureUnits[arrayIndex + locationInfo.arrayIndex] = newSamplerIndex;
+
+ // Update the reference counts.
+ uint32_t &oldRefCount = mState.mActiveSamplerRefCounts[oldSamplerIndex];
+ uint32_t &newRefCount = mState.mActiveSamplerRefCounts[newSamplerIndex];
+ ASSERT(oldRefCount > 0);
+ ASSERT(newRefCount < std::numeric_limits<uint32_t>::max());
+ oldRefCount--;
+ newRefCount++;
+
+ // Check for binding type change.
+ TextureType &newSamplerType = mState.mActiveSamplerTypes[newSamplerIndex];
+ TextureType &oldSamplerType = mState.mActiveSamplerTypes[oldSamplerIndex];
+
+ if (newRefCount == 1)
+ {
+ newSamplerType = samplerBinding.textureType;
+ mState.mActiveSamplersMask.set(newSamplerIndex);
+ }
+ else if (newSamplerType != samplerBinding.textureType)
+ {
+ // Conflict detected. Ensure we reset it properly.
+ newSamplerType = TextureType::InvalidEnum;
+ }
+
+ // Unset previously active sampler.
+ if (oldRefCount == 0)
+ {
+ oldSamplerType = TextureType::InvalidEnum;
+ mState.mActiveSamplersMask.reset(oldSamplerIndex);
+ }
+ else if (oldSamplerType == TextureType::InvalidEnum)
+ {
+ // Previous conflict. Check if this new change fixed the conflict.
+ oldSamplerType = mState.getSamplerUniformTextureType(oldSamplerIndex);
+ }
+ }
// Invalidate the validation cache.
mCachedValidateSamplersResult.reset();
}
+TextureType ProgramState::getSamplerUniformTextureType(size_t textureUnitIndex) const
+{
+ TextureType foundType = TextureType::InvalidEnum;
+
+ for (const SamplerBinding &binding : mSamplerBindings)
+ {
+ if (binding.unreferenced)
+ continue;
+
+ // A conflict exists if samplers of different types are sourced by the same texture unit.
+ // We need to check all bound textures to detect this error case.
+ for (GLuint textureUnit : binding.boundTextureUnits)
+ {
+ if (textureUnit == textureUnitIndex)
+ {
+ if (foundType == TextureType::InvalidEnum)
+ {
+ foundType = binding.textureType;
+ }
+ else if (foundType != binding.textureType)
+ {
+ return TextureType::InvalidEnum;
+ }
+ }
+ }
+ }
+
+ return foundType;
+}
+
template <typename T>
GLsizei Program::clampUniformCount(const VariableLocation &locationInfo,
GLsizei count,