Skip Texture::syncState when no dirty bits.

We sometimes generate local dirty bits in TextureGL. To make sure the local
dirty bits don't get skipped we use a Subject/Observer pattern between the
TextureGL and gl::Texture. This allows us to skip syncState in the hot path.

Also inlines a couple of other texture functions. And fixes a stray header
in EGLBlobCacheTest.

Bug: angleproject:2763
Change-Id: Ie1d8a5865deaf2a563a358c31ae28bef6b2458b1
Reviewed-on: https://chromium-review.googlesource.com/1228374
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index 12e805f..f2a7dec 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -1828,7 +1828,12 @@
                                        angle::SubjectIndex index,
                                        angle::SubjectMessage message)
 {
-    ASSERT(message == angle::SubjectMessage::STORAGE_CHANGED);
+    if (message != angle::SubjectMessage::STORAGE_CHANGED)
+    {
+        // This can be triggered by the GL back-end TextureGL class.
+        ASSERT(message == angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
+        return;
+    }
 
     ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(index));
     mDirtyBits.set(index);
diff --git a/src/libANGLE/FramebufferAttachment.cpp b/src/libANGLE/FramebufferAttachment.cpp
index f92cb80..5050911 100644
--- a/src/libANGLE/FramebufferAttachment.cpp
+++ b/src/libANGLE/FramebufferAttachment.cpp
@@ -361,11 +361,6 @@
     return getAttachmentImpl()->onStateChange(context, angle::SubjectMessage::STORAGE_CHANGED);
 }
 
