Improve speed of iterating dirty textures.
We had a performance regression in the Textures benchmark. What the
test was doing was iterating over all possible texture state,
ensuring the active texture was dirty every frame. This is an attempt
to improve on the speed by not doing as much resetting work in
State::syncProgramTextures. It introduces an active textures mask to
speed iteration over the active texture set.
Also makes a refactoring change to Context to make it easier to limit
caps to an implementation maxium. The number of active textures is
limited to 64 so they easily fit in the bitset mask, with a limit of
32 per shader stage. No mask is currenly kept for compute shaders.
With the fix the performance should be about the same as before (which
is good, as the test always sets the textures dirty).
Test: TexturesBenchmark.Run/gl_8_textures_5_rebind_3_state_8_mips
BUG=chromium:765363
BUG=angleproject:1387
Change-Id: I8bcf95be3671195373573f89f406edaba40aa1be
Reviewed-on: https://chromium-review.googlesource.com/670279
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Constants.h b/src/libANGLE/Constants.h
index b9575a5..cb3ffcd 100644
--- a/src/libANGLE/Constants.h
+++ b/src/libANGLE/Constants.h
@@ -47,7 +47,11 @@
IMPLEMENTATION_MAX_3D_TEXTURE_SIZE = 2048,
IMPLEMENTATION_MAX_2D_ARRAY_TEXTURE_LAYERS = 2048,
- IMPLEMENTATION_MAX_TEXTURE_LEVELS = 15 // 1+log2 of MAX_TEXTURE_SIZE
+ // 1+log2 of max of MAX_*_TEXTURE_SIZE
+ IMPLEMENTATION_MAX_TEXTURE_LEVELS = 15,
+
+ // Limit active textures so we can use fast bitsets.
+ IMPLEMENTATION_MAX_ACTIVE_TEXTURES = 64,
};
}
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index a0955d2..74ff875 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -240,6 +240,12 @@
}
}
+template <typename CapT, typename MaxT>
+void LimitCap(CapT *cap, MaxT maximum)
+{
+ *cap = std::min(*cap, static_cast<CapT>(maximum));
+}
+
} // anonymous namespace
namespace gl
@@ -2672,19 +2678,25 @@
mExtensions.programCacheControl = true;
// Apply implementation limits
- mCaps.maxVertexAttributes = std::min<GLuint>(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS);
- mCaps.maxVertexAttribBindings =
- getClientVersion() < ES_3_1
- ? mCaps.maxVertexAttributes
- : std::min<GLuint>(mCaps.maxVertexAttribBindings, MAX_VERTEX_ATTRIB_BINDINGS);
+ LimitCap(&mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS);
- mCaps.maxVertexUniformBlocks = std::min<GLuint>(
- mCaps.maxVertexUniformBlocks, IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS);
- mCaps.maxVertexOutputComponents =
- std::min<GLuint>(mCaps.maxVertexOutputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
+ if (getClientVersion() < ES_3_1)
+ {
+ mCaps.maxVertexAttribBindings = mCaps.maxVertexAttributes;
+ }
+ else
+ {
+ LimitCap(&mCaps.maxVertexAttribBindings, MAX_VERTEX_ATTRIB_BINDINGS);
+ }
- mCaps.maxFragmentInputComponents =
- std::min<GLuint>(mCaps.maxFragmentInputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
+ LimitCap(&mCaps.maxVertexUniformBlocks, IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS);
+ LimitCap(&mCaps.maxVertexOutputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
+ LimitCap(&mCaps.maxFragmentInputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
+
+ // Limit textures as well, so we can use fast bitsets with texture bindings.
+ LimitCap(&mCaps.maxCombinedTextureImageUnits, IMPLEMENTATION_MAX_ACTIVE_TEXTURES);
+ LimitCap(&mCaps.maxVertexTextureImageUnits, IMPLEMENTATION_MAX_ACTIVE_TEXTURES / 2);
+ LimitCap(&mCaps.maxTextureImageUnits, IMPLEMENTATION_MAX_ACTIVE_TEXTURES / 2);
// WebGL compatibility
mExtensions.webglCompatibility = mWebGLContext;
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index c630a13..dcf65c3 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -2206,12 +2206,6 @@
void State::syncProgramTextures(const Context *context)
{
- std::fill(mCompleteTextureCache.begin(), mCompleteTextureCache.end(), nullptr);
- for (auto &binding : mCompleteTextureBindings)
- {
- binding.reset();
- }
-
// TODO(jmadill): Fine-grained updates.
if (!mProgram)
{
@@ -2221,6 +2215,8 @@
ASSERT(mDirtyObjects[DIRTY_OBJECT_PROGRAM_TEXTURES]);
mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS);
+ ActiveTextureMask newActiveTextures;
+
for (const SamplerBinding &samplerBinding : mProgram->getSamplerBindings())
{
if (samplerBinding.unreferenced)
@@ -2231,6 +2227,8 @@
{
Texture *texture = getSamplerTexture(textureUnitIndex, textureType);
Sampler *sampler = getSampler(textureUnitIndex);
+ ASSERT(static_cast<size_t>(textureUnitIndex) < mCompleteTextureCache.size());
+ ASSERT(static_cast<size_t>(textureUnitIndex) < newActiveTextures.size());
if (texture != nullptr)
{
@@ -2239,14 +2237,17 @@
if (texture->isSamplerComplete(context, sampler))
{
texture->syncState();
- ASSERT(static_cast<size_t>(textureUnitIndex) < mCompleteTextureCache.size());
- ASSERT(mCompleteTextureCache[textureUnitIndex] == nullptr ||
- mCompleteTextureCache[textureUnitIndex] == texture);
mCompleteTextureCache[textureUnitIndex] = texture;
}
+ else
+ {
+ mCompleteTextureCache[textureUnitIndex] = nullptr;
+ }
// Bind the texture unconditionally, to recieve completeness change notifications.
mCompleteTextureBindings[textureUnitIndex].bind(texture->getDirtyChannel());
+ newActiveTextures.set(textureUnitIndex);
+ mCompleteTexturesMask.set(textureUnitIndex);
}
if (sampler != nullptr)
@@ -2255,6 +2256,18 @@
}
}
}
+
+ // Unset now missing textures.
+ ActiveTextureMask negativeMask = mCompleteTexturesMask & ~newActiveTextures;
+ if (negativeMask.any())
+ {
+ for (auto textureIndex : negativeMask)
+ {
+ mCompleteTextureBindings[textureIndex].reset();
+ mCompleteTextureCache[textureIndex] = nullptr;
+ mCompleteTexturesMask.reset(textureIndex);
+ }
+ }
}
void State::syncDirtyObject(const Context *context, GLenum target)
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 1a82def..0aa368e 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -559,6 +559,8 @@
// Also stores a notification channel to the texture itself to handle texture change events.
std::vector<Texture *> mCompleteTextureCache;
std::vector<OnAttachmentDirtyBinding> mCompleteTextureBindings;
+ using ActiveTextureMask = angle::BitSet<IMPLEMENTATION_MAX_ACTIVE_TEXTURES>;
+ ActiveTextureMask mCompleteTexturesMask;
typedef std::vector<BindingPointer<Sampler>> SamplerBindingVector;
SamplerBindingVector mSamplers;