-angle::Subject *FramebufferAttachmentObject::getSubject() const
-{
-    return getAttachmentImpl();
-}
-
 Error FramebufferAttachmentObject::initializeContents(const Context *context,
                                                       const ImageIndex &imageIndex)
 {
diff --git a/src/libANGLE/FramebufferAttachment.h b/src/libANGLE/FramebufferAttachment.h
index 2211df4..d2ef1f0 100644
--- a/src/libANGLE/FramebufferAttachment.h
+++ b/src/libANGLE/FramebufferAttachment.h
@@ -15,6 +15,7 @@
 #include "libANGLE/Error.h"
 #include "libANGLE/ImageIndex.h"
 #include "libANGLE/formatutils.h"
+#include "libANGLE/renderer/FramebufferAttachmentObjectImpl.h"
 
 namespace egl
 {
@@ -220,7 +221,7 @@
     Error initializeContents(const Context *context, const ImageIndex &imageIndex);
 
     void onStorageChange(const gl::Context *context) const;
-    angle::Subject *getSubject() const;
+    angle::Subject *getSubject() const { return getAttachmentImpl(); }
 
   protected:
     virtual rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const = 0;
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index fcd6414..ba5c9a3 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -25,6 +25,7 @@
 #include "libANGLE/queryconversions.h"
 #include "libANGLE/queryutils.h"
 #include "libANGLE/renderer/ContextImpl.h"
+#include "libANGLE/renderer/TextureImpl.h"
 
 namespace gl
 {
@@ -2756,7 +2757,10 @@
         // TODO(jmadill): Use specific dirty bit for completeness change.
         if (texture->isSamplerComplete(context, sampler))
         {
-            ANGLE_TRY(texture->syncState(context));
+            if (texture->hasAnyDirtyBit())
+            {
+                ANGLE_TRY(texture->syncState(context));
+            }
             mActiveTexturesCache[textureUnitIndex] = texture;
         }
         else
@@ -2765,7 +2769,7 @@
         }
 
         // Bind the texture unconditionally, to recieve completeness change notifications.
-        mCompleteTextureBindings[textureUnitIndex].bind(texture->getSubject());
+        mCompleteTextureBindings[textureUnitIndex].bind(texture->getImplementation());
         newActiveTextures.set(textureUnitIndex);
 
         if (texture->initState() == InitState::MayNeedInit)
@@ -2792,7 +2796,10 @@
         {
             continue;
         }
-        ANGLE_TRY(texture->syncState(context));
+        if (texture->hasAnyDirtyBit())
+        {
+            ANGLE_TRY(texture->syncState(context));
+        }
         if (texture->initState() == InitState::MayNeedInit)
         {
             mCachedImageTexturesInitState = InitState::MayNeedInit;
diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp
index ba52eb6..9b450b2 100644
--- a/src/libANGLE/Texture.cpp
+++ b/src/libANGLE/Texture.cpp
@@ -584,10 +584,15 @@
     : RefCountObject(id),
       mState(type),
       mTexture(factory->createTexture(mState)),
+      mImplObserver(this, 0),
       mLabel(),
       mBoundSurface(nullptr),
       mBoundStream(nullptr)
 {
+    mImplObserver.bind(mTexture);
+
+    // Initially assume the implementation is dirty.
+    mDirtyBits.set(DIRTY_BIT_IMPLEMENTATION);
 }
 
 Error Texture::onDestroy(const Context *context)
@@ -1255,7 +1260,11 @@
     {
         return NoError();
     }
-    ANGLE_TRY(syncState(context));
+
+    if (hasAnyDirtyBit())
+    {
+        ANGLE_TRY(syncState(context));
+    }
 
     // Clear the base image(s) immediately if needed
     if (context->isRobustResourceInitEnabled())
@@ -1515,6 +1524,7 @@
 
 Error Texture::syncState(const Context *context)
 {
+    ASSERT(hasAnyDirtyBit());
     ANGLE_TRY(mTexture->syncState(context, mDirtyBits));
     mDirtyBits.reset();
     return NoError();
@@ -1607,11 +1617,6 @@
     return mState.getImageDesc(imageIndex).initState;
 }
 
-InitState Texture::initState() const
-{
-    return mState.mInitState;
-}
-
 void Texture::setInitState(const ImageIndex &imageIndex, InitState initState)
 {
     // As an ImageIndex that represents an entire level of a cube map corresponds to 6 ImageDescs,
@@ -1673,4 +1678,13 @@
     return NoError();
 }
 
+void Texture::onSubjectStateChange(const gl::Context *context,
+                                   angle::SubjectIndex index,
+                                   angle::SubjectMessage message)
+{
+    if (message == angle::SubjectMessage::DEPENDENT_DIRTY_BITS)
+    {
+        mDirtyBits.set(DIRTY_BIT_IMPLEMENTATION);
+    }
+}
 }  // namespace gl
diff --git a/src/libANGLE/Texture.h b/src/libANGLE/Texture.h
index 420ca17..42c108b 100644
--- a/src/libANGLE/Texture.h
+++ b/src/libANGLE/Texture.h
@@ -21,6 +21,7 @@
 #include "libANGLE/Error.h"
 #include "libANGLE/FramebufferAttachment.h"
 #include "libANGLE/Image.h"
+#include "libANGLE/Observer.h"
 #include "libANGLE/Stream.h"
 #include "libANGLE/angletypes.h"
 #include "libANGLE/formatutils.h"
@@ -187,7 +188,10 @@
 bool operator==(const TextureState &a, const TextureState &b);
 bool operator!=(const TextureState &a, const TextureState &b);
 
-class Texture final : public RefCountObject, public egl::ImageSibling, public LabeledObject
+class Texture final : public RefCountObject,
+                      public egl::ImageSibling,
+                      public LabeledObject,
+                      public angle::ObserverInterface
 {
   public:
     Texture(rx::GLImplFactory *factory, GLuint id, TextureType type);
@@ -397,7 +401,7 @@
     // Needed for robust resource init.
     Error ensureInitialized(const Context *context);
     InitState initState(const ImageIndex &imageIndex) const override;
-    InitState initState() const;
+    InitState initState() const { return mState.mInitState; }
     void setInitState(const ImageIndex &imageIndex, InitState initState) override;
 
     enum DirtyBitType
@@ -427,6 +431,7 @@
         // Misc
         DIRTY_BIT_LABEL,
         DIRTY_BIT_USAGE,
+        DIRTY_BIT_IMPLEMENTATION,
 
         DIRTY_BIT_COUNT,
     };
@@ -435,6 +440,11 @@
     Error syncState(const Context *context);
     bool hasAnyDirtyBit() const { return mDirtyBits.any(); }
 
+    // ObserverInterface implementation.
+    void onSubjectStateChange(const gl::Context *context,
+                              angle::SubjectIndex index,
+                              angle::SubjectMessage message) override;
+
   private:
     rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const override;
 
@@ -464,6 +474,7 @@
     TextureState mState;
     DirtyBits mDirtyBits;
     rx::TextureImpl *mTexture;
+    angle::ObserverBinding mImplObserver;
 
     std::string mLabel;
 
diff --git a/src/libANGLE/renderer/FramebufferAttachmentObjectImpl.h b/src/libANGLE/renderer/FramebufferAttachmentObjectImpl.h
index 334e2f2..bd82410 100644
--- a/src/libANGLE/renderer/FramebufferAttachmentObjectImpl.h
+++ b/src/libANGLE/renderer/FramebufferAttachmentObjectImpl.h
@@ -11,11 +11,12 @@
 #ifndef LIBANGLE_RENDERER_FRAMEBUFFER_ATTACHMENT_OBJECT_IMPL_H_
 #define LIBANGLE_RENDERER_FRAMEBUFFER_ATTACHMENT_OBJECT_IMPL_H_
 
-#include "libANGLE/FramebufferAttachment.h"
+#include "libANGLE/ImageIndex.h"
 #include "libANGLE/Observer.h"
 
 namespace rx
 {
+class FramebufferAttachmentRenderTarget;
 
 class FramebufferAttachmentObjectImpl : public angle::Subject
 {
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
index b95b6d8..271f441 100644
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
@@ -235,7 +235,8 @@
         UNREACHABLE();
     }
 
-    setLevelInfo(target, level, 1, GetLevelInfo(internalFormat, texImageFormat.internalFormat));
+    setLevelInfo(context, target, level, 1,
+                 GetLevelInfo(internalFormat, texImageFormat.internalFormat));
 }
 
 void TextureGL::reserveTexImageToBeFilled(const gl::Context *context,
@@ -508,7 +509,7 @@
 
     LevelInfoGL levelInfo = GetLevelInfo(internalFormat, compressedTexImageFormat.internalFormat);
     ASSERT(!levelInfo.lumaWorkaround.enabled);
-    setLevelInfo(target, level, 1, levelInfo);
+    setLevelInfo(context, target, level, 1, levelInfo);
 
     return gl::NoError();
 }
@@ -660,7 +661,7 @@
             UNREACHABLE();
         }
 
-        setLevelInfo(target, level, 1, levelInfo);
+        setLevelInfo(context, target, level, 1, levelInfo);
     }
 
     return gl::NoError();
@@ -1007,7 +1008,8 @@
         UNREACHABLE();
     }
 
-    setLevelInfo(type, 0, levels, GetLevelInfo(internalFormat, texStorageFormat.internalFormat));
+    setLevelInfo(context, type, 0, levels,
+                 GetLevelInfo(internalFormat, texStorageFormat.internalFormat));
 
     return gl::NoError();
 }
@@ -1046,7 +1048,8 @@
         UNREACHABLE();
     }
 
-    setLevelInfo(type, 0, 1, GetLevelInfo(internalFormat, texStorageFormat.internalFormat));
+    setLevelInfo(context, type, 0, 1,
+                 GetLevelInfo(internalFormat, texStorageFormat.internalFormat));
 
     return gl::NoError();
 }
@@ -1071,7 +1074,8 @@
     const GLuint effectiveBaseLevel = mState.getEffectiveBaseLevel();
     const GLuint maxLevel           = mState.getMipmapMaxLevel();
 
-    setLevelInfo(getType(), effectiveBaseLevel, maxLevel - effectiveBaseLevel, getBaseLevelInfo());
+    setLevelInfo(context, getType(), effectiveBaseLevel, maxLevel - effectiveBaseLevel,
+                 getBaseLevelInfo());
 
     return gl::NoError();
 }
@@ -1085,7 +1089,7 @@
     // Make sure this texture is bound
     stateManager->bindTexture(getType(), mTextureID);
 
-    setLevelInfo(getType(), 0, 1, LevelInfoGL());
+    setLevelInfo(context, getType(), 0, 1, LevelInfoGL());
     return gl::NoError();
 }
 
@@ -1119,7 +1123,7 @@
     GLenum imageNativeInternalFormat = GL_NONE;
     ANGLE_TRY(imageGL->setTexture2D(context, type, this, &imageNativeInternalFormat));
 
-    setLevelInfo(type, 0, 1,
+    setLevelInfo(context, type, 0, 1,
                  GetLevelInfo(image->getFormat().info->internalFormat, imageNativeInternalFormat));
 
     return gl::NoError();
@@ -1247,6 +1251,11 @@
             case gl::Texture::DIRTY_BIT_LABEL:
                 break;
 
+            case gl::Texture::DIRTY_BIT_IMPLEMENTATION:
+                // This special dirty bit is used to signal the front-end that the implementation
+                // has local dirty bits. The real dirty bits are in mLocalDirty bits.
+                break;
+
             default:
                 UNREACHABLE();
         }
@@ -1271,6 +1280,9 @@
         mAppliedBaseLevel = baseLevel;
         mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_BASE_LEVEL);
 
+        // Signal to the GL layer that the Impl has dirty bits.
+        onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
+
         stateManager->bindTexture(getType(), mTextureID);
         functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_BASE_LEVEL, baseLevel);
     }
@@ -1287,6 +1299,9 @@
         mAppliedSampler.setMinFilter(filter);
         mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_MIN_FILTER);
 
+        // Signal to the GL layer that the Impl has dirty bits.
+        onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
+
         stateManager->bindTexture(getType(), mTextureID);
         functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_MIN_FILTER, filter);
     }
@@ -1301,6 +1316,9 @@
         mAppliedSampler.setMagFilter(filter);
         mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_MAG_FILTER);
 
+        // Signal to the GL layer that the Impl has dirty bits.
+        onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
+
         stateManager->bindTexture(getType(), mTextureID);
         functions->texParameteri(ToGLenum(getType()), GL_TEXTURE_MAG_FILTER, filter);
     }
@@ -1322,6 +1340,9 @@
         mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_SWIZZLE_BLUE);
         mLocalDirtyBits.set(gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA);
 
+        // Signal to the GL layer that the Impl has dirty bits.
+        onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
+
         stateManager->bindTexture(getType(), mTextureID);
         functions->texParameteriv(ToGLenum(getType()), GL_TEXTURE_SWIZZLE_RGBA, swizzle);
     }
@@ -1446,7 +1467,8 @@
     functions->texParameteri(ToGLenum(getType()), name, resultSwizzle);
 }
 
-void TextureGL::setLevelInfo(gl::TextureTarget target,
+void TextureGL::setLevelInfo(const gl::Context *context,
+                             gl::TextureTarget target,
                              size_t level,
                              size_t levelCount,
                              const LevelInfoGL &levelInfo)
@@ -1470,10 +1492,12 @@
     if (updateWorkarounds)
     {
         mLocalDirtyBits |= GetLevelWorkaroundDirtyBits();
+        onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
     }
 }
 
-void TextureGL::setLevelInfo(gl::TextureType type,
+void TextureGL::setLevelInfo(const gl::Context *context,
+                             gl::TextureType type,
                              size_t level,
                              size_t levelCount,
                              const LevelInfoGL &levelInfo)
@@ -1482,12 +1506,12 @@
     {
         for (gl::TextureTarget target : gl::AllCubeFaceTextureTargets())
         {
-            setLevelInfo(target, level, levelCount, levelInfo);
+            setLevelInfo(context, target, level, levelCount, levelInfo);
         }
     }
     else
     {
-        setLevelInfo(NonCubeTextureTypeToTarget(type), level, levelCount, levelInfo);
+        setLevelInfo(context, NonCubeTextureTypeToTarget(type), level, levelCount, levelInfo);
     }
 }
 
diff --git a/src/libANGLE/renderer/gl/TextureGL.h b/src/libANGLE/renderer/gl/TextureGL.h
index 52fc880..3260e9a 100644
--- a/src/libANGLE/renderer/gl/TextureGL.h
+++ b/src/libANGLE/renderer/gl/TextureGL.h
@@ -223,11 +223,13 @@
                                  GLenum value,
                                  GLenum *outValue);
 
-    void setLevelInfo(gl::TextureTarget target,
+    void setLevelInfo(const gl::Context *context,
+                      gl::TextureTarget target,
                       size_t level,
                       size_t levelCount,
                       const LevelInfoGL &levelInfo);
-    void setLevelInfo(gl::TextureType type,
+    void setLevelInfo(const gl::Context *context,
+                      gl::TextureType type,
                       size_t level,
                       size_t levelCount,
                       const LevelInfoGL &levelInfo);
diff --git a/src/tests/egl_tests/EGLBlobCacheTest.cpp b/src/tests/egl_tests/EGLBlobCacheTest.cpp
index 76a7002..987892f 100644
--- a/src/tests/egl_tests/EGLBlobCacheTest.cpp
+++ b/src/tests/egl_tests/EGLBlobCacheTest.cpp
@@ -12,8 +12,6 @@
 #include "test_utils/ANGLETest.h"
 #include "test_utils/gl_raii.h"
 
-#include "libANGLE/validationEGL.h"
-
 using namespace angle;
 
 constexpr char kEGLExtName[] = "EGL_ANDROID_blob_cache";